[PATCH v3 2/2] virtio-blk: add shutdown and fail queue_rq after VQ teardown

Xixin Liu <[email protected]>
Newsgroups org.kernel.vger.linux-kernel,dev.linux.lists.virtualization,org.kernel.vger.linux-block
Message-ID <[email protected]>
Register virtio_driver.shutdown for reboot and poweroff via
virtio_dev_shutdown: freeze to reclaim in-flight requests, then
break virtqueues and mark the disk dead.

Also fail queue_rq early when vqs were removed during frozen
reset_prepare and vqs is NULL.

Assisted-by: DeepSeek:deepseek-v3
Signed-off-by: Xixin Liu <[email protected]>
---
 drivers/block/virtio_blk.c | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 32bf3ba07a9d..bbbbbbbbbbbb 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -435,6 +435,12 @@ static blk_status_t virtio_queue_rq(struct blk_mq_hw_ctx *hctx,
 	blk_status_t status;
 	int err;
 
+	/* VQs may be gone after frozen reset_prepare; avoid NULL deref. */
+	if (unlikely(!vblk->vqs || !vblk->vdev)) {
+		blk_mq_start_request(req);
+		return BLK_STS_IOERR;
+	}
+
 	status = virtblk_prep_rq(hctx, vblk, req, vbr);
 	if (unlikely(status))
 		return status;
@@ -1561,6 +1561,34 @@ static int virtblk_probe(struct virtio_device *vdev)
 	return err;
 }
 
+/* System shutdown: reclaim in-flight requests, then break and mark dead. */
+static void virtblk_shutdown(struct virtio_device *vdev)
+{
+	struct virtio_blk *vblk = vdev->priv;
+	struct request_queue *q;
+	unsigned int memflags;
+
+	if (!vblk || !vblk->disk)
+		return;
+
+	q = vblk->disk->queue;
+	/*
+	 * Reclaim request memory before break: freeze waits until
+	 * q_usage_counter is zero (PDU + chained sg freed in done path).
+	 * After virtio_break_device(), completions no longer run.
+	 */
+	memflags = blk_mq_freeze_queue(q);
+	blk_mq_quiesce_queue_nowait(q);
+
+	virtio_break_device(vdev);
+	flush_work(&vblk->config_work);
+
+	blk_mark_disk_dead(vblk->disk);
+
+	blk_mq_unquiesce_queue(q);
+	blk_mq_unfreeze_queue(q, memflags);
+}
+
 static void virtblk_remove(struct virtio_device *vdev)
 {
 	struct virtio_blk *vblk = vdev->priv;
@@ -1684,6 +1712,7 @@ static struct virtio_driver virtio_blk = {
 	.probe				= virtblk_probe,
 	.remove				= virtblk_remove,
 	.config_changed			= virtblk_config_changed,
+	.shutdown			= virtblk_shutdown,
 #ifdef CONFIG_PM_SLEEP
 	.freeze				= virtblk_freeze,
 	.restore			= virtblk_restore,
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.