Linux PCI subsystem development
 help / color / mirror / Atom feed
* [RFC PATCH 0/1] vfio: circular locking dependency in pci_dev_reset_iommu_prepare()
@ 2026-08-21 19:35 Vipin Sharma
  2026-08-21 19:35 ` [RFC PATCH 1/1] selftests: vfio: Add mmap fault and device reset test Vipin Sharma
  2026-08-25 19:06 ` [RFC PATCH 0/1] vfio: circular locking dependency in pci_dev_reset_iommu_prepare() David Matlack
  0 siblings, 2 replies; 4+ messages in thread
From: Vipin Sharma @ 2026-08-21 19:35 UTC (permalink / raw)
  To: Alex Williamson, Joerg Roedel, Bjorn Helgaas, Nicolin Chen,
	Jason Gunthorpe, Kevin Tian, Robin Murphy
  Cc: jrhilke, skhawaja, tatashin, Will Deacon, David Matlack, kvm,
	iommu, linux-pci, linux-kernel, Vipin Sharma

Hello,

I have encountered a reproducible circular locking dependency (lockdep)
warning when performing a device reset via VFIO (e.g. VFIO_DEVICE_RESET ioctl
or kexec live update) on a device whose BARs have been mmapped and faulted.

This issue was introduced in commit f5b16b802174 ("PCI: Suspend iommu function
prior to resetting a device"), which added calls to
pci_dev_reset_iommu_prepare() / pci_dev_reset_iommu_done() inside core PCI
reset routines (pcie_flr(), pci_pm_reset(), etc.).

Patch 1 adds a new selftest in tools/testing/selftests/vfio/ that triggers
this warning reliably on systems with ATS and reset support.

I used Intel DSA device 8086:0b25 with the test in Patch 1.

This issue was identified in the VFIO Live Update series
  https://lore.kernel.org/kvm/20260723181436.GB260824.vipinsh@google.com/

Since issue is independent of Live Update, I am sending this out
separately to not mix two things.

I have uploaded the patch to github also:
  https://github.com/shvipin/linux vfio/lockdep-mmap-fault-device-reset

================================================================================
Lock Hierarchy Analysis (Helped by AI)
================================================================================

1. In VFIO, handling VFIO_DEVICE_RESET (vfio_pci_ioctl_reset()) or bus/hot reset
   acquires down_write(&vdev->memory_lock) to zap active MMIO BAR mappings
   and prevent concurrent MMIO faults during reset.
   Inside memory_lock, it calls pci_try_reset_function() / pcie_flr().

2. Commit f5b16b802174 added pci_dev_reset_iommu_prepare() into pcie_flr().
   pci_dev_reset_iommu_prepare() acquires guard(mutex)(&group->mutex).
   This establishes:
       &vdev->memory_lock --> &group->mutex

3. Meanwhile, during kernel boot or device discovery (iommu_probe_device() /
   bus_iommu_probe()), iommu.c holds &group->mutex while calling
   iommu_setup_dma_ops(), which calls iova_domain_init_rcaches().
   iova_domain_init_rcaches() registers a multi-instance cpuhp handler via
   cpuhp_state_add_instance_nocalls(), acquiring cpus_read_lock().
   This establishes:
       &group->mutex --> cpu_hotplug_lock

4. cpu_hotplug_lock is an outer lock for various kernel subsystems that
   execute callbacks or sysfs/firmware/perf interactions, which eventually
   perform copy_to_user(), copy_from_user(), or filldir() (which can trigger
   a page fault in user memory under mmap_lock).
   This establishes:
       cpu_hotplug_lock --> (kernfs_rwsem / inode->i_rwsem / cpuctx_mutex)
                        --> &mm->mmap_lock

5. When userspace touches an mmapped VFIO BAR, the page fault handler
   (vfio_pci_mmap_huge_fault() / vfio_pci_mmap_page_fault()) executes under
   &mm->mmap_lock and acquires down_read(&vdev->memory_lock).
   This establishes:
       &mm->mmap_lock --> &vdev->memory_lock

Combining (1) through (5) yields the circular dependency cycle:

    &group->mutex
      --> cpu_hotplug_lock
        --> [kernfs_rwsem | i_mutex_dir_key | cpuctx_mutex]
          --> &mm->mmap_lock
            --> &vdev->memory_lock
              --> &group->mutex  <== DEADLOCK DETECTED


================================================================================
Reproductions / Lockdep Traces
================================================================================

I observed this lockdep warning closing via multiple distinct paths:

--- Trace 1: via sysfs / kernfs readdir ---

[  438.457974] WARNING: possible circular locking dependency detected
[  438.477952] vfio_cdev_lockd/17678 is trying to acquire lock:
[  438.484320] (&group->mutex){+.+.}-{4:4}, at: pci_dev_reset_iommu_prepare+0x6e/0x200
[  438.494169] but task is already holding lock:
[  438.501401] (&vdev->memory_lock){++++}-{4:4}, at: vfio_pci_core_ioctl+0x460/0xb20
[  438.511064] which lock already depends on the new lock.
...
[  438.919866] Chain exists of:
[  438.919866]   &group->mutex --> &mm->mmap_lock --> &vdev->memory_lock
...
[  438.999115]  __mutex_lock+0x8c/0xd80
[  438.999122]  pci_dev_reset_iommu_prepare+0x6e/0x200
[  438.999123]  pcie_flr+0x32/0xc0
[  438.999125]  __pci_reset_function_locked+0x84/0x120
[  438.999126]  pci_try_reset_function+0x51/0xe0
[  438.999128]  vfio_pci_core_ioctl+0x72b/0xb20

--- Trace 2: via VFS lookup / microcode loading during kexec ---

[  584.552264] WARNING: possible circular locking dependency detected
[  584.572247] kexec/14239 is trying to acquire lock:
[  584.577754] (&group->mutex){+.+.}-{4:4}, at: pci_dev_reset_iommu_prepare+0x6e/0x1a0
[  584.587602] but task is already holding lock:
[  584.594838] (&vdev->memory_lock){++++}-{4:4}, at: vfio_pci_liveupdate_freeze+0x51/0x100
...
[  584.751130] -> #2 (&type->i_mutex_dir_key#4):
[  584.760319]        lookup_slow+0x26/0x50
[  584.786970]        _request_firmware+0x4ab/0x8c0
[  584.797979]        request_microcode_fw+0xf2/0x510
[  584.834601] -> #1 (cpu_hotplug_lock):
[  584.842183]        cpus_read_lock+0x3b/0xd0
[  584.858957]        iommu_setup_dma_ops+0x175/0x540

--- Trace 3: via perf read / watchdog cpuhp callback ---

[  233.859740] WARNING: possible circular locking dependency detected
[  233.879725] vfio_cdev_lockd/14999 is trying to acquire lock:
[  233.886094] (&group->mutex){+.+.}-{4:4}, at: pci_dev_reset_iommu_prepare+0x6e/0x200
[  233.895948] but task is already holding lock:
[  233.903183] (&vdev->memory_lock){++++}-{4:4}, at: vfio_pci_core_ioctl+0x460/0xb20
...
[  234.030037] -> #3 (&cpuctx_mutex):
[  234.040960]        perf_event_enable+0x18/0xa0
[  234.046126]        lockup_detector_online_cpu+0x22/0x30
[  234.057658]        cpuhp_thread_fun+0x164/0x1e0
[  234.115402] -> #1 (cpu_hotplug_lock):
[  234.122985]        cpus_read_lock+0x3b/0xd0
[  234.139757]        iommu_setup_dma_ops+0x18a/0x560


================================================================================
Potential Solutions Suggested by AI
================================================================================

1. Decouple iommu_setup_dma_ops() from group->mutex in drivers/iommu/iommu.c:
   iommu_setup_dma_ops() only requires struct device * and the domain pointer
   (group->default_domain); it does not mutate any fields in struct iommu_group.
   Moving the iommu_setup_dma_ops() calls after mutex_unlock(&group->mutex) in
   iommu_probe_device(), bus_iommu_probe(), and iommu_group_store_type() breaks
   the initial &group->mutex -> cpu_hotplug_lock dependency.

2. Avoid holding down_write(&vdev->memory_lock) across pci_try_reset_function()
   in VFIO:
   vfio-pci could zap active BAR mappings under memory_lock and set a state
   flag / disable memory decoding, drop memory_lock before calling
   pci_try_reset_function(), and then re-acquire memory_lock to re-enable
   memory. While resetting, any concurrent user fault will see the memory
   disabled condition and return VM_FAULT_SIGBUS safely.

3. Refine synchronization in pci_dev_reset_iommu_prepare():
   Evaluate if attaching to the blocking domain and pausing ATS during device
   reset can be protected using more fine-grained locking or atomic state
   flags without holding the coarse &group->mutex.

Feedback and suggestions on the preferred direction to resolve this would be
greatly appreciated.

Thanks,
Vipin Sharma

Vipin Sharma (1):
  selftests: vfio: Add mmap fault and device reset test

 tools/testing/selftests/vfio/Makefile         |  1 +
 .../selftests/vfio/vfio_pci_mmap_reset_test.c | 60 +++++++++++++++++++
 2 files changed, 61 insertions(+)
 create mode 100644 tools/testing/selftests/vfio/vfio_pci_mmap_reset_test.c

-- 
2.55.0.766.g2966f0265a-goog

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [RFC PATCH 1/1] selftests: vfio: Add mmap fault and device reset test
  2026-08-21 19:35 [RFC PATCH 0/1] vfio: circular locking dependency in pci_dev_reset_iommu_prepare() Vipin Sharma
@ 2026-08-21 19:35 ` Vipin Sharma
  2026-08-21 19:46   ` sashiko-bot
  2026-08-25 19:06 ` [RFC PATCH 0/1] vfio: circular locking dependency in pci_dev_reset_iommu_prepare() David Matlack
  1 sibling, 1 reply; 4+ messages in thread
From: Vipin Sharma @ 2026-08-21 19:35 UTC (permalink / raw)
  To: Alex Williamson, Joerg Roedel, Bjorn Helgaas, Nicolin Chen,
	Jason Gunthorpe, Kevin Tian, Robin Murphy
  Cc: jrhilke, skhawaja, tatashin, Will Deacon, David Matlack, kvm,
	iommu, linux-pci, linux-kernel, Vipin Sharma

Add a selftest to verify VFIO PCI device reset on an mmapped and faulted
device. The test maps all available BARs on the device, faults them,
and triggers a device reset via the VFIO_DEVICE_RESET ioctl.

This exercise uncovers a circular locking dependency introduced in
commit f5b16b802174 ("PCI: Suspend iommu function prior to resetting a
device"), where pci_dev_reset_iommu_prepare() acquires group->mutex
under vfio's vdev->memory_lock.

Assisted-by: Jetski:gemini-3.1-pro
Signed-off-by: Vipin Sharma <vipinsh@google.com>
---
 tools/testing/selftests/vfio/Makefile         |  1 +
 .../selftests/vfio/vfio_pci_mmap_reset_test.c | 60 +++++++++++++++++++
 2 files changed, 61 insertions(+)
 create mode 100644 tools/testing/selftests/vfio/vfio_pci_mmap_reset_test.c

diff --git a/tools/testing/selftests/vfio/Makefile b/tools/testing/selftests/vfio/Makefile
index 2c32c48db509..17b3a2fe215c 100644
--- a/tools/testing/selftests/vfio/Makefile
+++ b/tools/testing/selftests/vfio/Makefile
@@ -13,6 +13,7 @@ TEST_GEN_PROGS += vfio_pci_device_test
 TEST_GEN_PROGS += vfio_pci_device_init_perf_test
 TEST_GEN_PROGS += vfio_pci_driver_test
 TEST_GEN_PROGS += vfio_pci_sriov_uapi_test
+TEST_GEN_PROGS += vfio_pci_mmap_reset_test
 
 TEST_FILES += scripts/cleanup.sh
 TEST_FILES += scripts/lib.sh
diff --git a/tools/testing/selftests/vfio/vfio_pci_mmap_reset_test.c b/tools/testing/selftests/vfio/vfio_pci_mmap_reset_test.c
new file mode 100644
index 000000000000..b004867905de
--- /dev/null
+++ b/tools/testing/selftests/vfio/vfio_pci_mmap_reset_test.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/pci_regs.h>
+#include <linux/vfio.h>
+
+#include <libvfio.h>
+
+#include "kselftest_harness.h"
+
+static const char *device_bdf;
+
+FIXTURE(vfio_pci_mmap_reset_test) {
+	struct iommu *iommu;
+	struct vfio_pci_device *device;
+};
+
+FIXTURE_SETUP(vfio_pci_mmap_reset_test)
+{
+	self->iommu = iommu_init(MODE_IOMMUFD);
+	self->device = vfio_pci_device_init(device_bdf, self->iommu);
+}
+
+FIXTURE_TEARDOWN(vfio_pci_mmap_reset_test)
+{
+	vfio_pci_device_cleanup(self->device);
+	iommu_cleanup(self->iommu);
+}
+
+TEST_F(vfio_pci_mmap_reset_test, mmap_fault_and_reset)
+{
+	volatile char dummy;
+	bool has_mmap = false;
+	int i;
+
+	if (!(self->device->info.flags & VFIO_DEVICE_FLAGS_RESET))
+		SKIP(return, "Device does not support reset\n");
+
+	for (i = 0; i < PCI_STD_NUM_BARS; i++) {
+		struct vfio_pci_bar *bar = &self->device->bars[i];
+
+		if (!bar->vaddr)
+			continue;
+
+		/* Touch BAR to trigger page fault under mmap_lock */
+		dummy = *(volatile char *)bar->vaddr;
+		(void)dummy;
+		has_mmap = true;
+	}
+
+	if (!has_mmap)
+		SKIP(return, "No mmapable BAR found on device\n");
+
+	/* Trigger device reset under memory_lock */
+	vfio_pci_device_reset(self->device);
+}
+
+int main(int argc, char *argv[])
+{
+	device_bdf = vfio_selftests_get_bdf(&argc, argv);
+	return test_harness_run(argc, argv);
+}
-- 
2.55.0.766.g2966f0265a-goog


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [RFC PATCH 1/1] selftests: vfio: Add mmap fault and device reset test
  2026-08-21 19:35 ` [RFC PATCH 1/1] selftests: vfio: Add mmap fault and device reset test Vipin Sharma
@ 2026-08-21 19:46   ` sashiko-bot
  0 siblings, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-08-21 19:46 UTC (permalink / raw)
  To: Vipin Sharma; +Cc: linux-pci, kvm

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] A circular locking dependency exists between `mmap_lock`, `vdev->memory_lock`, and `group->mutex`, leading to a potential deadlock.
--

--- Patch [1]: [RFC PATCH 1/1] selftests: vfio: Add mmap fault and device reset test ---
commit 3fb77a6912d6cc2309856fef1f82986ea816df96
Author: Vipin Sharma <vipinsh@google.com>
Subject: selftests: vfio: Add mmap fault and device reset test

This commit adds a selftest to verify VFIO PCI device reset on an mmapped
and faulted device. The test maps all available BARs, triggers a page
fault on them, and then initiates a device reset via the VFIO_DEVICE_RESET
ioctl to expose a circular locking dependency.

> diff --git a/tools/testing/selftests/vfio/vfio_pci_mmap_reset_test.c b/tools/testing/selftests/vfio/vfio_pci_mmap_reset_test.c
> new file mode 100644
> --- /dev/null
> +++ b/tools/testing/selftests/vfio/vfio_pci_mmap_reset_test.c
[ ... ]
> +TEST_F(vfio_pci_mmap_reset_test, mmap_fault_and_reset)
> +{
[ ... ]
> +	if (!has_mmap)
> +		SKIP(return, "No mmapable BAR found on device\n");
> +
> +	/* Trigger device reset under memory_lock */
> +	vfio_pci_device_reset(self->device);

[Severity: High]
This is a pre-existing issue, but does this test sequence reliably trigger a
host kernel deadlock by exposing a circular locking dependency between
mmap_lock, vdev->memory_lock, and group->mutex?

The lockdep cycle appears to start when a page fault holds mmap_lock and
acquires vdev->memory_lock:

drivers/vfio/pci/vfio_pci_core.c:vfio_pci_mmap_huge_fault()
    scoped_guard(rwsem_read, &vdev->memory_lock)

Separately, a device reset acquires vdev->memory_lock before initiating the
reset:

drivers/vfio/pci/vfio_pci_core.c:vfio_pci_ioctl_reset()
    vfio_pci_zap_and_down_write_memory_lock(vdev);

While held, it calls into the PCI reset functions which eventually prepare
the IOMMU and acquire group->mutex:

drivers/iommu/iommu.c:pci_dev_reset_iommu_prepare()
    guard(mutex)(&group->mutex);

The cycle completes when another kernel path holding group->mutex waits on
mmap_lock or memory_lock. Can a malicious guest VM or local user with VFIO
access trigger concurrent page faults and device resets to exploit this
cycle?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260821193502.92431-2-vipinsh@google.com?part=1

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [RFC PATCH 0/1] vfio: circular locking dependency in pci_dev_reset_iommu_prepare()
  2026-08-21 19:35 [RFC PATCH 0/1] vfio: circular locking dependency in pci_dev_reset_iommu_prepare() Vipin Sharma
  2026-08-21 19:35 ` [RFC PATCH 1/1] selftests: vfio: Add mmap fault and device reset test Vipin Sharma
@ 2026-08-25 19:06 ` David Matlack
  1 sibling, 0 replies; 4+ messages in thread
From: David Matlack @ 2026-08-25 19:06 UTC (permalink / raw)
  To: Vipin Sharma
  Cc: Alex Williamson, Joerg Roedel, Bjorn Helgaas, Nicolin Chen,
	Jason Gunthorpe, Kevin Tian, Robin Murphy, jrhilke, skhawaja,
	tatashin, Will Deacon, kvm, iommu, linux-pci, linux-kernel

On 2026-08-21 12:35 PM, Vipin Sharma wrote:

> ================================================================================
> Potential Solutions Suggested by AI
> ================================================================================
> 
> 1. Decouple iommu_setup_dma_ops() from group->mutex in drivers/iommu/iommu.c:
>    iommu_setup_dma_ops() only requires struct device * and the domain pointer
>    (group->default_domain); it does not mutate any fields in struct iommu_group.
>    Moving the iommu_setup_dma_ops() calls after mutex_unlock(&group->mutex) in
>    iommu_probe_device(), bus_iommu_probe(), and iommu_group_store_type() breaks
>    the initial &group->mutex -> cpu_hotplug_lock dependency.

Are there any other code paths that rely on group->mutex -->
mm->mmap_lock ordering? If so fixing this one case wouldn't help.

> 2. Avoid holding down_write(&vdev->memory_lock) across pci_try_reset_function()
>    in VFIO:
>    vfio-pci could zap active BAR mappings under memory_lock and set a state
>    flag / disable memory decoding, drop memory_lock before calling
>    pci_try_reset_function(), and then re-acquire memory_lock to re-enable
>    memory. While resetting, any concurrent user fault will see the memory
>    disabled condition and return VM_FAULT_SIGBUS safely.

This would change the userspace-visible behavior of faulting on a VFIO
device BAR from "block until reset is done and the succeed" to "fail
with SIGBUS". And it would allow VFIO to access VFIO device BARs during
the reset through vfio_pci_core_iowrite*().

But I think we can extend this idea to solve those problems by
introducing a wait queue for tasks to sit on while a device is being
reset.

e.g. Something like this (completely untested and partially written by AI):

From: David Matlack <dmatlack@google.com>
Date: Tue, 25 Aug 2026 18:37:52 +0000
Subject: [PATCH] vfio/pci: Avoid circular locking dependency during device reset

Avoid a circular locking dependency during VFIO device reset by dropping
vdev->memory_lock prior to calling PCI reset functions
(pci_try_reset_function() and pci_reset_bus()). Introduce an explicit reset
state flag (vdev->resetting) and wait queue (vdev->reset_done_wq) to stall
concurrent BAR page faults and MMIO accesses during reset without holding
vdev->memory_lock across PCI reset operations.

Export a new helper function vfio_pci_core_try_reset() to consolidate the
common reset sequence across vfio_pci_ioctl_reset() and PCI config space
FLR handlers.

Export a new helper function vfio_pci_core_down_read_memory_lock() to
encapsulate acquiring down_read(&vdev->memory_lock) while waiting on
vdev->reset_done_wq if a reset is in progress.

Holding vdev->memory_lock across pci_try_reset_function() or pci_reset_bus()
causes a lock inversion between vdev->memory_lock and the IOMMU group
mutex (group->mutex). PCI reset functions invoke
pci_dev_reset_iommu_prepare(), which acquires group->mutex. However,
vdev->memory_lock is acquired inside page fault context
(vfio_pci_mmap_huge_fault()), placing vdev->memory_lock below mm->mmap_lock
in the locking hierarchy. Meanwhile, operations holding group->mutex (such
as sysfs interactions or IOMMU domain operations) can fault on user memory,
placing group->mutex above mm->mmap_lock. This establishes the circular
locking chain:

  group->mutex --> mm->mmap_lock --> vdev->memory_lock --> group->mutex

Simply dropping vdev->memory_lock before calling PCI reset routines would
alter userspace-visible behavior by causing concurrent BAR page faults
during reset to fail with VM_FAULT_SIGBUS rather than stalling until reset
completes.

Preserve the stalling behavior while resolving the deadlock:
1. In vfio_pci_core_try_reset(), set vdev->resetting = true under
   vdev->memory_lock, zap BAR mmaps, and drop vdev->memory_lock before
   calling pci_try_reset_function().
2. In vfio_pci_core_down_read_memory_lock(), if vdev->resetting is true, drop
   memory_lock and sleep uninterruptibly on vdev->reset_done_wq via wait_event().
3. Upon reset completion, clear vdev->resetting = false and wake up the wait
   queue.

Fixes: f5b16b802174 ("PCI: Suspend iommu function prior to resetting a device")
Signed-off-by: David Matlack <dmatlack@google.com>
---
 drivers/vfio/pci/vfio_pci_config.c | 20 +++---------
 drivers/vfio/pci/vfio_pci_core.c   | 51 ++++++++++++++++++++++++++----
 drivers/vfio/pci/vfio_pci_rdwr.c   |  4 +--
 include/linux/vfio_pci_core.h      |  4 +++
 4 files changed, 54 insertions(+), 25 deletions(-)

diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c
index 9914f3ac69ae..4980f53c21ca 100644
--- a/drivers/vfio/pci/vfio_pci_config.c
+++ b/drivers/vfio/pci/vfio_pci_config.c
@@ -907,14 +907,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))
+			vfio_pci_core_try_reset(vdev);
 	}
 
 	/*
@@ -992,14 +986,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))
+			vfio_pci_core_try_reset(vdev);
 	}
 
 	return count;
diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c
index 6a184588ff23..ba5405f9980e 100644
--- a/drivers/vfio/pci/vfio_pci_core.c
+++ b/drivers/vfio/pci/vfio_pci_core.c
@@ -1315,15 +1315,12 @@ static int vfio_pci_ioctl_set_irqs(struct vfio_pci_core_device *vdev,
 	return ret;
 }
 
-static int vfio_pci_ioctl_reset(struct vfio_pci_core_device *vdev,
-				void __user *arg)
+int vfio_pci_core_try_reset(struct vfio_pci_core_device *vdev)
 {
 	int ret;
 
-	if (!vdev->reset_works)
-		return -EINVAL;
-
 	vfio_pci_zap_and_down_write_memory_lock(vdev);
+	vdev->resetting = true;
 
 	/*
 	 * This function can be invoked while the power state is non-D0. If
@@ -1337,13 +1334,29 @@ static int vfio_pci_ioctl_reset(struct vfio_pci_core_device *vdev,
 	vfio_pci_set_power_state(vdev, PCI_D0);
 
 	vfio_pci_dma_buf_move(vdev, true);
+	up_write(&vdev->memory_lock);
+
 	ret = pci_try_reset_function(vdev->pdev);
+
+	down_write(&vdev->memory_lock);
+	vdev->resetting = false;
+	wake_up_all(&vdev->reset_done_wq);
 	if (__vfio_pci_memory_enabled(vdev))
 		vfio_pci_dma_buf_move(vdev, false);
 	up_write(&vdev->memory_lock);
 
 	return ret;
 }
+EXPORT_SYMBOL_GPL(vfio_pci_core_try_reset);
+
+static int vfio_pci_ioctl_reset(struct vfio_pci_core_device *vdev,
+				void __user *arg)
+{
+	if (!vdev->reset_works)
+		return -EINVAL;
+
+	return vfio_pci_core_try_reset(vdev);
+}
 
 static int vfio_pci_ioctl_get_pci_hot_reset_info(
 	struct vfio_pci_core_device *vdev,
@@ -1742,6 +1755,19 @@ void vfio_pci_memory_unlock_and_restore(struct vfio_pci_core_device *vdev, u16 c
 	up_write(&vdev->memory_lock);
 }
 
+void vfio_pci_core_down_read_memory_lock(struct vfio_pci_core_device *vdev)
+{
+	while (1) {
+		down_read(&vdev->memory_lock);
+		if (!vdev->resetting)
+			return;
+
+		up_read(&vdev->memory_lock);
+		wait_event(vdev->reset_done_wq, !vdev->resetting);
+	}
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_down_read_memory_lock);
+
 static unsigned long vma_to_pfn(struct vm_area_struct *vma)
 {
 	struct vfio_pci_core_device *vdev = vma->vm_private_data;
@@ -1788,8 +1814,9 @@ static vm_fault_t vfio_pci_mmap_huge_fault(struct vm_fault *vmf,
 	vm_fault_t ret = VM_FAULT_FALLBACK;
 
 	if (is_aligned_for_order(vma, addr, pfn, order)) {
-		scoped_guard(rwsem_read, &vdev->memory_lock)
-			ret = vfio_pci_vmf_insert_pfn(vdev, vmf, pfn, order);
+		vfio_pci_core_down_read_memory_lock(vdev);
+		ret = vfio_pci_vmf_insert_pfn(vdev, vmf, pfn, order);
+		up_read(&vdev->memory_lock);
 	}
 
 	dev_dbg_ratelimited(&vdev->pdev->dev,
@@ -2198,6 +2225,7 @@ int vfio_pci_core_init_dev(struct vfio_device *core_vdev)
 		return ret;
 	INIT_LIST_HEAD(&vdev->dmabufs);
 	init_rwsem(&vdev->memory_lock);
+	init_waitqueue_head(&vdev->reset_done_wq);
 	xa_init(&vdev->ctx);
 
 	return 0;
@@ -2577,6 +2605,7 @@ static int vfio_pci_dev_set_hot_reset(struct vfio_device_set *dev_set,
 			break;
 		}
 
+		vdev->resetting = true;
 		vfio_pci_dma_buf_move(vdev, true);
 		vfio_pci_zap_bars(vdev);
 	}
@@ -2599,14 +2628,22 @@ static int vfio_pci_dev_set_hot_reset(struct vfio_device_set *dev_set,
 	list_for_each_entry(vdev, &dev_set->device_list, vdev.dev_set_list)
 		vfio_pci_set_power_state(vdev, PCI_D0);
 
+	list_for_each_entry(vdev, &dev_set->device_list, vdev.dev_set_list)
+		up_write(&vdev->memory_lock);
+
 	ret = pci_reset_bus(pdev);
 
+	list_for_each_entry(vdev, &dev_set->device_list, vdev.dev_set_list)
+		down_write(&vdev->memory_lock);
+
 	vdev = list_last_entry(&dev_set->device_list,
 			       struct vfio_pci_core_device, vdev.dev_set_list);
 
 err_undo:
 	list_for_each_entry_from_reverse(vdev, &dev_set->device_list,
 					 vdev.dev_set_list) {
+		vdev->resetting = false;
+		wake_up_all(&vdev->reset_done_wq);
 		if (vdev->vdev.open_count && __vfio_pci_memory_enabled(vdev))
 			vfio_pci_dma_buf_move(vdev, false);
 		up_write(&vdev->memory_lock);
diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c
index 7f14dd46de17..4313972e35ef 100644
--- a/drivers/vfio/pci/vfio_pci_rdwr.c
+++ b/drivers/vfio/pci/vfio_pci_rdwr.c
@@ -43,7 +43,7 @@ int vfio_pci_core_iowrite##size(struct vfio_pci_core_device *vdev,	\
 			bool test_mem, u##size val, void __iomem *io)	\
 {									\
 	if (test_mem) {							\
-		down_read(&vdev->memory_lock);				\
+		vfio_pci_core_down_read_memory_lock(vdev);		\
 		if (!__vfio_pci_memory_enabled(vdev)) {			\
 			up_read(&vdev->memory_lock);			\
 			return -EIO;					\
@@ -69,7 +69,7 @@ int vfio_pci_core_ioread##size(struct vfio_pci_core_device *vdev,	\
 			bool test_mem, u##size *val, void __iomem *io)	\
 {									\
 	if (test_mem) {							\
-		down_read(&vdev->memory_lock);				\
+		vfio_pci_core_down_read_memory_lock(vdev);		\
 		if (!__vfio_pci_memory_enabled(vdev)) {			\
 			up_read(&vdev->memory_lock);			\
 			return -EIO;					\
diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h
index 9a1674c152aa..152662be3795 100644
--- a/include/linux/vfio_pci_core.h
+++ b/include/linux/vfio_pci_core.h
@@ -148,6 +148,8 @@ struct vfio_pci_core_device {
 	struct vfio_pci_core_device	*sriov_pf_core_dev;
 	struct notifier_block	nb;
 	struct rw_semaphore	memory_lock;
+	bool			resetting;
+	wait_queue_head_t	reset_done_wq;
 	struct list_head	dmabufs;
 };
 
@@ -171,6 +173,8 @@ void vfio_pci_core_unregister_device(struct vfio_pci_core_device *vdev);
 extern const struct pci_error_handlers vfio_pci_core_err_handlers;
 int vfio_pci_core_sriov_configure(struct vfio_pci_core_device *vdev,
 				  int nr_virtfn);
+int vfio_pci_core_try_reset(struct vfio_pci_core_device *vdev);
+void vfio_pci_core_down_read_memory_lock(struct vfio_pci_core_device *vdev);
 long vfio_pci_core_ioctl(struct vfio_device *core_vdev, unsigned int cmd,
 		unsigned long arg);
 int vfio_pci_core_ioctl_feature(struct vfio_device *device, u32 flags,

> 
> 3. Refine synchronization in pci_dev_reset_iommu_prepare():
>    Evaluate if attaching to the blocking domain and pausing ATS during device
>    reset can be protected using more fine-grained locking or atomic state
>    flags without holding the coarse &group->mutex.

I don't know enough about this part of the kernel to say, but this would
directly address the new lock ordering dependency vdev->memory_lock -->
group->mutex introduced by commit f5b16b802174 ("PCI: Suspend iommu function
prior to resetting a device"), which is what led to this lockdep error.

^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-08-25 19:06 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-21 19:35 [RFC PATCH 0/1] vfio: circular locking dependency in pci_dev_reset_iommu_prepare() Vipin Sharma
2026-08-21 19:35 ` [RFC PATCH 1/1] selftests: vfio: Add mmap fault and device reset test Vipin Sharma
2026-08-21 19:46   ` sashiko-bot
2026-08-25 19:06 ` [RFC PATCH 0/1] vfio: circular locking dependency in pci_dev_reset_iommu_prepare() David Matlack

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox