aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@infradead.org>2011-10-17 13:56:45 -0400
committerNicholas Bellinger <nab@linux-iscsi.org>2011-10-24 03:21:39 +0000
commit0c2cfe5fe78e682d6235a1d32a363460b1c77528 (patch)
treefae9d86d8415ea163a49df6bf290aaef8901702e
parentb7b8bef7f8c1c9b3358127608e867db7cd928022 (diff)
downloadlinux-test-0c2cfe5fe78e682d6235a1d32a363460b1c77528.tar.gz
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>
-rw-r--r--drivers/target/target_core_transport.c13
1 files changed, 8 insertions, 5 deletions
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 0bfef095c98d29..4dc492d6ae1ba1 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)