Skip to content

Commit

Permalink
[SCSI] Fix race between starved list and device removal
Browse files Browse the repository at this point in the history
scsi_run_queue() examines all SCSI devices that are present on
the starved list. Since scsi_run_queue() unlocks the SCSI host
lock a SCSI device can get removed after it has been removed
from the starved list and before its queue is run. Protect
against that race condition by holding a reference on the
queue while running it.

Reported-by: Chanho Min <chanho.min@lge.com>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
  • Loading branch information
James Bottomley authored and James Bottomley committed Jul 9, 2013
1 parent fec3c1b commit e2eb724
Showing 1 changed file with 21 additions and 5 deletions.
26 changes: 21 additions & 5 deletions drivers/scsi/scsi_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,8 @@ static void scsi_run_queue(struct request_queue *q)
list_splice_init(&shost->starved_list, &starved_list);

while (!list_empty(&starved_list)) {
struct request_queue *slq;

/*
* As long as shost is accepting commands and we have
* starved queues, call blk_run_queue. scsi_request_fn
Expand All @@ -456,11 +458,25 @@ static void scsi_run_queue(struct request_queue *q)
continue;
}

spin_unlock(shost->host_lock);
spin_lock(sdev->request_queue->queue_lock);
__blk_run_queue(sdev->request_queue);
spin_unlock(sdev->request_queue->queue_lock);
spin_lock(shost->host_lock);
/*
* Once we drop the host lock, a racing scsi_remove_device()
* call may remove the sdev from the starved list and destroy
* it and the queue. Mitigate by taking a reference to the
* queue and never touching the sdev again after we drop the
* host lock. Note: if __scsi_remove_device() invokes
* blk_cleanup_queue() before the queue is run from this
* function then blk_run_queue() will return immediately since
* blk_cleanup_queue() marks the queue with QUEUE_FLAG_DYING.
*/
slq = sdev->request_queue;
if (!blk_get_queue(slq))
continue;
spin_unlock_irqrestore(shost->host_lock, flags);

blk_run_queue(slq);
blk_put_queue(slq);

spin_lock_irqsave(shost->host_lock, flags);
}
/* put any unprocessed entries back */
list_splice(&starved_list, &shost->starved_list);
Expand Down

0 comments on commit e2eb724

Please sign in to comment.