aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi
diff options
context:
space:
mode:
authorJames Bottomley <JBottomley@Parallels.com>2013-07-02 09:05:26 -0400
committerJames Bottomley <JBottomley@Parallels.com>2013-07-09 07:14:08 -0400
commite2eb7244bc9e4fd130fc8a961224968e22ba48ee (patch)
treea1ba31c44721a9a106967e16b6065d4b98c0abeb /drivers/scsi
parentfec3c1b4575431e2020c5c6502d18b281925ca45 (diff)
[SCSI] Fix race between starved list and device removal
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>
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/scsi_lib.c26
1 files changed, 21 insertions, 5 deletions
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 86d522004a20..df8bd5ab3c0b 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -434,6 +434,8 @@ static void scsi_run_queue(struct request_queue *q)
434 list_splice_init(&shost->starved_list, &starved_list); 434 list_splice_init(&shost->starved_list, &starved_list);
435 435
436 while (!list_empty(&starved_list)) { 436 while (!list_empty(&starved_list)) {
437 struct request_queue *slq;
438
437 /* 439 /*
438 * As long as shost is accepting commands and we have 440 * As long as shost is accepting commands and we have
439 * starved queues, call blk_run_queue. scsi_request_fn 441 * starved queues, call blk_run_queue. scsi_request_fn
@@ -456,11 +458,25 @@ static void scsi_run_queue(struct request_queue *q)
456 continue; 458 continue;
457 } 459 }
458 460
459 spin_unlock(shost->host_lock); 461 /*
460 spin_lock(sdev->request_queue->queue_lock); 462 * Once we drop the host lock, a racing scsi_remove_device()
461 __blk_run_queue(sdev->request_queue); 463 * call may remove the sdev from the starved list and destroy
462 spin_unlock(sdev->request_queue->queue_lock); 464 * it and the queue. Mitigate by taking a reference to the
463 spin_lock(shost->host_lock); 465 * queue and never touching the sdev again after we drop the
466 * host lock. Note: if __scsi_remove_device() invokes
467 * blk_cleanup_queue() before the queue is run from this
468 * function then blk_run_queue() will return immediately since
469 * blk_cleanup_queue() marks the queue with QUEUE_FLAG_DYING.
470 */
471 slq = sdev->request_queue;
472 if (!blk_get_queue(slq))
473 continue;
474 spin_unlock_irqrestore(shost->host_lock, flags);
475
476 blk_run_queue(slq);
477 blk_put_queue(slq);
478
479 spin_lock_irqsave(shost->host_lock, flags);
464 } 480 }
465 /* put any unprocessed entries back */ 481 /* put any unprocessed entries back */
466 list_splice(&starved_list, &shost->starved_list); 482 list_splice(&starved_list, &shost->starved_list);