VFIO fixes for v7.2-rc2

- Fix racy bitfield updates in vfio-pci-core and the mlx5 vfio-pci
    variant driver with a binary split between setup/release and
    runtime modified flags.  These were noted across several Sashiko
    reviews as pre-existing issues. (Alex Williamson)
 
  - Fix runtime PM inconsistency where the vfio-pci driver module_init
    could modify the idle PM policy of existing devices through globals
    managed in vfio-pci-core, leading to unbalanced runtime PM
    operations. (Alex Williamson)
 
  - Restore mutability of writable vfio-pci module options by further
    pulling policy globals out of vfio-pci-core, to instead be latched
    per device at device init.  Provide visibility of the per device
    latched values through debugfs. (Alex Williamson)
 
  - Fix missing VGA arbiter uninit callback in unwind path.
    (Alex Williamson)
 
  - Reorder device debugfs removal before device_del() to avoid gap
    where debugfs is available with stale devres pointers.
    (Alex Williamson)
 
  - Move UUID library linking flag from vfio selftest Makefile into
    libvfio.mk to avoid exposing such dependencies when linking with
    KVM selftests. (Sean Christopherson)
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCgAvFiEEQvbATlQL0amee4qQI5ubbjuwiyIFAmpGoBgRHGFsZXhAc2hh
 emJvdC5vcmcACgkQI5ubbjuwiyIsTQ//aBSIKmchiU701m5Cl9FP/er0IRkjqmJd
 EoKseoQoFvBi9Rsog5RiDwTnsu3NTcvqaZgcWshxJZ0G3/rPP6wTqr3W3sGMqsXp
 q6EwdeebmCsBecc14gexMjic6azS3w4IyqDGo56X30i7BbRAxpQdnlOG2oGdCmz6
 bxjIlRVYiYgC4NYJM5wwpx+fVxcHmoejgKQVxetG3ersvW/PoWR8DT8AEP1iDA2W
 /jhj+aqX0Il7+Wxxxxhw7qQoBe0U1U1uz3+frRVAMwBQY0dTsMzU6aGuT+ajbTJC
 /rgXSiXVDUoRaIUGi8TolWwXXE+V9IpXoNouEokCoQ6WoxiThhS4codKwveArWOv
 C4LdEV1XLiZTBFj59q4DKCj7+6mYWK2z8OylPIUxXckxvWsEH8b4U/BjMH+NwyAy
 hn854fEpMA0SSIl4GEJRocZcH7C+EzE7CDDBop18e4fKDIwjU3odnci5c1titG8W
 kvqSel11JYnOnThLCF8qvqAHmRJqqbtUurlrzPQwWif+tNwAl/QVyHYxr/HY4UMB
 wLKgfb9TbFD2jH1b3KPOeUv8I2AkIBbXARsBZXn1r0+MWHr0hRkrlgir1thTzcxn
 yau7OWlkOnbf9NCiB+KyZ0xwqhG+dBJWmX2lzQcDMQ/1hbrqOTfAaKvg9kRZNdjn
 qckJIv7VGwQ=
 =V/L3
 -----END PGP SIGNATURE-----

Merge tag 'vfio-v7.2-rc2' of https://github.com/awilliam/linux-vfio

Pull VFIO fixes from Alex Williamson:
 "Mostly straightforward fixes here, inconsistent runtime PM handling
  due to global device policies, bitfield races, unwind path gaps,
  teardown ordering, and a misplaced library flag.

   - Fix racy bitfield updates in vfio-pci-core and the mlx5 vfio-pci
     variant driver with a binary split between setup/release and
     runtime modified flags. These were noted across several Sashiko
     reviews as pre-existing issues (Alex Williamson)

   - Fix runtime PM inconsistency where the vfio-pci driver module_init
     could modify the idle PM policy of existing devices through globals
     managed in vfio-pci-core, leading to unbalanced runtime PM
     operations (Alex Williamson)

   - Restore mutability of writable vfio-pci module options by further
     pulling policy globals out of vfio-pci-core, to instead be latched
     per device at device init. Provide visibility of the per device
     latched values through debugfs (Alex Williamson)

   - Fix missing VGA arbiter uninit callback in unwind path (Alex
     Williamson)

   - Reorder device debugfs removal before device_del() to avoid gap
     where debugfs is available with stale devres pointers (Alex
     Williamson)

   - Move UUID library linking flag from vfio selftest Makefile into
     libvfio.mk to avoid exposing such dependencies when linking with
     KVM selftests (Sean Christopherson)"

* tag 'vfio-v7.2-rc2' of https://github.com/awilliam/linux-vfio:
  vfio: selftests: Add luuid to libvfio.mk's list of libraries, not to the Makefile
  vfio/pci: Expose latched module parameter policy in debugfs
  vfio: Remove device debugfs before releasing devres
  vfio/pci: Latch all module parameters per device
  vfio/mlx5: Fix racy bitfields and tighten struct layout
  vfio/pci: Fix racy bitfields and tighten struct layout
  vfio/pci: Release the VGA arbiter client on register_device() failure
  vfio/pci: Latch disable_idle_d3 per device
This commit is contained in:
Linus Torvalds 2026-07-02 15:25:54 -10:00
commit 826eec5b5e
9 changed files with 152 additions and 51 deletions

View File

@ -29,3 +29,29 @@ Date: Oct 2025
KernelVersion: 6.18
Contact: Cédric Le Goater <clg@redhat.com>
Description: Read the migration features of the vfio device.
What: /sys/kernel/debug/vfio/<device>/pci
Date: June 2026
KernelVersion: 7.2
Contact: Alex Williamson <alex.williamson@nvidia.com>
Description: This debugfs file directory is used for debugging
VFIO PCI devices.
What: /sys/kernel/debug/vfio/<device>/pci/nointxmask
Date: June 2026
KernelVersion: 7.2
Contact: Alex Williamson <alex.williamson@nvidia.com>
Description: Read the nointxmask policy latched for this device. This
policy governs whether the device may use PCI 2.3 style
INTx masking when supported, reporting a value of "N", or
requires APIC level INTx masking, reporting a value of "Y".
What: /sys/kernel/debug/vfio/<device>/pci/disable_idle_d3
Date: June 2026
KernelVersion: 7.2
Contact: Alex Williamson <alex.williamson@nvidia.com>
Description: Read the disable_idle_d3 policy latched for this device. This
policy governs whether the device PM runtime usage count is
kept elevated while the device is bound to the driver and
unused, reporting a value of "Y", or decremented to allow the
device to enter a low power state, reporting a value of "N".

View File

@ -97,6 +97,7 @@ void vfio_device_debugfs_init(struct vfio_device *vdev)
void vfio_device_debugfs_exit(struct vfio_device *vdev)
{
debugfs_remove_recursive(vdev->debug_root);
vdev->debug_root = NULL;
}
void vfio_debugfs_create_root(void)

View File

@ -158,26 +158,29 @@ struct mlx5_vhca_qp {
struct mlx5_vhca_page_tracker {
u32 id;
u32 pdn;
u8 is_err:1;
u8 object_changed:1;
/* Flags modified at runtime - dedicated storage unit */
u8 is_err;
u8 object_changed;
int status;
struct mlx5_uars_page *uar;
struct mlx5_vhca_cq cq;
struct mlx5_vhca_qp *host_qp;
struct mlx5_vhca_qp *fw_qp;
struct mlx5_nb nb;
int status;
};
struct mlx5vf_pci_core_device {
struct vfio_pci_core_device core_device;
int vf_id;
u16 vhca_id;
/* Flags only modified on setup/release - bitfield ok */
u8 migrate_cap:1;
u8 deferred_reset:1;
u8 mdev_detach:1;
u8 log_active:1;
u8 chunk_mode:1;
u8 mig_state_cap:1;
/* Flags modified at runtime - dedicated storage unit */
u8 mdev_detach;
u8 log_active;
u8 deferred_reset;
struct completion tracker_comp;
/* protect migration state */
struct mutex state_mutex;

View File

@ -125,9 +125,30 @@ static int vfio_pci_open_device(struct vfio_device *core_vdev)
return 0;
}
static int vfio_pci_init_dev(struct vfio_device *core_vdev)
{
struct vfio_pci_core_device *vdev =
container_of(core_vdev, struct vfio_pci_core_device, vdev);
/*
* These behaviors originated in vfio-pci and moved into
* vfio-pci-core when the driver was split; vfio-pci remains the
* only driver that toggles them. Latch our module parameters per
* device at init time so that later parameter changes do not
* affect already-initialized devices.
*/
vdev->nointxmask = nointxmask;
vdev->disable_idle_d3 = disable_idle_d3;
#ifdef CONFIG_VFIO_PCI_VGA
vdev->disable_vga = disable_vga;
#endif
return vfio_pci_core_init_dev(core_vdev);
}
static const struct vfio_device_ops vfio_pci_ops = {
.name = "vfio-pci",
.init = vfio_pci_core_init_dev,
.init = vfio_pci_init_dev,
.release = vfio_pci_core_release_dev,
.open_device = vfio_pci_open_device,
.close_device = vfio_pci_core_close_device,
@ -256,13 +277,6 @@ static void __init vfio_pci_fill_ids(void)
static int __init vfio_pci_init(void)
{
int ret;
bool is_disable_vga = true;
#ifdef CONFIG_VFIO_PCI_VGA
is_disable_vga = disable_vga;
#endif
vfio_pci_core_set_params(nointxmask, is_disable_vga, disable_idle_d3);
/* Register and scan for devices */
ret = pci_register_driver(&vfio_pci_driver);

View File

@ -11,6 +11,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/aperture.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/eventfd.h>
#include <linux/file.h>
@ -29,6 +30,7 @@
#include <linux/sched/mm.h>
#include <linux/iommufd.h>
#include <linux/pci-p2pdma.h>
#include <linux/seq_file.h>
#if IS_ENABLED(CONFIG_EEH)
#include <asm/eeh.h>
#endif
@ -38,10 +40,6 @@
#define DRIVER_AUTHOR "Alex Williamson <alex.williamson@redhat.com>"
#define DRIVER_DESC "core driver for VFIO based PCI devices"
static bool nointxmask;
static bool disable_vga;
static bool disable_idle_d3;
static void vfio_pci_eventfd_rcu_free(struct rcu_head *rcu)
{
struct vfio_pci_eventfd *eventfd =
@ -92,15 +90,69 @@ struct vfio_pci_vf_token {
int users;
};
static inline bool vfio_vga_disabled(void)
static inline bool vfio_vga_disabled(struct vfio_pci_core_device *vdev)
{
#ifdef CONFIG_VFIO_PCI_VGA
return disable_vga;
return vdev->disable_vga;
#else
return true;
#endif
}
#ifdef CONFIG_VFIO_DEBUGFS
static struct vfio_pci_core_device *
vfio_pci_core_debugfs_private(struct seq_file *seq)
{
struct device *dev = seq->private;
struct vfio_device *core_vdev = container_of(dev, struct vfio_device,
device);
return container_of(core_vdev, struct vfio_pci_core_device, vdev);
}
static int vfio_pci_core_debugfs_nointxmask(struct seq_file *seq, void *data)
{
struct vfio_pci_core_device *vdev = vfio_pci_core_debugfs_private(seq);
seq_puts(seq, vdev->nointxmask ? "Y\n" : "N\n");
return 0;
}
static int vfio_pci_core_debugfs_disable_idle_d3(struct seq_file *seq,
void *data)
{
struct vfio_pci_core_device *vdev = vfio_pci_core_debugfs_private(seq);
seq_puts(seq, vdev->disable_idle_d3 ? "Y\n" : "N\n");
return 0;
}
/*
* disable_idle_d3 and nointxmask are writable module parameters latched
* per device at init, so a device's effective value can differ from the
* current parameter setting. Expose the per-device (read-only) values
* here for visibility; read-only parameters can't drift and are omitted.
*/
static void vfio_pci_core_debugfs_init(struct vfio_pci_core_device *vdev)
{
struct device *dev = &vdev->vdev.device;
struct dentry *pci_dir;
if (IS_ERR_OR_NULL(vdev->vdev.debug_root))
return;
pci_dir = debugfs_create_dir("pci", vdev->vdev.debug_root);
debugfs_create_devm_seqfile(dev, "nointxmask", pci_dir,
vfio_pci_core_debugfs_nointxmask);
debugfs_create_devm_seqfile(dev, "disable_idle_d3", pci_dir,
vfio_pci_core_debugfs_disable_idle_d3);
}
#else
static inline void vfio_pci_core_debugfs_init(struct vfio_pci_core_device *vdev)
{
}
#endif /* CONFIG_VFIO_DEBUGFS */
/*
* Our VGA arbiter participation is limited since we don't know anything
* about the device itself. However, if the device is the only VGA device
@ -111,11 +163,12 @@ static inline bool vfio_vga_disabled(void)
*/
static unsigned int vfio_pci_set_decode(struct pci_dev *pdev, bool single_vga)
{
struct vfio_pci_core_device *vdev = dev_get_drvdata(&pdev->dev);
struct pci_dev *tmp = NULL;
unsigned char max_busnr;
unsigned int decodes;
if (single_vga || !vfio_vga_disabled() || pci_is_root_bus(pdev->bus))
if (single_vga || !vfio_vga_disabled(vdev) || pci_is_root_bus(pdev->bus))
return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM |
VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM;
@ -538,7 +591,7 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)
u16 cmd;
u8 msix_pos;
if (!disable_idle_d3) {
if (!vdev->disable_idle_d3) {
ret = pm_runtime_resume_and_get(&pdev->dev);
if (ret < 0)
return ret;
@ -562,7 +615,7 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)
if (!vdev->pci_saved_state)
pci_dbg(pdev, "%s: Couldn't store saved state\n", __func__);
if (likely(!nointxmask)) {
if (likely(!vdev->nointxmask)) {
if (vfio_pci_nointx(pdev)) {
pci_info(pdev, "Masking broken INTx support\n");
vdev->nointx = true;
@ -602,7 +655,7 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)
vdev->has_dyn_msix = false;
}
if (!vfio_vga_disabled() && vfio_pci_is_vga(pdev))
if (!vfio_vga_disabled(vdev) && vfio_pci_is_vga(pdev))
vdev->has_vga = true;
vfio_pci_core_map_bars(vdev);
@ -617,7 +670,7 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)
out_disable_device:
pci_disable_device(pdev);
out_power:
if (!disable_idle_d3)
if (!vdev->disable_idle_d3)
pm_runtime_put(&pdev->dev);
return ret;
}
@ -753,7 +806,7 @@ void vfio_pci_core_disable(struct vfio_pci_core_device *vdev)
vfio_pci_dev_set_try_reset(vdev->vdev.dev_set);
/* Put the pm-runtime usage counter acquired during enable */
if (!disable_idle_d3)
if (!vdev->disable_idle_d3)
pm_runtime_put(&pdev->dev);
}
EXPORT_SYMBOL_GPL(vfio_pci_core_disable);
@ -2239,19 +2292,23 @@ int vfio_pci_core_register_device(struct vfio_pci_core_device *vdev)
dev->driver->pm = &vfio_pci_core_pm_ops;
pm_runtime_allow(dev);
if (!disable_idle_d3)
if (!vdev->disable_idle_d3)
pm_runtime_put(dev);
ret = vfio_register_group_dev(&vdev->vdev);
if (ret)
goto out_power;
vfio_pci_core_debugfs_init(vdev);
return 0;
out_power:
if (!disable_idle_d3)
if (!vdev->disable_idle_d3)
pm_runtime_get_noresume(dev);
pm_runtime_forbid(dev);
vfio_pci_vga_uninit(vdev);
out_vf:
vfio_pci_vf_uninit(vdev);
return ret;
@ -2267,7 +2324,7 @@ void vfio_pci_core_unregister_device(struct vfio_pci_core_device *vdev)
vfio_pci_vf_uninit(vdev);
vfio_pci_vga_uninit(vdev);
if (!disable_idle_d3)
if (!vdev->disable_idle_d3)
pm_runtime_get_noresume(&vdev->pdev->dev);
pm_runtime_forbid(&vdev->pdev->dev);
@ -2599,7 +2656,7 @@ static void vfio_pci_dev_set_try_reset(struct vfio_device_set *dev_set)
* state. Increment the usage count for all the devices in the dev_set
* before reset and decrement the same after reset.
*/
if (!disable_idle_d3 && vfio_pci_dev_set_pm_runtime_get(dev_set))
if (vfio_pci_dev_set_pm_runtime_get(dev_set))
return;
if (!pci_reset_bus(pdev))
@ -2609,20 +2666,10 @@ static void vfio_pci_dev_set_try_reset(struct vfio_device_set *dev_set)
if (reset_done)
cur->needs_reset = false;
if (!disable_idle_d3)
pm_runtime_put(&cur->pdev->dev);
pm_runtime_put(&cur->pdev->dev);
}
}
void vfio_pci_core_set_params(bool is_nointxmask, bool is_disable_vga,
bool is_disable_idle_d3)
{
nointxmask = is_nointxmask;
disable_vga = is_disable_vga;
disable_idle_d3 = is_disable_idle_d3;
}
EXPORT_SYMBOL_GPL(vfio_pci_core_set_params);
static void vfio_pci_core_cleanup(void)
{
vfio_pci_uninit_perm_bits();

View File

@ -406,6 +406,13 @@ void vfio_unregister_group_dev(struct vfio_device *device)
*/
vfio_device_group_unregister(device);
/*
* Remove debugfs before device_del(), which releases devres. Some
* debugfs entries are created with debugfs_create_devm_seqfile() and
* therefore rely on devres-managed inode private data.
*/
vfio_device_debugfs_exit(device);
/*
* Balances vfio_device_add() in register path, also prevents
* new device opened by userspace in the cdev path.
@ -435,7 +442,6 @@ void vfio_unregister_group_dev(struct vfio_device *device)
}
}
vfio_device_debugfs_exit(device);
/* Balances vfio_device_set_group in register path */
vfio_device_remove_group(device);
}

View File

@ -101,6 +101,9 @@ struct vfio_pci_core_device {
const struct vfio_pci_device_ops *pci_ops;
void __iomem *barmap[PCI_STD_NUM_BARS];
bool bar_mmap_supported[PCI_STD_NUM_BARS];
/* Flags modified at runtime - dedicated storage unit */
bool virq_disabled;
bool bardirty;
u8 *pci_config_map;
u8 *vconfig;
struct perm_bits *msi_perm;
@ -115,18 +118,21 @@ struct vfio_pci_core_device {
u16 msix_size;
u32 msix_offset;
u32 rbar[7];
/* Flags only modified on setup/release - bitfield ok */
bool has_dyn_msix:1;
bool pci_2_3:1;
bool virq_disabled:1;
bool reset_works:1;
bool extended_caps:1;
bool bardirty:1;
bool has_vga:1;
bool needs_reset:1;
bool nointx:1;
bool needs_pm_restore:1;
bool pm_intx_masked:1;
bool pm_runtime_engaged:1;
bool disable_idle_d3:1;
bool nointxmask:1;
bool disable_vga:1;
/* Flags modified at runtime - dedicated storage unit */
bool needs_reset;
bool pm_intx_masked;
bool pm_runtime_engaged;
bool sriov_active;
struct pci_saved_state *pci_saved_state;
struct pci_saved_state *pm_save;
@ -157,8 +163,6 @@ int vfio_pci_core_register_dev_region(struct vfio_pci_core_device *vdev,
unsigned int type, unsigned int subtype,
const struct vfio_pci_regops *ops,
size_t size, u32 flags, void *data);
void vfio_pci_core_set_params(bool nointxmask, bool is_disable_vga,
bool is_disable_idle_d3);
void vfio_pci_core_close_device(struct vfio_device *core_vdev);
int vfio_pci_core_init_dev(struct vfio_device *core_vdev);
void vfio_pci_core_release_dev(struct vfio_device *core_vdev);

View File

@ -29,8 +29,6 @@ CFLAGS += $(EXTRA_CFLAGS)
LDFLAGS += -pthread
LDLIBS += -luuid
$(TEST_GEN_PROGS): $(OUTPUT)/%: $(OUTPUT)/%.o $(LIBVFIO_O)
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $< $(LIBVFIO_O) $(LDLIBS) -o $@

View File

@ -26,6 +26,8 @@ $(LIBVFIO_O_DIRS):
CFLAGS += -I$(LIBVFIO_SRCDIR)/include
LDLIBS += -luuid
$(LIBVFIO_O): $(LIBVFIO_OUTPUT)/%.o : $(LIBVFIO_SRCDIR)/%.c | $(LIBVFIO_O_DIRS)
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@