[RFC PATCH 6/8] PCI/IOV: Let sriov_add_vfs() own the failure unwind
From: Pavol Sakac
Date: Fri Sep 11 2026 - 08:39:27 EST
__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 <sakacpav@xxxxxxxxx>
---
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