__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


Reply via email to