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 <[email protected]>
---
 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 164f480b18a6..e2dda946e70e 100644
--- a/drivers/virtio/virtio_pci_common.c
+++ b/drivers/virtio/virtio_pci_common.c
@@ -828,7 +828,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,
 };


Reply via email to