[PATCH v3 1/2] virtio-pci: add error_detected and slot_reset for AER

Xixin Liu <[email protected]>
Newsgroups org.kernel.vger.linux-kernel,dev.linux.lists.virtualization,org.kernel.vger.linux-block
Message-ID <[email protected]>
virtio-pci only registered reset_prepare/reset_done.  Without
.error_detected the recovery core votes NO_AER_DRIVER on the first
broadcast, and report_perm_failure_detected skips the driver callback
whenever overall status is not RECOVERED.  That skip covers
NO_AER_DRIVER even when subordinate reset succeeded.  Virtio therefore
misses the normal ERS path and pci_channel_io_perm_failure.

AER's aer_root_reset() uses pci_bus_error_reset(), which does a bus or
slot reset without pci_dev_save_and_disable().  The existing
.reset_prepare and .reset_done therefore do not run on that path.
Those hooks still serve FLR and pci_reset_function.

Register error_detected and slot_reset:
- frozen / unknown: virtio_device_reset_prepare() then NEED_RESET
- after a successful PCI reset: restore config, virtio_device_reset_done()
- perm_failure: virtio_break_device() and return DISCONNECT

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

diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c
index 164f480b18a6..c0ffeed00004 100644
--- a/drivers/virtio/virtio_pci_common.c
+++ b/drivers/virtio/virtio_pci_common.c
@@ -828,7 +828,60 @@ 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);
+
+	/*
+	 * Quiesce on frozen/unknown before AER bus reset.  That path does
+	 * not call .reset_prepare.  On permanent failure only break
+	 * virtqueues.
+	 */
+	switch (state) {
+	case pci_channel_io_normal:
+		return PCI_ERS_RESULT_CAN_RECOVER;
+	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;
+	case pci_channel_io_frozen:
+	default:
+		pci_info(pci_dev, "channel state %u, quiesce device\n", state);
+		if (virtio_device_reset_prepare(&vp_dev->vdev))
+			dev_warn(&pci_dev->dev, "reset prepare failed\n");
+		return PCI_ERS_RESULT_NEED_RESET;
+	}
+}
+
+static pci_ers_result_t virtio_pci_slot_reset(struct pci_dev *pci_dev)
+{
+	struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
+	int ret;
+
+	if (pci_enable_device(pci_dev)) {
+		dev_err(&pci_dev->dev,
+			"Cannot re-enable PCI device after reset\n");
+		return PCI_ERS_RESULT_DISCONNECT;
+	}
+
+	pci_set_master(pci_dev);
+	pci_restore_state(pci_dev);
+
+	ret = virtio_device_reset_done(&vp_dev->vdev);
+	if (ret && ret != -EOPNOTSUPP) {
+		dev_warn(&pci_dev->dev, "slot reset restore failed: %d\n",
+			 ret);
+		return PCI_ERS_RESULT_DISCONNECT;
+	}
+
+	return PCI_ERS_RESULT_RECOVERED;
+}
+
 static const struct pci_error_handlers virtio_pci_err_handler = {
+	.error_detected = virtio_pci_error_detected,
+	.slot_reset     = virtio_pci_slot_reset,
 	.reset_prepare  = virtio_pci_reset_prepare,
 	.reset_done     = virtio_pci_reset_done,
 };
-- 
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.