Skip to content

Commit 93542fb

Browse files
Ming Leiaxboe
authored andcommitted
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 <[email protected]> Fixes: e70feb8 ("blk-mq: support concurrent queue quiesce/unquiesce") Signed-off-by: Ming Lei <[email protected]> Reviewed-by: Martin K. Petersen <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jens Axboe <[email protected]>
1 parent d2b9f12 commit 93542fb

File tree

2 files changed

+29
-9
lines changed

2 files changed

+29
-9
lines changed

drivers/scsi/scsi_lib.c

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2638,6 +2638,32 @@ static int __scsi_internal_device_block_nowait(struct scsi_device *sdev)
26382638
return 0;
26392639
}
26402640

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+
26412667
/**
26422668
* scsi_internal_device_block_nowait - try to transition to the SDEV_BLOCK state
26432669
* @sdev: device to block
@@ -2662,7 +2688,7 @@ int scsi_internal_device_block_nowait(struct scsi_device *sdev)
26622688
* request queue.
26632689
*/
26642690
if (!ret)
2665-
blk_mq_quiesce_queue_nowait(sdev->request_queue);
2691+
scsi_stop_queue(sdev, true);
26662692
return ret;
26672693
}
26682694
EXPORT_SYMBOL_GPL(scsi_internal_device_block_nowait);
@@ -2689,19 +2715,12 @@ static int scsi_internal_device_block(struct scsi_device *sdev)
26892715
mutex_lock(&sdev->state_mutex);
26902716
err = __scsi_internal_device_block_nowait(sdev);
26912717
if (err == 0)
2692-
blk_mq_quiesce_queue(sdev->request_queue);
2718+
scsi_stop_queue(sdev, false);
26932719
mutex_unlock(&sdev->state_mutex);
26942720

26952721
return err;
26962722
}
26972723

2698-
void scsi_start_queue(struct scsi_device *sdev)
2699-
{
2700-
struct request_queue *q = sdev->request_queue;
2701-
2702-
blk_mq_unquiesce_queue(q);
2703-
}
2704-
27052724
/**
27062725
* scsi_internal_device_unblock_nowait - resume a device after a block request
27072726
* @sdev: device to resume

include/scsi/scsi_device.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ struct scsi_device {
207207
* creation time */
208208
unsigned ignore_media_change:1; /* Ignore MEDIA CHANGE on resume */
209209

210+
unsigned int queue_stopped; /* request queue is quiesced */
210211
bool offline_already; /* Device offline message logged */
211212

212213
atomic_t disk_events_disable_depth; /* disable depth for disk events */

0 commit comments

Comments
 (0)