mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-09-10 09:55:38 +02:00
The current implementation uses pci_num_vf() while holding the
memory_lock to prevent changing the power state of a PF when
VFs are enabled. This creates a lockdep circular dependency
warning because memory_lock is held during device probing.
[ 286.997167] ======================================================
[ 287.003363] WARNING: possible circular locking dependency detected
[ 287.009562] 7.0.0-dbg-DEV #3 Tainted: G S
[ 287.015074] ------------------------------------------------------
[ 287.021270] vfio_pci_sriov_/18636 is trying to acquire lock:
[ 287.026942] ff45bea2294d4968 (&vdev->memory_lock){+.+.}-{4:4}, at:
vfio_pci_core_runtime_resume+0x1f/0xa0
[ 287.036530]
[ 287.036530] but task is already holding lock:
[ 287.042383] ff45bea3a96b8230 (&new_dev_set->lock){+.+.}-{4:4}, at:
vfio_group_fops_unl_ioctl+0x44d/0x7b0
[ 287.051879]
[ 287.051879] which lock already depends on the new lock.
[ 287.051879]
[ 287.060070]
[ 287.060070] the existing dependency chain (in reverse order) is:
[ 287.067568]
[ 287.067568] -> #2 (&new_dev_set->lock){+.+.}-{4:4}:
[ 287.073941] __mutex_lock+0x92/0xb80
[ 287.078058] vfio_assign_device_set+0x66/0x1b0
[ 287.083042] vfio_pci_core_register_device+0xd1/0x2a0
[ 287.088638] vfio_pci_probe+0xd2/0x100
[ 287.092933] local_pci_probe_callback+0x4d/0xa0
[ 287.098001] process_scheduled_works+0x2ca/0x680
[ 287.103158] worker_thread+0x1e8/0x2f0
[ 287.107452] kthread+0x10c/0x140
[ 287.111230] ret_from_fork+0x18e/0x360
[ 287.115519] ret_from_fork_asm+0x1a/0x30
[ 287.119983]
[ 287.119983] -> #1 ((work_completion)(&arg.work)){+.+.}-{0:0}:
[ 287.127219] __flush_work+0x345/0x490
[ 287.131429] pci_device_probe+0x2e3/0x490
[ 287.135979] really_probe+0x1f9/0x4e0
[ 287.140180] __driver_probe_device+0x77/0x100
[ 287.145079] driver_probe_device+0x1e/0x110
[ 287.149803] __device_attach_driver+0xe3/0x170
[ 287.154789] bus_for_each_drv+0x125/0x150
[ 287.159346] __device_attach+0xca/0x1a0
[ 287.163720] device_initial_probe+0x34/0x50
[ 287.168445] pci_bus_add_device+0x6e/0x90
[ 287.172995] pci_iov_add_virtfn+0x3c9/0x3e0
[ 287.177719] sriov_add_vfs+0x2c/0x60
[ 287.181838] sriov_enable+0x306/0x4a0
[ 287.186038] vfio_pci_core_sriov_configure+0x184/0x220
[ 287.191715] sriov_numvfs_store+0xd9/0x1c0
[ 287.196351] kernfs_fop_write_iter+0x13f/0x1d0
[ 287.201338] vfs_write+0x2be/0x3b0
[ 287.205286] ksys_write+0x73/0x100
[ 287.209233] do_syscall_64+0x14d/0x750
[ 287.213529] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 287.219120]
[ 287.219120] -> #0 (&vdev->memory_lock){+.+.}-{4:4}:
[ 287.225491] __lock_acquire+0x14c6/0x2800
[ 287.230048] lock_acquire+0xd3/0x2f0
[ 287.234168] down_write+0x3a/0xc0
[ 287.238019] vfio_pci_core_runtime_resume+0x1f/0xa0
[ 287.243436] __rpm_callback+0x8c/0x310
[ 287.247730] rpm_resume+0x529/0x6f0
[ 287.251765] __pm_runtime_resume+0x68/0x90
[ 287.256402] vfio_pci_core_enable+0x44/0x310
[ 287.261216] vfio_pci_open_device+0x1c/0x80
[ 287.265947] vfio_df_open+0x10f/0x150
[ 287.270148] vfio_group_fops_unl_ioctl+0x4a4/0x7b0
[ 287.275476] __se_sys_ioctl+0x71/0xc0
[ 287.279679] do_syscall_64+0x14d/0x750
[ 287.283975] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 287.289559]
[ 287.289559] other info that might help us debug this:
[ 287.289559]
[ 287.297582] Chain exists of:
[ 287.297582] &vdev->memory_lock --> (work_completion)(&arg.work)
--> &new_dev_set->lock
[ 287.297582]
[ 287.310023] Possible unsafe locking scenario:
[ 287.310023]
[ 287.315961] CPU0 CPU1
[ 287.320510] ---- ----
[ 287.325059] lock(&new_dev_set->lock);
[ 287.328917]
lock((work_completion)(&arg.work));
[ 287.336153] lock(&new_dev_set->lock);
[ 287.342523] lock(&vdev->memory_lock);
[ 287.346382]
[ 287.346382] *** DEADLOCK ***
[ 287.346382]
[ 287.352315] 2 locks held by vfio_pci_sriov_/18636:
[ 287.357125] #0: ff45bea208ed3e18 (&group->group_lock){+.+.}-{4:4},
at: vfio_group_fops_unl_ioctl+0x3e3/0x7b0
[ 287.367048] #1: ff45bea3a96b8230 (&new_dev_set->lock){+.+.}-{4:4},
at: vfio_group_fops_unl_ioctl+0x44d/0x7b0
[ 287.376976]
[ 287.376976] stack backtrace:
[ 287.381353] CPU: 191 UID: 0 PID: 18636 Comm: vfio_pci_sriov_
Tainted: G S 7.0.0-dbg-DEV #3 PREEMPTLAZY
[ 287.381355] Tainted: [S]=CPU_OUT_OF_SPEC
[ 287.381356] Call Trace:
[ 287.381357] <TASK>
[ 287.381358] dump_stack_lvl+0x54/0x70
[ 287.381361] print_circular_bug+0x2e1/0x300
[ 287.381363] check_noncircular+0xf9/0x120
[ 287.381364] ? __lock_acquire+0x5b4/0x2800
[ 287.381366] __lock_acquire+0x14c6/0x2800
[ 287.381368] ? pci_mmcfg_read+0x4f/0x220
[ 287.381370] ? pci_mmcfg_write+0x57/0x220
[ 287.381371] ? lock_acquire+0xd3/0x2f0
[ 287.381373] ? pci_mmcfg_write+0x57/0x220
[ 287.381374] ? lock_release+0xef/0x360
[ 287.381376] ? vfio_pci_core_runtime_resume+0x1f/0xa0
[ 287.381377] lock_acquire+0xd3/0x2f0
[ 287.381378] ? vfio_pci_core_runtime_resume+0x1f/0xa0
[ 287.381379] ? lock_is_held_type+0x76/0x100
[ 287.381382] down_write+0x3a/0xc0
[ 287.381382] ? vfio_pci_core_runtime_resume+0x1f/0xa0
[ 287.381383] vfio_pci_core_runtime_resume+0x1f/0xa0
[ 287.381384] ? __pfx_pci_pm_runtime_resume+0x10/0x10
[ 287.381385] __rpm_callback+0x8c/0x310
[ 287.381386] ? ktime_get_mono_fast_ns+0x3d/0xb0
[ 287.381389] ? __pfx_pci_pm_runtime_resume+0x10/0x10
[ 287.381390] rpm_resume+0x529/0x6f0
[ 287.381392] ? lock_is_held_type+0x76/0x100
[ 287.381394] __pm_runtime_resume+0x68/0x90
[ 287.381396] vfio_pci_core_enable+0x44/0x310
[ 287.381398] vfio_pci_open_device+0x1c/0x80
[ 287.381399] vfio_df_open+0x10f/0x150
[ 287.381401] vfio_group_fops_unl_ioctl+0x4a4/0x7b0
[ 287.381402] __se_sys_ioctl+0x71/0xc0
[ 287.381404] do_syscall_64+0x14d/0x750
[ 287.381405] ? entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 287.381406] ? trace_irq_disable+0x25/0xd0
[ 287.381409] entry_SYSCALL_64_after_hwframe+0x77/0x7f
Introduce a private flag 'sriov_active' in the vfio_pci_core_device
struct. This allows the driver to track the SR-IOV power state requirement
without relying on pci_num_vf() while holding the memory_lock. The lock is
now only held to set the flag and ensure the device is in D0, after which
pci_enable_sriov() can be called without the lock.
Fixes: f4162eb1e2 ("vfio/pci: Change the PF power state to D0 before enabling VFs")
Cc: stable@vger.kernel.org
Suggested-by: Jason Gunthorpe <jgg@ziepe.ca>
Suggested-by: Alex Williamson <alex@shazbot.org>
Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
Link: https://lore.kernel.org/r/20260514173449.3282188-1-rananta@google.com
[promote bitfield to plain bool to avoid storage-unit races]
Signed-off-by: Alex Williamson <alex@shazbot.org>
260 lines
8.5 KiB
C
260 lines
8.5 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
/*
|
|
* Copyright (C) 2012 Red Hat, Inc. All rights reserved.
|
|
* Author: Alex Williamson <alex.williamson@redhat.com>
|
|
*
|
|
* Derived from original vfio:
|
|
* Copyright 2010 Cisco Systems, Inc. All rights reserved.
|
|
* Author: Tom Lyon, pugs@cisco.com
|
|
*/
|
|
|
|
#include <linux/mutex.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/vfio.h>
|
|
#include <linux/irqbypass.h>
|
|
#include <linux/rcupdate.h>
|
|
#include <linux/types.h>
|
|
#include <linux/uuid.h>
|
|
#include <linux/notifier.h>
|
|
|
|
#ifndef VFIO_PCI_CORE_H
|
|
#define VFIO_PCI_CORE_H
|
|
|
|
#define VFIO_PCI_OFFSET_SHIFT 40
|
|
#define VFIO_PCI_OFFSET_TO_INDEX(off) ((u64)(off) >> VFIO_PCI_OFFSET_SHIFT)
|
|
#define VFIO_PCI_INDEX_TO_OFFSET(index) ((u64)(index) << VFIO_PCI_OFFSET_SHIFT)
|
|
#define VFIO_PCI_OFFSET_MASK (((u64)(1) << VFIO_PCI_OFFSET_SHIFT) - 1)
|
|
|
|
struct vfio_pci_core_device;
|
|
struct vfio_pci_region;
|
|
struct p2pdma_provider;
|
|
struct dma_buf_attachment;
|
|
|
|
struct vfio_pci_eventfd {
|
|
struct eventfd_ctx *ctx;
|
|
struct rcu_head rcu;
|
|
};
|
|
|
|
struct vfio_pci_regops {
|
|
ssize_t (*rw)(struct vfio_pci_core_device *vdev, char __user *buf,
|
|
size_t count, loff_t *ppos, bool iswrite);
|
|
void (*release)(struct vfio_pci_core_device *vdev,
|
|
struct vfio_pci_region *region);
|
|
int (*mmap)(struct vfio_pci_core_device *vdev,
|
|
struct vfio_pci_region *region,
|
|
struct vm_area_struct *vma);
|
|
int (*add_capability)(struct vfio_pci_core_device *vdev,
|
|
struct vfio_pci_region *region,
|
|
struct vfio_info_cap *caps);
|
|
};
|
|
|
|
struct vfio_pci_region {
|
|
u32 type;
|
|
u32 subtype;
|
|
const struct vfio_pci_regops *ops;
|
|
void *data;
|
|
size_t size;
|
|
u32 flags;
|
|
};
|
|
|
|
struct vfio_pci_device_ops {
|
|
int (*get_dmabuf_phys)(struct vfio_pci_core_device *vdev,
|
|
struct p2pdma_provider **provider,
|
|
unsigned int region_index,
|
|
struct phys_vec *phys_vec,
|
|
struct vfio_region_dma_range *dma_ranges,
|
|
size_t nr_ranges);
|
|
};
|
|
|
|
#if IS_ENABLED(CONFIG_VFIO_PCI_DMABUF)
|
|
int vfio_pci_core_fill_phys_vec(struct phys_vec *phys_vec,
|
|
struct vfio_region_dma_range *dma_ranges,
|
|
size_t nr_ranges, phys_addr_t start,
|
|
phys_addr_t len);
|
|
int vfio_pci_core_get_dmabuf_phys(struct vfio_pci_core_device *vdev,
|
|
struct p2pdma_provider **provider,
|
|
unsigned int region_index,
|
|
struct phys_vec *phys_vec,
|
|
struct vfio_region_dma_range *dma_ranges,
|
|
size_t nr_ranges);
|
|
#else
|
|
static inline int
|
|
vfio_pci_core_fill_phys_vec(struct phys_vec *phys_vec,
|
|
struct vfio_region_dma_range *dma_ranges,
|
|
size_t nr_ranges, phys_addr_t start,
|
|
phys_addr_t len)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
static inline int vfio_pci_core_get_dmabuf_phys(
|
|
struct vfio_pci_core_device *vdev, struct p2pdma_provider **provider,
|
|
unsigned int region_index, struct phys_vec *phys_vec,
|
|
struct vfio_region_dma_range *dma_ranges, size_t nr_ranges)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
#endif
|
|
|
|
struct vfio_pci_core_device {
|
|
struct vfio_device vdev;
|
|
struct pci_dev *pdev;
|
|
const struct vfio_pci_device_ops *pci_ops;
|
|
void __iomem *barmap[PCI_STD_NUM_BARS];
|
|
bool bar_mmap_supported[PCI_STD_NUM_BARS];
|
|
u8 *pci_config_map;
|
|
u8 *vconfig;
|
|
struct perm_bits *msi_perm;
|
|
spinlock_t irqlock;
|
|
struct mutex igate;
|
|
struct xarray ctx;
|
|
int irq_type;
|
|
int num_regions;
|
|
struct vfio_pci_region *region;
|
|
u8 msi_qmax;
|
|
u8 msix_bar;
|
|
u16 msix_size;
|
|
u32 msix_offset;
|
|
u32 rbar[7];
|
|
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 sriov_active;
|
|
struct pci_saved_state *pci_saved_state;
|
|
struct pci_saved_state *pm_save;
|
|
int ioeventfds_nr;
|
|
struct vfio_pci_eventfd __rcu *err_trigger;
|
|
struct vfio_pci_eventfd __rcu *req_trigger;
|
|
struct eventfd_ctx *pm_wake_eventfd_ctx;
|
|
struct list_head dummy_resources_list;
|
|
struct mutex ioeventfds_lock;
|
|
struct list_head ioeventfds_list;
|
|
struct vfio_pci_vf_token *vf_token;
|
|
struct list_head sriov_pfs_item;
|
|
struct vfio_pci_core_device *sriov_pf_core_dev;
|
|
struct notifier_block nb;
|
|
struct rw_semaphore memory_lock;
|
|
struct list_head dmabufs;
|
|
};
|
|
|
|
enum vfio_pci_io_width {
|
|
VFIO_PCI_IO_WIDTH_1 = 1,
|
|
VFIO_PCI_IO_WIDTH_2 = 2,
|
|
VFIO_PCI_IO_WIDTH_4 = 4,
|
|
VFIO_PCI_IO_WIDTH_8 = 8,
|
|
};
|
|
|
|
/* Will be exported for vfio pci drivers usage */
|
|
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);
|
|
int vfio_pci_core_register_device(struct vfio_pci_core_device *vdev);
|
|
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);
|
|
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,
|
|
void __user *arg, size_t argsz);
|
|
int vfio_pci_ioctl_get_region_info(struct vfio_device *core_vdev,
|
|
struct vfio_region_info *info,
|
|
struct vfio_info_cap *caps);
|
|
ssize_t vfio_pci_core_read(struct vfio_device *core_vdev, char __user *buf,
|
|
size_t count, loff_t *ppos);
|
|
ssize_t vfio_pci_core_write(struct vfio_device *core_vdev, const char __user *buf,
|
|
size_t count, loff_t *ppos);
|
|
vm_fault_t vfio_pci_vmf_insert_pfn(struct vfio_pci_core_device *vdev,
|
|
struct vm_fault *vmf, unsigned long pfn,
|
|
unsigned int order);
|
|
int vfio_pci_core_mmap(struct vfio_device *core_vdev, struct vm_area_struct *vma);
|
|
void vfio_pci_core_request(struct vfio_device *core_vdev, unsigned int count);
|
|
int vfio_pci_core_match(struct vfio_device *core_vdev, char *buf);
|
|
int vfio_pci_core_match_token_uuid(struct vfio_device *core_vdev,
|
|
const uuid_t *uuid);
|
|
int vfio_pci_core_enable(struct vfio_pci_core_device *vdev);
|
|
void vfio_pci_core_disable(struct vfio_pci_core_device *vdev);
|
|
void vfio_pci_core_finish_enable(struct vfio_pci_core_device *vdev);
|
|
pci_ers_result_t vfio_pci_core_aer_err_detected(struct pci_dev *pdev,
|
|
pci_channel_state_t state);
|
|
ssize_t vfio_pci_core_do_io_rw(struct vfio_pci_core_device *vdev, bool test_mem,
|
|
void __iomem *io, char __user *buf,
|
|
loff_t off, size_t count, size_t x_start,
|
|
size_t x_end, bool iswrite,
|
|
enum vfio_pci_io_width max_width);
|
|
bool __vfio_pci_memory_enabled(struct vfio_pci_core_device *vdev);
|
|
bool vfio_pci_core_range_intersect_range(loff_t buf_start, size_t buf_cnt,
|
|
loff_t reg_start, size_t reg_cnt,
|
|
loff_t *buf_offset,
|
|
size_t *intersect_count,
|
|
size_t *register_offset);
|
|
#define VFIO_IOWRITE_DECLARATION(size) \
|
|
int vfio_pci_core_iowrite##size(struct vfio_pci_core_device *vdev, \
|
|
bool test_mem, u##size val, void __iomem *io);
|
|
|
|
VFIO_IOWRITE_DECLARATION(8)
|
|
VFIO_IOWRITE_DECLARATION(16)
|
|
VFIO_IOWRITE_DECLARATION(32)
|
|
#ifdef iowrite64
|
|
VFIO_IOWRITE_DECLARATION(64)
|
|
#endif
|
|
|
|
#define VFIO_IOREAD_DECLARATION(size) \
|
|
int vfio_pci_core_ioread##size(struct vfio_pci_core_device *vdev, \
|
|
bool test_mem, u##size *val, void __iomem *io);
|
|
|
|
VFIO_IOREAD_DECLARATION(8)
|
|
VFIO_IOREAD_DECLARATION(16)
|
|
VFIO_IOREAD_DECLARATION(32)
|
|
#ifdef ioread64
|
|
VFIO_IOREAD_DECLARATION(64)
|
|
#endif
|
|
|
|
static inline bool is_aligned_for_order(struct vm_area_struct *vma,
|
|
unsigned long addr,
|
|
unsigned long pfn,
|
|
unsigned int order)
|
|
{
|
|
return !(order && (addr < vma->vm_start ||
|
|
addr + (PAGE_SIZE << order) > vma->vm_end ||
|
|
!IS_ALIGNED(pfn, 1 << order)));
|
|
}
|
|
|
|
/*
|
|
* Returns a BAR's iomap base or an ERR_PTR() if, for example, the
|
|
* BAR isn't valid, its resource wasn't acquired, or its iomap
|
|
* failed. This shall only be used after vfio_pci_core_enable()
|
|
* has set up the BAR maps and before vfio_pci_core_disable()
|
|
* tears them down.
|
|
*/
|
|
static inline void __iomem __must_check *
|
|
vfio_pci_core_get_iomap(struct vfio_pci_core_device *vdev, unsigned int bar)
|
|
{
|
|
if (WARN_ON_ONCE(bar >= PCI_STD_NUM_BARS))
|
|
return IOMEM_ERR_PTR(-EINVAL);
|
|
|
|
if (WARN_ON_ONCE(!vdev->barmap[bar]))
|
|
return IOMEM_ERR_PTR(-ENODEV);
|
|
|
|
return vdev->barmap[bar];
|
|
}
|
|
|
|
int vfio_pci_dma_buf_iommufd_map(struct dma_buf_attachment *attachment,
|
|
struct phys_vec *phys);
|
|
|
|
#endif /* VFIO_PCI_CORE_H */
|