diff options
author | Christoph Hellwig <hch@infradead.org> | 2011-10-17 13:56:45 -0400 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2011-10-23 23:21:39 -0400 |
commit | 0c2cfe5fe78e682d6235a1d32a363460b1c77528 (patch) | |
tree | fae9d86d8415ea163a49df6bf290aaef8901702e /drivers/target | |
parent | b7b8bef7f8c1c9b3358127608e867db7cd928022 (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.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 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 | ||
3607 | static inline void transport_free_sgl(struct scatterlist *sgl, int nents) | 3610 | static inline void transport_free_sgl(struct scatterlist *sgl, int nents) |