aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/target
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-23 23:21:39 -0400
commit0c2cfe5fe78e682d6235a1d32a363460b1c77528 (patch)
treefae9d86d8415ea163a49df6bf290aaef8901702e /drivers/target
parentb7b8bef7f8c1c9b3358127608e867db7cd928022 (diff)
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.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 0bfef095c98..4dc492d6ae1 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)
3585{ 3585{
3586 struct se_task *task, *task_tmp; 3586 struct se_task *task, *task_tmp;
3587 unsigned long flags; 3587 unsigned long flags;
3588 LIST_HEAD(dispose_list);
3588 3589
3589 spin_lock_irqsave(&cmd->t_state_lock, flags); 3590 spin_lock_irqsave(&cmd->t_state_lock, flags);
3590 list_for_each_entry_safe(task, task_tmp, 3591 list_for_each_entry_safe(task, task_tmp,
3591 &cmd->t_task_list, t_list) { 3592 &cmd->t_task_list, t_list) {
3592 if (task->task_flags & TF_ACTIVE) 3593 if (!(task->task_flags & TF_ACTIVE))
3593 continue; 3594 list_move_tail(&task->t_list, &dispose_list);
3595 }
3596 spin_unlock_irqrestore(&cmd->t_state_lock, flags);
3597
3598 while (!list_empty(&dispose_list)) {
3599 task = list_first_entry(&dispose_list, struct se_task, t_list);
3594 3600
3595 kfree(task->task_sg_bidi); 3601 kfree(task->task_sg_bidi);
3596 kfree(task->task_sg); 3602 kfree(task->task_sg);
3597 3603
3598 list_del(&task->t_list); 3604 list_del(&task->t_list);
3599 3605
3600 spin_unlock_irqrestore(&cmd->t_state_lock, flags);
3601 cmd->se_dev->transport->free_task(task); 3606 cmd->se_dev->transport->free_task(task);
3602 spin_lock_irqsave(&cmd->t_state_lock, flags);
3603 } 3607 }
3604 spin_unlock_irqrestore(&cmd->t_state_lock, flags);
3605} 3608}
3606 3609
3607static inline void transport_free_sgl(struct scatterlist *sgl, int nents) 3610static inline void transport_free_sgl(struct scatterlist *sgl, int nents)