From: Shameer Kolothum <skolothumtho@nvidia.com>
To: <kvm@vger.kernel.org>, <linux-pci@vger.kernel.org>,
<linux-kernel@vger.kernel.org>
Cc: <alex@shazbot.org>, <jgg@ziepe.ca>, <kevin.tian@intel.com>,
<kbusch@meta.com>, <michal.winiarski@intel.com>,
<satyanarayana.k.v.p@intel.com>, <sonangp@nvidia.com>,
<ankita@nvidia.com>, <nathanc@nvidia.com>, <mochs@nvidia.com>,
<skolothumtho@nvidia.com>
Subject: [RFC PATCH v3 16/20] vfio/pci: Restore device state from slot_reset()
Date: Thu, 12 Nov 2026 14:16:28 +0000 [thread overview]
Message-ID: <20261112141632.918954-17-skolothumtho@nvidia.com> (raw)
In-Reply-To: <20261112141632.918954-1-skolothumtho@nvidia.com>
A host reset loses device configuration and IRQ state. Add slot_reset()
to restore the configuration saved at open, discard the old PM snapshot
and tear down IRQ configuration for userspace to rebuild. Record RESET
before restoration so failures also report that a reset occurred. Leave
failed devices blocked.
Use the locked PCI power helper because native PCI recovery already
holds pci_bus_sem for reading. Taking it again could deadlock behind a
queued writer.
Preserve NoINTx and recovery masking when restoring saved configuration.
The state saved at open predates the NoINTx correction. Route PM
restoration through access ops while the device is open. Open and close
continue to restore state directly.
Keep the original error_detected-only callback table for Xen PV and EEH-
enabled builds. Xen treats a missing slot_reset callback as recovered
but treats NONE from a callback as failure. EEH also uses callback
presence to decide whether to keep a device. Disabling the new feature
alone would not preserve these decisions.
Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com>
---
drivers/vfio/pci/vfio_pci_priv.h | 2 +
include/linux/vfio_pci_core.h | 1 +
drivers/vfio/pci/vfio_pci.c | 17 +++++
drivers/vfio/pci/vfio_pci_core.c | 112 ++++++++++++++++++++++++++++++-
4 files changed, 131 insertions(+), 1 deletion(-)
diff --git a/drivers/vfio/pci/vfio_pci_priv.h b/drivers/vfio/pci/vfio_pci_priv.h
index e4f3e4f51180..bb22f0a15416 100644
--- a/drivers/vfio/pci/vfio_pci_priv.h
+++ b/drivers/vfio/pci/vfio_pci_priv.h
@@ -98,6 +98,8 @@ struct vfio_pci_access_ops {
vfio_pci_access_fn fn, void *data);
/* Caller holds memory_lock. */
int (*set_power_state)(struct vfio_pci_core_device *vdev, pci_power_t state);
+ /* Used by PM restoration while the device is open. */
+ void (*restore_config)(struct vfio_pci_core_device *vdev);
/* Caller holds memory_lock. */
void (*restore_dmabufs)(struct vfio_pci_core_device *vdev);
/* Caller holds the PCI device lock. The device need not be open. */
diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h
index fadbbaa58770..d36bca65d180 100644
--- a/include/linux/vfio_pci_core.h
+++ b/include/linux/vfio_pci_core.h
@@ -288,6 +288,7 @@ int vfio_pci_dma_buf_iommufd_map(struct dma_buf_attachment *attachment,
int vfio_pci_core_runtime_suspend(struct vfio_pci_core_device *vdev);
int vfio_pci_core_runtime_resume(struct vfio_pci_core_device *vdev);
pci_ers_result_t vfio_pci_core_aer_err_detected(struct vfio_pci_core_device *vdev);
+pci_ers_result_t vfio_pci_core_aer_slot_reset(struct vfio_pci_core_device *vdev);
/*
* Per-driver PM/AER trampolines. driver_data points to the driver's private
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index e7c012febe91..b4b3351cbd85 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -24,6 +24,7 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/uaccess.h>
+#include <xen/xen.h>
#include "vfio_pci_priv.h"
@@ -249,8 +250,20 @@ static pci_ers_result_t vfio_pci_aer_err_detected(struct pci_dev *pdev,
return vfio_pci_core_aer_err_detected(vdev);
}
+static pci_ers_result_t vfio_pci_aer_slot_reset(struct pci_dev *pdev)
+{
+ struct vfio_pci_core_device *vdev = pci_get_drvdata(pdev);
+
+ return vfio_pci_core_aer_slot_reset(vdev);
+}
+
+static const struct pci_error_handlers vfio_pci_legacy_err_handlers = {
+ .error_detected = vfio_pci_aer_err_detected,
+};
+
static const struct pci_error_handlers vfio_pci_err_handlers = {
.error_detected = vfio_pci_aer_err_detected,
+ .slot_reset = vfio_pci_aer_slot_reset,
};
static struct pci_driver vfio_pci_driver = {
@@ -309,6 +322,10 @@ static int __init vfio_pci_init(void)
{
int ret;
+ /* Xen PV and EEH check for callbacks. Keep the original table. */
+ if (xen_pv_domain() || IS_ENABLED(CONFIG_EEH))
+ vfio_pci_driver.err_handler = &vfio_pci_legacy_err_handlers;
+
/* Register and scan for devices */
ret = pci_register_driver(&vfio_pci_driver);
if (ret)
diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c
index 0ef51810254e..039fd864119b 100644
--- a/drivers/vfio/pci/vfio_pci_core.c
+++ b/drivers/vfio/pci/vfio_pci_core.c
@@ -312,6 +312,16 @@ static void vfio_pci_probe_power_state(struct vfio_pci_core_device *vdev)
vdev->needs_pm_restore = !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET);
}
+/* Keep NoINTx and recovery INTx masking when loading saved state. */
+static void vfio_pci_recovery_mask_saved_intx(struct vfio_pci_core_device *vdev)
+{
+ if (vdev->nointx ||
+ (vdev->pci_2_3 &&
+ READ_ONCE(vdev->irq_type) == VFIO_PCI_INTX_IRQ_INDEX))
+ vdev->pdev->saved_config_space[PCI_COMMAND / 4] |=
+ PCI_COMMAND_INTX_DISABLE;
+}
+
static int vfio_pci_sriov_configure_live(struct vfio_pci_core_device *vdev,
int nr_virtfn);
static int vfio_pci_prepare_hot_reset(struct vfio_pci_core_device *vdev);
@@ -329,6 +339,11 @@ static long vfio_pci_access_blocked(struct vfio_pci_core_device *vdev,
return -EIO;
}
+static void vfio_pci_restore_config(struct vfio_pci_core_device *vdev)
+{
+ pci_restore_state(vdev->pdev);
+}
+
static int vfio_pci_power_blocked(struct vfio_pci_core_device *vdev,
pci_power_t state)
{
@@ -339,6 +354,12 @@ static void vfio_pci_restore_dmabufs_blocked(struct vfio_pci_core_device *vdev)
{
}
+static void vfio_pci_restore_config_blocked(struct vfio_pci_core_device *vdev)
+{
+ vfio_pci_recovery_mask_saved_intx(vdev);
+ pci_restore_state(vdev->pdev);
+}
+
static int vfio_pci_sriov_configure_blocked(struct vfio_pci_core_device *vdev,
int nr_virtfn)
{
@@ -351,6 +372,7 @@ static int vfio_pci_sriov_configure_blocked(struct vfio_pci_core_device *vdev,
static const struct vfio_pci_access_ops vfio_pci_live_ops = {
.execute = vfio_pci_access_live,
.set_power_state = vfio_pci_try_set_power_state,
+ .restore_config = vfio_pci_restore_config,
.restore_dmabufs = vfio_pci_dma_buf_restore,
.sriov_configure = vfio_pci_sriov_configure_live,
.prepare_hot_reset = vfio_pci_prepare_hot_reset,
@@ -361,6 +383,7 @@ static const struct vfio_pci_access_ops vfio_pci_live_ops = {
static const struct vfio_pci_access_ops vfio_pci_blocked_ops = {
.execute = vfio_pci_access_blocked,
.set_power_state = vfio_pci_power_blocked,
+ .restore_config = vfio_pci_restore_config_blocked,
.restore_dmabufs = vfio_pci_restore_dmabufs_blocked,
.sriov_configure = vfio_pci_sriov_configure_blocked,
.prepare_hot_reset = vfio_pci_prepare_hot_reset_blocked,
@@ -476,7 +499,11 @@ static int __vfio_pci_set_power_state(struct vfio_pci_core_device *vdev,
vdev->pm_save = pci_store_saved_state(pdev);
} else if (needs_restore) {
pci_load_and_free_saved_state(pdev, &vdev->pm_save);
- pci_restore_state(pdev);
+ /* Open and close restore state without reopening access. */
+ if (!READ_ONCE(vdev->device_open))
+ pci_restore_state(pdev);
+ else
+ vfio_pci_access_ops(vdev)->restore_config(vdev);
}
}
@@ -2640,6 +2667,89 @@ pci_ers_result_t vfio_pci_core_aer_err_detected(struct vfio_pci_core_device *vde
}
EXPORT_SYMBOL_GPL(vfio_pci_core_aer_err_detected);
+/*
+ * The caller holds memory_lock. Discard the PM state saved before reset.
+ * Supported native PCI recovery paths already hold pci_bus_sem for reading.
+ * Taking it again could deadlock behind a queued writer.
+ */
+static int vfio_pci_recovery_restore_state(struct vfio_pci_core_device *vdev)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ int ret;
+
+ if (!vdev->pci_saved_state)
+ return -ENODATA;
+ ret = pci_load_saved_state(pdev, vdev->pci_saved_state);
+ if (ret)
+ return ret;
+
+ kfree(vdev->pm_save);
+ vdev->pm_save = NULL;
+ ret = pci_set_power_state_locked(pdev, PCI_D0);
+ if (ret)
+ return ret;
+
+ vfio_pci_recovery_mask_saved_intx(vdev);
+ pci_restore_state(pdev);
+ return 0;
+}
+
+pci_ers_result_t vfio_pci_core_aer_slot_reset(struct vfio_pci_core_device *vdev)
+{
+ int ret = 0;
+
+ mutex_lock(&vdev->access_lock);
+ if (!(vdev->pci_recovery_flags & VFIO_PCI_RECOVERY_IN_PROGRESS) ||
+ !vdev->device_open) {
+ mutex_unlock(&vdev->access_lock);
+ return PCI_ERS_RESULT_NONE;
+ }
+
+ /* The host reset completed even if restoring the device fails. */
+ WRITE_ONCE(vdev->pci_recovery_flags,
+ vdev->pci_recovery_flags | VFIO_PCI_RECOVERY_RESET);
+
+ /*
+ * Load the configuration saved at open before entering D0, which may
+ * restore BARs from that state. IRQ teardown needs the BARs restored so
+ * MSI-X shutdown can access its table after the host reset.
+ */
+ down_write(&vdev->memory_lock);
+ ret = vfio_pci_recovery_restore_state(vdev);
+ up_write(&vdev->memory_lock);
+ if (ret)
+ goto out_failed;
+
+ /*
+ * Close clears device_open under access_lock before tearing down IRQs,
+ * so this callback cannot race with that teardown.
+ */
+ mutex_lock(&vdev->igate);
+ if (vdev->irq_type < VFIO_PCI_NUM_IRQS)
+ ret = vfio_pci_set_irqs_ioctl(vdev,
+ VFIO_IRQ_SET_DATA_NONE |
+ VFIO_IRQ_SET_ACTION_TRIGGER,
+ vdev->irq_type, 0, 0, NULL);
+ mutex_unlock(&vdev->igate);
+ if (ret)
+ goto out_failed;
+
+ mutex_unlock(&vdev->access_lock);
+
+ return PCI_ERS_RESULT_RECOVERED;
+
+out_failed:
+ WRITE_ONCE(vdev->pci_recovery_flags,
+ (vdev->pci_recovery_flags | VFIO_PCI_RECOVERY_FAILED) &
+ ~VFIO_PCI_RECOVERY_IN_PROGRESS);
+ vdev->pci_recovery_command_valid = false;
+ mutex_unlock(&vdev->access_lock);
+
+ /* Allow recovery of other devices under the bridge to continue. */
+ return PCI_ERS_RESULT_NONE;
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_aer_slot_reset);
+
static int vfio_pci_sriov_configure_live(struct vfio_pci_core_device *vdev,
int nr_virtfn)
{
--
2.43.0
next prev parent reply other threads:[~2026-11-12 14:19 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-12 14:16 [RFC PATCH v3 00/20] vfio/pci: Handle PCI error recovery and report state to userspace Shameer Kolothum
2026-11-12 14:16 ` [RFC PATCH v3 01/20] vfio/pci: Add SRCU-protected access ops Shameer Kolothum
2026-11-12 14:16 ` [RFC PATCH v3 02/20] PCI/PM: Add a power-state helper that tries the bus lock Shameer Kolothum
2026-11-12 14:16 ` [RFC PATCH v3 03/20] vfio/pci: Avoid waiting for the bus lock when preparing D0 Shameer Kolothum
2026-11-12 14:16 ` [RFC PATCH v3 04/20] vfio/pci: Use access ops for config-space accesses Shameer Kolothum
2026-11-12 14:16 ` [RFC PATCH v3 05/20] vfio/pci: Buffer ROM reads before copying to userspace Shameer Kolothum
2026-11-12 14:16 ` [RFC PATCH v3 06/20] vfio/pci: Use access ops for BAR and ROM accesses Shameer Kolothum
2026-11-12 14:16 ` [RFC PATCH v3 07/20] vfio/pci: Use access ops when mapping BAR pages Shameer Kolothum
2026-11-12 14:16 ` [RFC PATCH v3 08/20] vfio/pci: Use access ops for hardware IRQ operations Shameer Kolothum
2026-11-12 14:16 ` [RFC PATCH v3 09/20] vfio/pci: Use access ops for function reset Shameer Kolothum
2026-11-12 14:16 ` [RFC PATCH v3 10/20] vfio/pci: Use access ops for runtime-PM requests Shameer Kolothum
2026-11-12 14:16 ` [RFC PATCH v3 11/20] vfio/pci: Use access ops for hardware information queries Shameer Kolothum
2026-11-12 14:16 ` [RFC PATCH v3 12/20] vfio/pci: Use access ops for DMA-BUF export and restoration Shameer Kolothum
2026-11-12 14:16 ` [RFC PATCH v3 13/20] vfio/pci: Track PCI recovery state Shameer Kolothum
2026-11-12 14:16 ` [RFC PATCH v3 14/20] vfio/pci: Add INTx recovery operations Shameer Kolothum
2026-11-12 14:16 ` [RFC PATCH v3 15/20] vfio/pci: Use access ops for SR-IOV configuration and shared reset Shameer Kolothum
2026-11-12 14:16 ` Shameer Kolothum [this message]
2026-11-12 14:16 ` [RFC PATCH v3 17/20] vfio/pci: Complete recovery in resume() Shameer Kolothum
2026-11-12 14:16 ` [RFC PATCH v3 18/20] vfio/pci: Block device access during host recovery Shameer Kolothum
2026-11-12 14:16 ` [RFC PATCH v3 19/20] vfio/pci: Add VFIO_DEVICE_FEATURE_PCI_ERROR_RECOVERY Shameer Kolothum
2026-11-12 14:16 ` [RFC PATCH v3 20/20] vfio/pci: Enable recovery for supported native PCIe endpoints Shameer Kolothum
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261112141632.918954-17-skolothumtho@nvidia.com \
--to=skolothumtho@nvidia.com \
--cc=alex@shazbot.org \
--cc=ankita@nvidia.com \
--cc=jgg@ziepe.ca \
--cc=kbusch@meta.com \
--cc=kevin.tian@intel.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=michal.winiarski@intel.com \
--cc=mochs@nvidia.com \
--cc=nathanc@nvidia.com \
--cc=satyanarayana.k.v.p@intel.com \
--cc=sonangp@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®