__pci_iov_add_virtfn() unwinds its own sysfs-link failure with pci_stop_and_remove_bus_device(), which lockdep-asserts pci_rescan_remove_lock. The next commit runs __pci_iov_add_virtfn() from async workers that must never take or require that lock, so the unwind has to move to the enabling task.
Leave __pci_iov_add_virtfn() reporting only and let each caller unwind through pci_iov_remove_virtfn(), whose lookup-based design is correct at every failure stage. sriov_add_vfs() unwinds ids 0..i inclusive on failure of VF i, since VF i may be registered but not yet linked. The wrapper unwinds fully before returning, because its EEH caller discards the return code: the VF is removed through pci_iov_remove_virtfn(), which also frees the bus it empties, and a bus this call created with no VF registered on it is removed explicitly. Assisted-by: LLM Signed-off-by: Pavol Sakac <[email protected]> --- drivers/pci/iov.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index dda9303516f5..a32b2c295922 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -383,35 +383,42 @@ static int __pci_iov_add_virtfn(struct pci_dev *dev, struct pci_bus *bus, pci_device_add(virtfn, virtfn->bus); rc = pci_iov_sysfs_link(dev, virtfn, id); if (rc) - goto failed1; + return rc; pci_bus_add_device(virtfn); return 0; - -failed1: - pci_stop_and_remove_bus_device(virtfn); - pci_dev_put(dev); - - return rc; } int pci_iov_add_virtfn(struct pci_dev *dev, int id) { struct pci_bus *bus; + bool created; int rc; - bus = virtfn_add_bus(dev->bus, pci_iov_virtfn_bus(dev, id), NULL); + bus = virtfn_add_bus(dev->bus, pci_iov_virtfn_bus(dev, id), &created); if (!bus) return -ENOMEM; rc = __pci_iov_add_virtfn(dev, bus, id); - if (rc) - virtfn_remove_bus(dev->bus, bus); + if (rc) { + pci_iov_remove_virtfn(dev, id); + /* + * Same ownership and stale-pointer rules as the + * sriov_add_vfs() bus unwind. + */ + if (created) { + bus = pci_find_bus(pci_domain_nr(dev->bus), + pci_iov_virtfn_bus(dev, id)); + if (bus) + virtfn_remove_bus(dev->bus, bus); + } + } return rc; } +/* Unwind primitive for partial adds: a missing VF must stay a silent no-op. */ void pci_iov_remove_virtfn(struct pci_dev *dev, int id) { char buf[VIRTFN_ID_LEN]; @@ -681,8 +688,10 @@ static int sriov_add_vfs(struct pci_dev *dev, u16 num_vfs) kvfree(buses); return 0; failed: - while (i--) + /* VF i may be partially added: unwind ids 0..i inclusive. */ + do { pci_iov_remove_virtfn(dev, i); + } while (i--); remove_buses: /* -- 2.47.3
