Kernel KVM virtualization development
 help / color / mirror / Atom feed
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>,
	<nathanc@nvidia.com>, <mochs@nvidia.com>
Subject: [RFC PATCH 06/19] vfio/pci: Serialize ioeventfd writes with recovery
Date: Tue, 1 Sep 2026 10:32:04 +0100	[thread overview]
Message-ID: <20260901093217.8539-7-skolothumtho@nvidia.com> (raw)
In-Reply-To: <20260901093217.8539-1-skolothumtho@nvidia.com>

Share one write helper between the ioeventfd fast path and the threaded
one. It takes memory_lock, checks the recovery state, then writes. The
fast path runs from the virqfd wakeup with a spinlock held, so it
trylocks and hands off to the thread if the lock is busy. The thread can
block.

The ioeventfd write path must not take recovery_lock at all. It is
reached through flush_work() from the virqfd cleanup workqueue, and
VFIO_DEVICE_SET_IRQS later calls vfio_virqfd_disable(), which does that
flush while holding recovery_lock for reading. If the write then blocked
on recovery_lock behind a queued AER writer, all three would be stuck.
The flush waits for the write, the write waits for the AER writer, and
the AER writer waits for the reader driving the flush.

So the recovery state is read lock-free, and the write goes through the
raw vfio_iowrite*() accessors rather than vfio_pci_core_iowrite*(). A
later patch makes those take recovery_lock, which is what this path has
to stay clear of.

memory_lock is still what drains a write already under way, and the
order is what makes it safe. The lock is taken before the flag is read,
so a write which saw the flag clear is already holding the read side,
and recovery waits for it when it takes memory_lock for writing. A write
to an I/O port BAR takes no memory_lock and is not drained. Those are
best effort. So is a write which arrives just after a reset has finished,
since VFIO_DEVICE_RESET releases memory_lock before it retakes
recovery_lock to unblock access.

The check is skipped for drivers which do not advertise support.
pci_recovery_device_open is only ever set when the recovery machinery is
live, so testing it unconditionally would drop every ioeventfd write for
every other vfio-pci-core driver.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com>
---
 drivers/vfio/pci/vfio_pci_rdwr.c | 91 +++++++++++++++++++++-----------
 1 file changed, 60 insertions(+), 31 deletions(-)

diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c
index 7f14dd46de17..20362e2f0166 100644
--- a/drivers/vfio/pci/vfio_pci_rdwr.c
+++ b/drivers/vfio/pci/vfio_pci_rdwr.c
@@ -349,56 +349,85 @@ ssize_t vfio_pci_vga_rw(struct vfio_pci_core_device *vdev, char __user *buf,
 }
 #endif
 
-static void vfio_pci_ioeventfd_do_write(struct vfio_pci_ioeventfd *ioeventfd,
-					bool test_mem)
+static int vfio_pci_ioeventfd_do_write(struct vfio_pci_ioeventfd *ioeventfd,
+				       bool trylock)
 {
+	struct vfio_pci_core_device *vdev = ioeventfd->vdev;
+
+	if (ioeventfd->test_mem) {
+		if (trylock) {
+			if (!down_read_trylock(&vdev->memory_lock))
+				return 1; /* Lock contended, use thread */
+		} else {
+			down_read(&vdev->memory_lock);
+		}
+	}
+
+	/*
+	 * Read the recovery state lock-free rather than under recovery_lock.
+	 * This path runs from the virqfd cleanup workqueue, which is flushed
+	 * from paths that take recovery_lock for reading, so blocking on it
+	 * here would deadlock behind a queued writer.
+	 *
+	 * For a memory BAR, a blocked device still waits for a write already
+	 * under way, through memory_lock. The lock is taken above before the
+	 * flag is read, so a write which saw the flag clear is already
+	 * holding the read side, and the blocker waits for it when it takes
+	 * memory_lock for writing. An I/O port BAR takes no memory_lock, so
+	 * a write which saw the flag clear can still land afterwards. Port
+	 * writes are best effort here.
+	 *
+	 * A write can also be dropped for a short while after a reset has
+	 * finished, since VFIO_DEVICE_RESET releases memory_lock before it
+	 * retakes recovery_lock to unblock access. Closing that would mean
+	 * taking recovery_lock inside memory_lock, which is the wrong way
+	 * round.
+	 *
+	 * pci_recovery_device_open records that the recovery machinery is
+	 * live, so it is only ever set for drivers which advertise support.
+	 * Testing it unconditionally would drop every write for every other
+	 * driver.
+	 *
+	 * The raw vfio_iowrite*() accessors below are used for the same
+	 * reason. This path must not take recovery_lock.
+	 */
+	if (vdev->pci_recovery_supported &&
+	    (!READ_ONCE(vdev->pci_recovery_device_open) ||
+	     READ_ONCE(vdev->pci_recovery_access_blocked)))
+		goto out_memory;
+
+	if (ioeventfd->test_mem && !__vfio_pci_memory_enabled(vdev))
+		goto out_memory;
+
 	switch (ioeventfd->count) {
 	case 1:
-		vfio_pci_core_iowrite8(ioeventfd->vdev, test_mem,
-				       ioeventfd->data, ioeventfd->addr);
+		vfio_iowrite8(ioeventfd->data, ioeventfd->addr);
 		break;
 	case 2:
-		vfio_pci_core_iowrite16(ioeventfd->vdev, test_mem,
-					ioeventfd->data, ioeventfd->addr);
+		vfio_iowrite16(ioeventfd->data, ioeventfd->addr);
 		break;
 	case 4:
-		vfio_pci_core_iowrite32(ioeventfd->vdev, test_mem,
-					ioeventfd->data, ioeventfd->addr);
+		vfio_iowrite32(ioeventfd->data, ioeventfd->addr);
 		break;
 	case 8:
-		vfio_pci_core_iowrite64(ioeventfd->vdev, test_mem,
-					ioeventfd->data, ioeventfd->addr);
+		vfio_iowrite64(ioeventfd->data, ioeventfd->addr);
 		break;
 	}
-}
-
-static int vfio_pci_ioeventfd_handler(void *opaque, void *unused)
-{
-	struct vfio_pci_ioeventfd *ioeventfd = opaque;
-	struct vfio_pci_core_device *vdev = ioeventfd->vdev;
-
-	if (ioeventfd->test_mem) {
-		if (!down_read_trylock(&vdev->memory_lock))
-			return 1; /* Lock contended, use thread */
-		if (!__vfio_pci_memory_enabled(vdev)) {
-			up_read(&vdev->memory_lock);
-			return 0;
-		}
-	}
-
-	vfio_pci_ioeventfd_do_write(ioeventfd, false);
 
+out_memory:
 	if (ioeventfd->test_mem)
 		up_read(&vdev->memory_lock);
-
 	return 0;
 }
 
-static void vfio_pci_ioeventfd_thread(void *opaque, void *unused)
+static int vfio_pci_ioeventfd_handler(void *opaque, void *unused)
 {
-	struct vfio_pci_ioeventfd *ioeventfd = opaque;
+	return vfio_pci_ioeventfd_do_write(opaque, true);
+}
 
-	vfio_pci_ioeventfd_do_write(ioeventfd, ioeventfd->test_mem);
+static void vfio_pci_ioeventfd_thread(void *opaque, void *unused)
+{
+	vfio_pci_ioeventfd_do_write(opaque, false);
 }
 
 int vfio_pci_ioeventfd(struct vfio_pci_core_device *vdev, loff_t offset,
-- 
2.43.0


  parent reply	other threads:[~2026-09-01  9:34 UTC|newest]

Thread overview: 50+ 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:45   ` sashiko-bot
2026-09-01  9:32 ` [RFC PATCH 02/19] vfio/pci: Serialize generic device lifetime with recovery Shameer Kolothum
2026-09-01  9:47   ` sashiko-bot
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:39   ` sashiko-bot
2026-09-01  9:32 ` [RFC PATCH 04/19] vfio/pci: Serialize function reset with recovery Shameer Kolothum
2026-09-01  9:45   ` sashiko-bot
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-01  9:46   ` sashiko-bot
2026-09-02  6:27   ` K V P, Satyanarayana
2026-09-03 11:08     ` Shameer Kolothum Thodi
2026-09-01  9:32 ` Shameer Kolothum [this message]
2026-09-01  9:43   ` [RFC PATCH 06/19] vfio/pci: Serialize ioeventfd writes " sashiko-bot
2026-09-01  9:32 ` [RFC PATCH 07/19] vfio/pci: Retry BAR faults after temporary recovery Shameer Kolothum
2026-09-01  9:47   ` sashiko-bot
2026-09-01  9:32 ` [RFC PATCH 08/19] vfio/pci: Serialize BAR and ROM access with recovery Shameer Kolothum
2026-09-01  9:48   ` sashiko-bot
2026-09-01  9:32 ` [RFC PATCH 09/19] vfio/pci: Serialize interrupt operations " Shameer Kolothum
2026-09-01  9:42   ` sashiko-bot
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:58   ` sashiko-bot
2026-09-01  9:32 ` [RFC PATCH 11/19] vfio/pci: Serialize runtime PM " Shameer Kolothum
2026-09-01  9:49   ` sashiko-bot
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:48   ` sashiko-bot
2026-09-01  9:32 ` [RFC PATCH 13/19] vfio/pci: Serialize DMA-BUF export " Shameer Kolothum
2026-09-01  9:43   ` sashiko-bot
2026-09-01  9:32 ` [RFC PATCH 14/19] vfio/pci: Add generic PCI error slot reset handling Shameer Kolothum
2026-09-01  9:53   ` sashiko-bot
2026-09-01  9:32 ` [RFC PATCH 15/19] vfio/pci: Add INTx helpers for PCI recovery Shameer Kolothum
2026-09-01  9:59   ` sashiko-bot
2026-09-01  9:32 ` [RFC PATCH 16/19] vfio/pci: Quiesce INTx during " Shameer Kolothum
2026-09-01  9:53   ` sashiko-bot
2026-09-01  9:32 ` [RFC PATCH 17/19] vfio/pci: Add generic PCI error resume handling Shameer Kolothum
2026-09-01  9:55   ` sashiko-bot
2026-09-01  9:32 ` [RFC PATCH 18/19] vfio/pci: Coordinate generic device access with host recovery Shameer Kolothum
2026-09-01  9:56   ` sashiko-bot
2026-09-01  9:32 ` [RFC PATCH 19/19] vfio/pci: Expose and enable host PCI error recovery Shameer Kolothum
2026-09-01  9:56   ` sashiko-bot
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=20260901093217.8539-7-skolothumtho@nvidia.com \
    --to=skolothumtho@nvidia.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=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