mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
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;

  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®