From: "K V P, Satyanarayana" <satyanarayana.k.v.p@intel.com>
To: Shameer Kolothum <skolothumtho@nvidia.com>, <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>,
<sonangp@nvidia.com>, <nathanc@nvidia.com>, <mochs@nvidia.com>
Subject: Re: [RFC PATCH 05/19] vfio/pci: Serialize config access with recovery
Date: Wed, 2 Sep 2026 11:57:30 +0530 [thread overview]
Message-ID: <4dfc0aa3-a08a-495f-896d-7ea7c91a12ff@intel.com> (raw)
In-Reply-To: <20260901093217.8539-6-skolothumtho@nvidia.com>
On 01-Sep-26 3:02 PM, Shameer Kolothum wrote:
> Hold recovery_lock for reading across each config space operation, so
> recovery can shut out new ones and wait for whatever is already running.
> The user copies stay outside the lock, since a copy can fault.
>
> Take the lock in the dispatcher rather than around the individual
> hardware accessors. That means once recovery blocks access every config
> read fails with -EIO, even a read served entirely from vconfig which
> never touches the device. Userspace which wants to know what is going on
> reads the device feature instead. That one stays available during an
> event.
>
> The PCIe and AF capability writes no longer reset the device themselves,
> and the power management write no longer moves it to D0 itself. They
> record what was asked for and the dispatcher does it after dropping
> recovery_lock. Both take pci_bus_sem, which AER already holds when it
> calls into the driver, so doing either inside the lock would be the wrong
> order. A reset method reaches it directly, and a D0 transition reaches it
> through pci_set_full_power_state() calling
> pcie_aspm_pm_state_change(). The lower power states take neither, so
> those still run in the writefn. The writefn declaration says so.
>
> Both stay best effort, as the guest requested FLR always was. The result
> is not reported back through the config write. With recovery enabled they
> are dropped while a recovery or reset is already in flight, since that
> leaves the device in D0 and reset anyway. The reset helper tests the
> recovery state for itself. The power up does not, so the dispatcher
> tests it before that one.
>
> Assisted-by: Claude:claude-opus-5
> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com>
> ---
> drivers/vfio/pci/vfio_pci_config.c | 147 ++++++++++++++++++++---------
> 1 file changed, 102 insertions(+), 45 deletions(-)
>
> diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c
> index 9914f3ac69ae..3365100acf21 100644
> --- a/drivers/vfio/pci/vfio_pci_config.c
> +++ b/drivers/vfio/pci/vfio_pci_config.c
> @@ -99,6 +99,12 @@ static const u16 pci_ext_cap_length[PCI_EXT_CAP_ID_MAX + 1] = {
> [PCI_EXT_CAP_ID_DVSEC] = 0xFF,
> };
>
> +/* What a config write asked for which has to wait for the access guard. */
> +struct vfio_pci_config_deferred {
> + bool flr; /* a function-level reset */
> + bool power_up; /* a transition to D0 */
> +};
> +
> /*
> * Read/Write Permission Bits - one bit for each bit in capability
> * Any field can be read if it exists, but what is read depends on
> @@ -111,8 +117,17 @@ 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);
> + /*
> + * @deferred records work the write asked for which a writefn must not
> + * do itself. Both a reset method and a transition to D0 acquire
> + * pci_bus_sem, which AER already holds when it enters the driver, so
> + * doing either here would invert the lock order against recovery_lock.
> + * The dispatcher does them after dropping recovery_lock. Callers zero
> + * it, and a writefn only sets a field on a success return.
> + */
> 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,
> + struct vfio_pci_config_deferred *deferred);
> };
>
> #define NO_VIRT 0
> @@ -200,7 +215,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,
> + struct vfio_pci_config_deferred *deferred)
> {
> __le32 virt = 0, write = 0;
>
> @@ -272,7 +288,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,
> + struct vfio_pci_config_deferred *deferred)
> {
> int ret;
>
> @@ -299,7 +316,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,
> + struct vfio_pci_config_deferred *deferred)
> {
> memcpy(vdev->vconfig + pos, &val, count);
> return count;
> @@ -563,7 +581,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,
> + struct vfio_pci_config_deferred *deferred)
> {
> struct pci_dev *pdev = vdev->pdev;
> __le16 *virt_cmd;
> @@ -613,7 +632,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,
> + deferred);
> if (count < 0) {
> if (offset == PCI_COMMAND)
> up_write(&vdev->memory_lock);
> @@ -727,9 +747,11 @@ static void vfio_lock_and_set_power_state(struct vfio_pci_core_device *vdev,
>
> 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,
> + struct vfio_pci_config_deferred *deferred)
> {
> - count = vfio_default_config_write(vdev, pos, count, perm, offset, val);
> + count = vfio_default_config_write(vdev, pos, count, perm, offset, val,
> + deferred);
> if (count < 0)
> return count;
>
> @@ -738,8 +760,15 @@ static int vfio_pm_config_write(struct vfio_pci_core_device *vdev, int pos,
>
> switch (le32_to_cpu(val) & PCI_PM_CTRL_STATE_MASK) {
> case 0:
> - state = PCI_D0;
> - break;
> + /*
> + * Going to D0 reaches pci_set_full_power_state(),
> + * which takes pci_bus_sem through
> + * pcie_aspm_pm_state_change(). Leave it to the
> + * dispatcher. The lower states do not, so they run
> + * here.
> + */
> + deferred->power_up = true;
> + return count;
> case 1:
> state = PCI_D1;
> break;
> @@ -799,7 +828,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,
> + struct vfio_pci_config_deferred *deferred)
> {
> struct pci_dev *pdev = vdev->pdev;
> __le16 *paddr = (__le16 *)(vdev->vconfig + pos - offset + PCI_VPD_ADDR);
> @@ -812,7 +842,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,
> + deferred);
> if (count < 0 || offset > PCI_VPD_ADDR + 1 ||
> offset + count <= PCI_VPD_ADDR + 1)
> return count;
> @@ -881,21 +912,24 @@ 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,
> + struct vfio_pci_config_deferred *deferred)
> {
> __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,
> + deferred);
> if (count < 0)
> return count;
>
> /*
> * The FLR bit is virtualized, if set and the device supports PCIe
> - * FLR, issue a reset_function. Regardless, clear the bit, the spec
> - * requires it to be always read as zero. NB, reset_function might
> - * not use a PCIe FLR, we don't have that level of granularity.
> + * FLR, request a function reset once recovery_lock has been
> + * released. Regardless, clear the bit, the spec requires it to be
> + * always read as zero. NB, reset_function might not use a PCIe FLR,
> + * we don't have that level of granularity.
> */
> if (*ctrl & cpu_to_le16(PCI_EXP_DEVCTL_BCR_FLR)) {
> u32 cap;
> @@ -907,14 +941,8 @@ static int vfio_exp_config_write(struct vfio_pci_core_device *vdev, int pos,
> pos - offset + PCI_EXP_DEVCAP,
> &cap);
>
> - if (!ret && (cap & PCI_EXP_DEVCAP_FLR)) {
> - vfio_pci_zap_and_down_write_memory_lock(vdev);
> - vfio_pci_dma_buf_move(vdev, true);
> - pci_try_reset_function(vdev->pdev);
> - if (__vfio_pci_memory_enabled(vdev))
> - vfio_pci_dma_buf_move(vdev, false);
> - up_write(&vdev->memory_lock);
> - }
> + if (!ret && (cap & PCI_EXP_DEVCAP_FLR))
> + deferred->flr = true;
> }
>
> /*
> @@ -968,19 +996,22 @@ 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,
> + struct vfio_pci_config_deferred *deferred)
> {
> 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,
> + deferred);
> if (count < 0)
> return count;
>
> /*
> * The FLR bit is virtualized, if set and the device supports AF
> - * FLR, issue a reset_function. Regardless, clear the bit, the spec
> - * requires it to be always read as zero. NB, reset_function might
> - * not use an AF FLR, we don't have that level of granularity.
> + * FLR, request a function reset once recovery_lock has been
> + * released. Regardless, clear the bit, the spec requires it to be
> + * always read as zero. NB, reset_function might not use an AF FLR,
> + * we don't have that level of granularity.
> */
> if (*ctrl & PCI_AF_CTRL_FLR) {
> u8 cap;
> @@ -992,14 +1023,8 @@ static int vfio_af_config_write(struct vfio_pci_core_device *vdev, int pos,
> pos - offset + PCI_AF_CAP,
> &cap);
>
> - if (!ret && (cap & PCI_AF_CAP_FLR) && (cap & PCI_AF_CAP_TP)) {
> - vfio_pci_zap_and_down_write_memory_lock(vdev);
> - vfio_pci_dma_buf_move(vdev, true);
> - pci_try_reset_function(vdev->pdev);
> - if (__vfio_pci_memory_enabled(vdev))
> - vfio_pci_dma_buf_move(vdev, false);
> - up_write(&vdev->memory_lock);
> - }
> + if (!ret && (cap & PCI_AF_CAP_FLR) && (cap & PCI_AF_CAP_TP))
> + deferred->flr = true;
> }
>
> return count;
> @@ -1168,9 +1193,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,
> + struct vfio_pci_config_deferred *deferred)
> {
> - count = vfio_default_config_write(vdev, pos, count, perm, offset, val);
> + count = vfio_default_config_write(vdev, pos, count, perm, offset, val,
> + deferred);
> if (count < 0)
> return count;
>
> @@ -1889,6 +1916,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 access_ret;
> + struct vfio_pci_config_deferred deferred = {};
> u8 cap_id;
> ssize_t ret;
>
> @@ -1957,14 +1986,42 @@ 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);
> + access_ret = vfio_pci_core_access_begin(vdev);
> + if (access_ret)
> + return access_ret;
> + ret = perm->writefn(vdev, *ppos, count, perm, offset, val,
> + &deferred);
> + vfio_pci_core_access_end(vdev);
> + if (ret < 0)
> + return ret;
> + /*
> + * Both of these take pci_bus_sem, so run them with the access
> + * guard dropped. The reset re-checks the recovery state for
> + * itself. The power up does not, so check it here.
> + *
> + * Both are best effort, as the guest-requested FLR has always
> + * been. The result is not reported back through the config
> + * write. Without recovery enabled the only failure is -EAGAIN
> + * from device lock contention, exactly as before. With it they
> + * are dropped while a recovery or reset transaction is in
> + * flight, which leaves the device in D0 and reset anyway.
> + */
> + if (deferred.power_up &&
> + !(vdev->pci_recovery_supported &&
> + READ_ONCE(vdev->pci_recovery_access_blocked)))
> + vfio_lock_and_set_power_state(vdev, PCI_D0);
> + if (deferred.flr)
> + vfio_pci_try_reset_function(vdev, false);
> } else {
> - if (perm->readfn) {
> + access_ret = vfio_pci_core_access_begin(vdev);
> + if (access_ret)
> + return access_ret;
> + if (perm->readfn)
> ret = perm->readfn(vdev, *ppos, count,
> perm, offset, &val);
> - if (ret < 0)
> - return ret;
> - }
> + vfio_pci_core_access_end(vdev);
> + if (ret < 0)
> + return ret;
The else {} is all about perm->readfn. Can we move
vfio_pci_core_access_begin() and end() inside the if (perm->readfn) ?
We do not need to bring if (ret < 0) out of if(perm->readfn) in that case.
- Satya.
>
> if (copy_to_user(buf, &val, count))
> return -EFAULT;
next prev parent reply other threads:[~2026-09-02 6:27 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-01 9:31 [RFC PATCH 00/19] vfio/pci: Handle PCI error recovery and report state to userspace Shameer Kolothum
2026-09-01 9:31 ` [RFC PATCH 01/19] vfio/pci: Add PCI error recovery support state Shameer Kolothum
2026-09-01 9:32 ` [RFC PATCH 02/19] vfio/pci: Serialize generic device lifetime with recovery Shameer Kolothum
2026-09-01 13:14 ` K V P, Satyanarayana
2026-09-01 13:37 ` Shameer Kolothum Thodi
2026-09-01 9:32 ` [RFC PATCH 03/19] vfio/pci: Add PCI recovery access guards Shameer Kolothum
2026-09-01 9:32 ` [RFC PATCH 04/19] vfio/pci: Serialize function reset with recovery Shameer Kolothum
2026-09-02 6:06 ` K V P, Satyanarayana
2026-09-03 11:20 ` Shameer Kolothum Thodi
2026-09-01 9:32 ` [RFC PATCH 05/19] vfio/pci: Serialize config access " Shameer Kolothum
2026-09-02 6:27 ` K V P, Satyanarayana [this message]
2026-09-03 11:08 ` Shameer Kolothum Thodi
2026-09-01 9:32 ` [RFC PATCH 06/19] vfio/pci: Serialize ioeventfd writes " Shameer Kolothum
2026-09-01 9:32 ` [RFC PATCH 07/19] vfio/pci: Retry BAR faults after temporary recovery Shameer Kolothum
2026-09-01 9:32 ` [RFC PATCH 08/19] vfio/pci: Serialize BAR and ROM access with recovery Shameer Kolothum
2026-09-01 9:32 ` [RFC PATCH 09/19] vfio/pci: Serialize interrupt operations " Shameer Kolothum
2026-09-03 6:34 ` K V P, Satyanarayana
2026-09-03 10:39 ` Shameer Kolothum Thodi
2026-09-01 9:32 ` [RFC PATCH 10/19] vfio/pci: Serialize hot reset " Shameer Kolothum
2026-09-01 9:32 ` [RFC PATCH 11/19] vfio/pci: Serialize runtime PM " Shameer Kolothum
2026-09-03 6:43 ` K V P, Satyanarayana
2026-09-03 10:47 ` Shameer Kolothum Thodi
2026-09-01 9:32 ` [RFC PATCH 12/19] vfio/pci: Serialize physical device information queries " Shameer Kolothum
2026-09-01 9:32 ` [RFC PATCH 13/19] vfio/pci: Serialize DMA-BUF export " Shameer Kolothum
2026-09-01 9:32 ` [RFC PATCH 14/19] vfio/pci: Add generic PCI error slot reset handling Shameer Kolothum
2026-09-01 9:32 ` [RFC PATCH 15/19] vfio/pci: Add INTx helpers for PCI recovery Shameer Kolothum
2026-09-01 9:32 ` [RFC PATCH 16/19] vfio/pci: Quiesce INTx during " Shameer Kolothum
2026-09-01 9:32 ` [RFC PATCH 17/19] vfio/pci: Add generic PCI error resume handling Shameer Kolothum
2026-09-01 9:32 ` [RFC PATCH 18/19] vfio/pci: Coordinate generic device access with host recovery Shameer Kolothum
2026-09-01 9:32 ` [RFC PATCH 19/19] vfio/pci: Expose and enable host PCI error recovery Shameer Kolothum
2026-09-04 19:09 ` [RFC PATCH 00/19] vfio/pci: Handle PCI error recovery and report state to userspace Alex Williamson
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=4dfc0aa3-a08a-495f-896d-7ea7c91a12ff@intel.com \
--to=satyanarayana.k.v.p@intel.com \
--cc=alex@shazbot.org \
--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=skolothumtho@nvidia.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®