From d4f3e388763723fc0be76b0cab9149dc151aa890 Mon Sep 17 00:00:00 2001 From: Xixin Liu Date: Wed, 10 Jun 2026 14:20:00 +0800 Subject: [PATCH 1/2] virtio-pci: add error_detected for PCI AER recovery virtio-pci only registered reset_prepare/reset_done. The PCI error recovery core treats devices without error_detected as NO_AER_DRIVER and does not deliver pci_channel_io_perm_failure to the driver after a failed recovery. Virtio devices therefore miss the normal ERS quiesce/teardown sequence. Register error_detected: quiesce on frozen (reset_prepare) before bus reset; on perm_failure break virtqueues and return DISCONNECT. Block-layer cleanup for virtio-blk is handled in the follow-up patch. Signed-off-by: Xixin Liu --- drivers/virtio/virtio_pci_common.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c index da97b6a988deb..f0506bc011c7e 100644 --- a/drivers/virtio/virtio_pci_common.c +++ b/drivers/virtio/virtio_pci_common.c @@ -826,7 +826,37 @@ static void virtio_pci_reset_done(struct pci_dev *pci_dev) dev_warn(&pci_dev->dev, "Reset done failure: %d", ret); } +static pci_ers_result_t virtio_pci_error_detected(struct pci_dev *pci_dev, + pci_channel_state_t state) +{ + struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev); + + /* + * PCI ERS error_detected: quiesce on frozen before bus reset; on + * permanent failure ask the virtio driver to shut down (virtio-blk + * marks the disk dead in its .shutdown handler). + */ + switch (state) { + case pci_channel_io_normal: + return PCI_ERS_RESULT_CAN_RECOVER; + case pci_channel_io_frozen: + pci_info(pci_dev, "frozen error detected, quiesce device\n"); + if (virtio_device_reset_prepare(&vp_dev->vdev)) + dev_warn(&pci_dev->dev, "frozen: reset prepare failed\n"); + return PCI_ERS_RESULT_NEED_RESET; + case pci_channel_io_perm_failure: + dev_warn(&pci_dev->dev, + "permanent failure, disconnecting device\n"); + virtio_break_device(&vp_dev->vdev); + return PCI_ERS_RESULT_DISCONNECT; + default: + break; + } + return PCI_ERS_RESULT_NEED_RESET; +} + static const struct pci_error_handlers virtio_pci_err_handler = { + .error_detected = virtio_pci_error_detected, .reset_prepare = virtio_pci_reset_prepare, .reset_done = virtio_pci_reset_done, }; From ce7af27637e3b75eddf466fc6a8fa1d67f61a23b Mon Sep 17 00:00:00 2001 From: Xixin Liu Date: Fri, 12 Jun 2026 18:00:00 +0800 Subject: [PATCH 2/2] virtio-blk: mark disk dead on ERS permanent failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After ERS reports pci_channel_io_perm_failure, virtio-pci must ask the virtio driver to tear down the block device — not only mark virtqueues broken. Call the virtio driver shutdown hook from virtio-pci on perm_failure; virtio-blk implements shutdown with blk_mark_disk_dead(). Fail new requests early in virtio_queue_rq when the disk is dead or virtqueues were removed during frozen reset_prepare. Signed-off-by: Xixin Liu --- drivers/block/virtio_blk.c | 30 ++++++++++++++++++++++++++++++ drivers/virtio/virtio_pci_common.c | 10 +++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index b1c9a27fe00f3..cdd197c3c2e03 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; + /* Fail fast if ERS frozen tore down VQs or the disk was marked dead. */ + if (unlikely(!disk_live(vblk->disk) || !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; @@ -1559,6 +1565,29 @@ static int virtblk_probe(struct virtio_device *vdev) return err; } +/* Stop I/O and mark the gendisk dead (ERS perm_failure or system shutdown). */ +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; + + flush_work(&vblk->config_work); + virtio_break_device(vdev); + + q = vblk->disk->queue; + memflags = blk_mq_freeze_queue(q); + blk_mq_quiesce_queue_nowait(q); + + 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; @@ -1682,6 +1711,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, diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c index f0506bc011c7e..75458e82d3602 100644 --- a/drivers/virtio/virtio_pci_common.c +++ b/drivers/virtio/virtio_pci_common.c @@ -847,7 +847,15 @@ static pci_ers_result_t virtio_pci_error_detected(struct pci_dev *pci_dev, case pci_channel_io_perm_failure: dev_warn(&pci_dev->dev, "permanent failure, disconnecting device\n"); - virtio_break_device(&vp_dev->vdev); + { + struct virtio_driver *drv = + drv_to_virtio(vp_dev->vdev.dev.driver); + + if (drv && drv->shutdown) + drv->shutdown(&vp_dev->vdev); + else + virtio_break_device(&vp_dev->vdev); + } return PCI_ERS_RESULT_DISCONNECT; default: break;