aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMing Lei <ming.lei@redhat.com>2018-11-14 03:25:51 -0500
committerJens Axboe <axboe@kernel.dk>2018-11-14 10:19:10 -0500
commit8dc765d438f1e42b3e8227b3b09fad7d73f4ec9a (patch)
treeab33205957486211bc8caa7a883d661606f6b759
parent4800bf7bc8c725e955fcbc6191cc872f43f506d3 (diff)
SCSI: fix queue cleanup race before queue initialization is done
c2856ae2f315d ("blk-mq: quiesce queue before freeing queue") has already fixed this race, however the implied synchronize_rcu() in blk_mq_quiesce_queue() can slow down LUN probe a lot, so caused performance regression. Then 1311326cf4755c7 ("blk-mq: avoid to synchronize rcu inside blk_cleanup_queue()") tried to quiesce queue for avoiding unnecessary synchronize_rcu() only when queue initialization is done, because it is usual to see lots of inexistent LUNs which need to be probed. However, turns out it isn't safe to quiesce queue only when queue initialization is done. Because when one SCSI command is completed, the user of sending command can be waken up immediately, then the scsi device may be removed, meantime the run queue in scsi_end_request() is still in-progress, so kernel panic can be caused. In Red Hat QE lab, there are several reports about this kind of kernel panic triggered during kernel booting. This patch tries to address the issue by grabing one queue usage counter during freeing one request and the following run queue. Fixes: 1311326cf4755c7 ("blk-mq: avoid to synchronize rcu inside blk_cleanup_queue()") Cc: Andrew Jones <drjones@redhat.com> Cc: Bart Van Assche <bart.vanassche@wdc.com> Cc: linux-scsi@vger.kernel.org Cc: Martin K. Petersen <martin.petersen@oracle.com> Cc: Christoph Hellwig <hch@lst.de> Cc: James E.J. Bottomley <jejb@linux.vnet.ibm.com> Cc: stable <stable@vger.kernel.org> Cc: jianchao.wang <jianchao.w.wang@oracle.com> Signed-off-by: Ming Lei <ming.lei@redhat.com> Signed-off-by: Jens Axboe <axboe@kernel.dk>
-rw-r--r--block/blk-core.c5
-rw-r--r--drivers/scsi/scsi_lib.c8
2 files changed, 10 insertions, 3 deletions
diff --git a/block/blk-core.c b/block/blk-core.c
index ce12515f9b9b..deb56932f8c4 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -798,9 +798,8 @@ void blk_cleanup_queue(struct request_queue *q)
798 * dispatch may still be in-progress since we dispatch requests 798 * dispatch may still be in-progress since we dispatch requests
799 * from more than one contexts. 799 * from more than one contexts.
800 * 800 *
801 * No need to quiesce queue if it isn't initialized yet since 801 * We rely on driver to deal with the race in case that queue
802 * blk_freeze_queue() should be enough for cases of passthrough 802 * initialization isn't done.
803 * request.
804 */ 803 */
805 if (q->mq_ops && blk_queue_init_done(q)) 804 if (q->mq_ops && blk_queue_init_done(q))
806 blk_mq_quiesce_queue(q); 805 blk_mq_quiesce_queue(q);
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index c7fccbb8f554..fa6e0c3b3aa6 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -697,6 +697,12 @@ static bool scsi_end_request(struct request *req, blk_status_t error,
697 */ 697 */
698 scsi_mq_uninit_cmd(cmd); 698 scsi_mq_uninit_cmd(cmd);
699 699
700 /*
701 * queue is still alive, so grab the ref for preventing it
702 * from being cleaned up during running queue.
703 */
704 percpu_ref_get(&q->q_usage_counter);
705
700 __blk_mq_end_request(req, error); 706 __blk_mq_end_request(req, error);
701 707
702 if (scsi_target(sdev)->single_lun || 708 if (scsi_target(sdev)->single_lun ||
@@ -704,6 +710,8 @@ static bool scsi_end_request(struct request *req, blk_status_t error,
704 kblockd_schedule_work(&sdev->requeue_work); 710 kblockd_schedule_work(&sdev->requeue_work);
705 else 711 else
706 blk_mq_run_hw_queues(q, true); 712 blk_mq_run_hw_queues(q, true);
713
714 percpu_ref_put(&q->q_usage_counter);
707 } else { 715 } else {
708 unsigned long flags; 716 unsigned long flags;
709 717