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 v2 02/16] vfio/pci: Gate config space access
Date: Tue, 29 Sep 2026 18:32:51 +0100 [thread overview]
Message-ID: <20260929173305.204856-3-skolothumtho@nvidia.com> (raw)
In-Reply-To: <20260929173305.204856-1-skolothumtho@nvidia.com>
Protect config callbacks with the access gate. Keep user copies outside
SRCU because userfaultfd can stall them indefinitely.
Run D0 transitions after releasing SRCU to avoid waiting for pci_bus_sem
while AER waits for readers to drain. Record blocked D0 requests for
replay in resume(). Return -EIO for blocked D1/D2/D3 requests, which are
not queued for replay. Preserve existing handling of hardware power-state
errors outside recovery.
Add a power_up output parameter to writefn so the PM callback can request
a D0 transition after the caller releases access_srcu.
Assisted-by: LLM
Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com>
---
Note: The memory_lock/pci_bus_sem dependency remains unresolved. See
the cover letter's "Locking and open questions" section.
---
include/linux/vfio_pci_core.h | 2 +
drivers/vfio/pci/vfio_pci_config.c | 104 +++++++++++++++++++++++------
2 files changed, 84 insertions(+), 22 deletions(-)
diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h
index de0993280344..1dc9630dc740 100644
--- a/include/linux/vfio_pci_core.h
+++ b/include/linux/vfio_pci_core.h
@@ -138,6 +138,8 @@ struct vfio_pci_core_device {
bool sriov_active;
struct pci_saved_state *pci_saved_state;
struct pci_saved_state *pm_save;
+ /* Deferred D0 request, protected by memory_lock. */
+ bool power_up_pending;
int ioeventfds_nr;
struct vfio_pci_eventfd __rcu *err_trigger;
struct vfio_pci_eventfd __rcu *req_trigger;
diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c
index 9914f3ac69ae..e9480b573381 100644
--- a/drivers/vfio/pci/vfio_pci_config.c
+++ b/drivers/vfio/pci/vfio_pci_config.c
@@ -111,8 +111,14 @@ struct perm_bits {
u8 *write; /* writeable bits */
int (*readfn)(struct vfio_pci_core_device *vdev, int pos, int count,
struct perm_bits *perm, int offset, __le32 *val);
+ /*
+ * Write callbacks run under access_srcu. Defer operations that take
+ * pci_bus_sem: AER may hold its read lock while waiting for SRCU,
+ * and a queued writer can block further read acquisitions.
+ */
int (*writefn)(struct vfio_pci_core_device *vdev, int pos, int count,
- struct perm_bits *perm, int offset, __le32 val);
+ struct perm_bits *perm, int offset, __le32 val,
+ bool *power_up);
};
#define NO_VIRT 0
@@ -200,7 +206,8 @@ static int vfio_default_config_read(struct vfio_pci_core_device *vdev, int pos,
static int vfio_default_config_write(struct vfio_pci_core_device *vdev, int pos,
int count, struct perm_bits *perm,
- int offset, __le32 val)
+ int offset, __le32 val,
+ bool *power_up)
{
__le32 virt = 0, write = 0;
@@ -272,7 +279,8 @@ static int vfio_direct_config_read(struct vfio_pci_core_device *vdev, int pos,
/* Raw access skips any kind of virtualization */
static int vfio_raw_config_write(struct vfio_pci_core_device *vdev, int pos,
int count, struct perm_bits *perm,
- int offset, __le32 val)
+ int offset, __le32 val,
+ bool *power_up)
{
int ret;
@@ -299,7 +307,8 @@ static int vfio_raw_config_read(struct vfio_pci_core_device *vdev, int pos,
/* Virt access uses only virtualization */
static int vfio_virt_config_write(struct vfio_pci_core_device *vdev, int pos,
int count, struct perm_bits *perm,
- int offset, __le32 val)
+ int offset, __le32 val,
+ bool *power_up)
{
memcpy(vdev->vconfig + pos, &val, count);
return count;
@@ -563,7 +572,8 @@ static bool vfio_need_bar_restore(struct vfio_pci_core_device *vdev)
static int vfio_basic_config_write(struct vfio_pci_core_device *vdev, int pos,
int count, struct perm_bits *perm,
- int offset, __le32 val)
+ int offset, __le32 val,
+ bool *power_up)
{
struct pci_dev *pdev = vdev->pdev;
__le16 *virt_cmd;
@@ -613,7 +623,8 @@ static int vfio_basic_config_write(struct vfio_pci_core_device *vdev, int pos,
vfio_bar_restore(vdev);
}
- count = vfio_default_config_write(vdev, pos, count, perm, offset, val);
+ count = vfio_default_config_write(vdev, pos, count, perm, offset, val,
+ power_up);
if (count < 0) {
if (offset == PCI_COMMAND)
up_write(&vdev->memory_lock);
@@ -709,8 +720,8 @@ static int __init init_pci_cap_basic_perm(struct perm_bits *perm)
* It takes all the required locks to protect the access of power related
* variables and then invokes vfio_pci_set_power_state().
*/
-static void vfio_lock_and_set_power_state(struct vfio_pci_core_device *vdev,
- pci_power_t state)
+static int vfio_lock_and_set_power_state(struct vfio_pci_core_device *vdev,
+ pci_power_t state)
{
if (state >= PCI_D3hot) {
vfio_pci_zap_and_down_write_memory_lock(vdev);
@@ -719,27 +730,51 @@ static void vfio_lock_and_set_power_state(struct vfio_pci_core_device *vdev,
down_write(&vdev->memory_lock);
}
+ /*
+ * Defer D0 until recovery completes if access is already blocked.
+ *
+ * This check does not prevent a block starting during the transition.
+ * A lock dependency remains: D0 takes pci_bus_sem under memory_lock,
+ * while AER takes memory_lock under pci_bus_sem. A queued bus writer
+ * can block the D0 reader and deadlock both paths.
+ */
+ if (vdev->pci_recovery_supported && READ_ONCE(vdev->access_blocked)) {
+ if (state == PCI_D0)
+ vdev->power_up_pending = true;
+ up_write(&vdev->memory_lock);
+ return state == PCI_D0 ? 0 : -EIO;
+ }
+
vfio_pci_set_power_state(vdev, state);
if (__vfio_pci_memory_enabled(vdev))
vfio_pci_dma_buf_move(vdev, false);
up_write(&vdev->memory_lock);
+
+ return 0;
}
static int vfio_pm_config_write(struct vfio_pci_core_device *vdev, int pos,
int count, struct perm_bits *perm,
- int offset, __le32 val)
+ int offset, __le32 val,
+ bool *power_up)
{
- count = vfio_default_config_write(vdev, pos, count, perm, offset, val);
+ count = vfio_default_config_write(vdev, pos, count, perm, offset, val,
+ power_up);
if (count < 0)
return count;
if (offset == PCI_PM_CTRL) {
pci_power_t state;
+ int ret;
switch (le32_to_cpu(val) & PCI_PM_CTRL_STATE_MASK) {
case 0:
- state = PCI_D0;
- break;
+ /*
+ * Request D0 after dropping access_srcu; the ASPM update can
+ * otherwise deadlock with AER waiting for SRCU readers.
+ */
+ *power_up = true;
+ return count;
case 1:
state = PCI_D1;
break;
@@ -751,7 +786,9 @@ static int vfio_pm_config_write(struct vfio_pci_core_device *vdev, int pos,
break;
}
- vfio_lock_and_set_power_state(vdev, state);
+ ret = vfio_lock_and_set_power_state(vdev, state);
+ if (ret)
+ return ret;
}
return count;
@@ -799,7 +836,8 @@ static int __init init_pci_cap_pm_perm(struct perm_bits *perm)
static int vfio_vpd_config_write(struct vfio_pci_core_device *vdev, int pos,
int count, struct perm_bits *perm,
- int offset, __le32 val)
+ int offset, __le32 val,
+ bool *power_up)
{
struct pci_dev *pdev = vdev->pdev;
__le16 *paddr = (__le16 *)(vdev->vconfig + pos - offset + PCI_VPD_ADDR);
@@ -812,7 +850,8 @@ static int vfio_vpd_config_write(struct vfio_pci_core_device *vdev, int pos,
* of PCI_VPD_ADDR, then the PCI_VPD_ADDR_F bit is written and we
* have work to do.
*/
- count = vfio_default_config_write(vdev, pos, count, perm, offset, val);
+ count = vfio_default_config_write(vdev, pos, count, perm, offset, val,
+ power_up);
if (count < 0 || offset > PCI_VPD_ADDR + 1 ||
offset + count <= PCI_VPD_ADDR + 1)
return count;
@@ -881,13 +920,15 @@ static int __init init_pci_cap_pcix_perm(struct perm_bits *perm)
static int vfio_exp_config_write(struct vfio_pci_core_device *vdev, int pos,
int count, struct perm_bits *perm,
- int offset, __le32 val)
+ int offset, __le32 val,
+ bool *power_up)
{
__le16 *ctrl = (__le16 *)(vdev->vconfig + pos -
offset + PCI_EXP_DEVCTL);
int readrq = le16_to_cpu(*ctrl) & PCI_EXP_DEVCTL_READRQ;
- count = vfio_default_config_write(vdev, pos, count, perm, offset, val);
+ count = vfio_default_config_write(vdev, pos, count, perm, offset, val,
+ power_up);
if (count < 0)
return count;
@@ -968,11 +1009,13 @@ static int __init init_pci_cap_exp_perm(struct perm_bits *perm)
static int vfio_af_config_write(struct vfio_pci_core_device *vdev, int pos,
int count, struct perm_bits *perm,
- int offset, __le32 val)
+ int offset, __le32 val,
+ bool *power_up)
{
u8 *ctrl = vdev->vconfig + pos - offset + PCI_AF_CTRL;
- count = vfio_default_config_write(vdev, pos, count, perm, offset, val);
+ count = vfio_default_config_write(vdev, pos, count, perm, offset, val,
+ power_up);
if (count < 0)
return count;
@@ -1168,9 +1211,11 @@ static int vfio_msi_config_read(struct vfio_pci_core_device *vdev, int pos,
static int vfio_msi_config_write(struct vfio_pci_core_device *vdev, int pos,
int count, struct perm_bits *perm,
- int offset, __le32 val)
+ int offset, __le32 val,
+ bool *power_up)
{
- count = vfio_default_config_write(vdev, pos, count, perm, offset, val);
+ count = vfio_default_config_write(vdev, pos, count, perm, offset, val,
+ power_up);
if (count < 0)
return count;
@@ -1889,6 +1934,8 @@ ssize_t vfio_pci_config_rw_single(struct vfio_pci_core_device *vdev,
struct perm_bits *perm;
__le32 val = 0;
int cap_start = 0, offset;
+ int idx;
+ bool power_up = false;
u8 cap_id;
ssize_t ret;
@@ -1957,11 +2004,24 @@ ssize_t vfio_pci_config_rw_single(struct vfio_pci_core_device *vdev,
if (copy_from_user(&val, buf, count))
return -EFAULT;
- ret = perm->writefn(vdev, *ppos, count, perm, offset, val);
+ idx = vfio_pci_core_access_begin(vdev);
+ if (idx < 0)
+ return idx;
+ ret = perm->writefn(vdev, *ppos, count, perm, offset, val,
+ &power_up);
+ vfio_pci_core_access_end(vdev, idx);
+ if (ret < 0)
+ return ret;
+ if (power_up)
+ vfio_lock_and_set_power_state(vdev, PCI_D0);
} else {
if (perm->readfn) {
+ idx = vfio_pci_core_access_begin(vdev);
+ if (idx < 0)
+ return idx;
ret = perm->readfn(vdev, *ppos, count,
perm, offset, &val);
+ vfio_pci_core_access_end(vdev, idx);
if (ret < 0)
return ret;
}
--
2.43.0
next prev parent reply other threads:[~2026-09-29 17:34 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-29 17:32 [RFC PATCH v2 00/16] vfio/pci: Handle PCI error recovery and report state to userspace Shameer Kolothum
2026-09-29 17:32 ` [RFC PATCH v2 01/16] vfio/pci: Add a device access gate Shameer Kolothum
2026-09-29 17:32 ` Shameer Kolothum [this message]
2026-09-29 17:32 ` [RFC PATCH v2 03/16] vfio/pci: Buffer ROM reads before copying to userspace Shameer Kolothum
2026-09-29 17:32 ` [RFC PATCH v2 04/16] vfio/pci: Gate BAR and ROM access Shameer Kolothum
2026-09-29 17:32 ` [RFC PATCH v2 05/16] vfio/pci: Fail BAR faults while access is blocked Shameer Kolothum
2026-09-29 17:32 ` [RFC PATCH v2 06/16] vfio/pci: Gate interrupt configuration Shameer Kolothum
2026-09-29 17:32 ` [RFC PATCH v2 07/16] vfio/pci: Gate function reset and runtime power management Shameer Kolothum
2026-09-29 17:32 ` [RFC PATCH v2 08/16] vfio/pci: Gate device information queries and DMA-BUF export Shameer Kolothum
2026-09-29 17:32 ` [RFC PATCH v2 09/16] vfio/pci: Add PCI error recovery state Shameer Kolothum
2026-09-29 17:32 ` [RFC PATCH v2 10/16] vfio/pci: Quiesce INTx while access is blocked Shameer Kolothum
2026-09-29 17:33 ` [RFC PATCH v2 11/16] vfio/pci: Add INTx recovery start and finish helpers Shameer Kolothum
2026-09-29 17:33 ` [RFC PATCH v2 12/16] vfio/pci: Restore device state from slot_reset() Shameer Kolothum
2026-09-29 17:33 ` [RFC PATCH v2 13/16] vfio/pci: Complete recovery in resume() Shameer Kolothum
2026-09-29 17:33 ` [RFC PATCH v2 14/16] vfio/pci: Block device access during host recovery Shameer Kolothum
2026-09-29 17:33 ` [RFC PATCH v2 15/16] vfio/pci: Add VFIO_DEVICE_FEATURE_PCI_ERROR_RECOVERY Shameer Kolothum
2026-09-29 17:33 ` [RFC PATCH v2 16/16] vfio/pci: Enable host PCI error recovery for vfio-pci 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=20260929173305.204856-3-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®