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 da97b6a988deb..75458e82d3602 100644 --- a/drivers/virtio/virtio_pci_common.c +++ b/drivers/virtio/virtio_pci_common.c @@ -826,7 +826,45 @@ 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"); + { + 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; + } + 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, };