Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

scsi: make sure that request queue queiesce and unquiesce balanced

For fixing queue quiesce race between driver and block layer(elevator
switch, update nr_requests, ...), we need to support concurrent quiesce
and unquiesce, which requires the two call balanced.

It isn't easy to audit that in all scsi drivers, especially the two may
be called from different contexts, so do it in scsi core with one
per-device atomic variable to balance quiesce and unquiesce.

Reported-by: Yi Zhang <yi.zhang@redhat.com>
Fixes: e70feb8b3e68 ("blk-mq: support concurrent queue quiesce/unquiesce")
Signed-off-by: Ming Lei <ming.lei@redhat.com>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Link: https://lore.kernel.org/r/20211109071144.181581-4-ming.lei@redhat.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by

Ming Lei and committed by
Jens Axboe
93542fbf d2b9f12b

+29 -9
+28 -9
drivers/scsi/scsi_lib.c
··· 2638 2638 return 0; 2639 2639 } 2640 2640 2641 + void scsi_start_queue(struct scsi_device *sdev) 2642 + { 2643 + if (cmpxchg(&sdev->queue_stopped, 1, 0)) 2644 + blk_mq_unquiesce_queue(sdev->request_queue); 2645 + } 2646 + 2647 + static void scsi_stop_queue(struct scsi_device *sdev, bool nowait) 2648 + { 2649 + /* 2650 + * The atomic variable of ->queue_stopped covers that 2651 + * blk_mq_quiesce_queue* is balanced with blk_mq_unquiesce_queue. 2652 + * 2653 + * However, we still need to wait until quiesce is done 2654 + * in case that queue has been stopped. 2655 + */ 2656 + if (!cmpxchg(&sdev->queue_stopped, 0, 1)) { 2657 + if (nowait) 2658 + blk_mq_quiesce_queue_nowait(sdev->request_queue); 2659 + else 2660 + blk_mq_quiesce_queue(sdev->request_queue); 2661 + } else { 2662 + if (!nowait) 2663 + blk_mq_wait_quiesce_done(sdev->request_queue); 2664 + } 2665 + } 2666 + 2641 2667 /** 2642 2668 * scsi_internal_device_block_nowait - try to transition to the SDEV_BLOCK state 2643 2669 * @sdev: device to block ··· 2688 2662 * request queue. 2689 2663 */ 2690 2664 if (!ret) 2691 - blk_mq_quiesce_queue_nowait(sdev->request_queue); 2665 + scsi_stop_queue(sdev, true); 2692 2666 return ret; 2693 2667 } 2694 2668 EXPORT_SYMBOL_GPL(scsi_internal_device_block_nowait); ··· 2715 2689 mutex_lock(&sdev->state_mutex); 2716 2690 err = __scsi_internal_device_block_nowait(sdev); 2717 2691 if (err == 0) 2718 - blk_mq_quiesce_queue(sdev->request_queue); 2692 + scsi_stop_queue(sdev, false); 2719 2693 mutex_unlock(&sdev->state_mutex); 2720 2694 2721 2695 return err; 2722 - } 2723 - 2724 - void scsi_start_queue(struct scsi_device *sdev) 2725 - { 2726 - struct request_queue *q = sdev->request_queue; 2727 - 2728 - blk_mq_unquiesce_queue(q); 2729 2696 } 2730 2697 2731 2698 /**
+1
include/scsi/scsi_device.h
··· 207 207 * creation time */ 208 208 unsigned ignore_media_change:1; /* Ignore MEDIA CHANGE on resume */ 209 209 210 + unsigned int queue_stopped; /* request queue is quiesced */ 210 211 bool offline_already; /* Device offline message logged */ 211 212 212 213 atomic_t disk_events_disable_depth; /* disable depth for disk events */