diff options
author | Christoph Hellwig <hch@infradead.org> | 2011-10-17 19:56:45 +0200 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2011-10-24 05:21:39 +0200 |
commit | 0c2cfe5fe78e682d6235a1d32a363460b1c77528 (patch) | |
tree | fae9d86d8415ea163a49df6bf290aaef8901702e /drivers/target | |
parent | target: use transport_cmd_check_stop_to_fabric consistently (diff) | |
download | linux-0c2cfe5fe78e682d6235a1d32a363460b1c77528.tar.xz linux-0c2cfe5fe78e682d6235a1d32a363460b1c77528.zip |
target: fix list walking in transport_free_dev_tasks
list_for_each_entry_safe only protects against deletions from the list,
but not against any concurrent modifications. Given that we drop
t_state_lock inside the loop it is not safe in transport_free_dev_tasks.
Instead of use a local dispose_list that we move all tasks that are
to be deleted to. This is safe because we never do list_emptry checks
on t_list to check if a command is on the list anywhere.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Diffstat (limited to 'drivers/target')
-rw-r--r-- | drivers/target/target_core_transport.c | 13 |
1 files changed, 8 insertions, 5 deletions
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 0bfef095c98d..4dc492d6ae1b 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -3585,23 +3585,26 @@ static void transport_free_dev_tasks(struct se_cmd *cmd) { struct se_task *task, *task_tmp; unsigned long flags; + LIST_HEAD(dispose_list); spin_lock_irqsave(&cmd->t_state_lock, flags); list_for_each_entry_safe(task, task_tmp, &cmd->t_task_list, t_list) { - if (task->task_flags & TF_ACTIVE) - continue; + if (!(task->task_flags & TF_ACTIVE)) + list_move_tail(&task->t_list, &dispose_list); + } + spin_unlock_irqrestore(&cmd->t_state_lock, flags); + + while (!list_empty(&dispose_list)) { + task = list_first_entry(&dispose_list, struct se_task, t_list); kfree(task->task_sg_bidi); kfree(task->task_sg); list_del(&task->t_list); - spin_unlock_irqrestore(&cmd->t_state_lock, flags); cmd->se_dev->transport->free_task(task); - spin_lock_irqsave(&cmd->t_state_lock, flags); } - spin_unlock_irqrestore(&cmd->t_state_lock, flags); } static inline void transport_free_sgl(struct scatterlist *sgl, int nents) |