]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
PCI: Make reset_subordinate hotplug safe
authorKeith Busch <kbusch@kernel.org>
Tue, 17 Feb 2026 16:08:36 +0000 (08:08 -0800)
committerBjorn Helgaas <bhelgaas@google.com>
Mon, 9 Mar 2026 20:40:28 +0000 (15:40 -0500)
Use the slot reset method when resetting the bridge if the bus contains
hot plug slots. This fixes spurious hot plug events that are triggered
by the secondary bus reset that bypasses the slot's detection disabling.

Resetting a bridge's subordinate bus can be done like this:

  # echo 1 > /sys/bus/pci/devices/0000:50:01.0/reset_subordinate

Prior to this patch, an example kernel message may show something like:

  pcieport 0000:50:01.0: pciehp: Slot(40): Link Down

With this change, the pciehp driver ignores the link event during the
reset, so may show this message instead:

  pcieport 0000:50:01.0: pciehp: Slot(40): Link Down/Up ignored

Signed-off-by: Keith Busch <kbusch@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Link: https://patch.msgid.link/20260217160836.2709885-4-kbusch@meta.com
drivers/pci/pci-sysfs.c
drivers/pci/pci.c
drivers/pci/pci.h

index aaf92195da3218b538870dbc59df238e124252c5..a2f8a5d6190fdb019117184802cf947905b1729f 100644 (file)
@@ -553,7 +553,6 @@ static ssize_t reset_subordinate_store(struct device *dev,
                                const char *buf, size_t count)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
-       struct pci_bus *bus = pdev->subordinate;
        unsigned long val;
 
        if (!capable(CAP_SYS_ADMIN))
@@ -563,7 +562,7 @@ static ssize_t reset_subordinate_store(struct device *dev,
                return -EINVAL;
 
        if (val) {
-               int ret = pci_try_reset_bus(bus);
+               int ret = pci_try_reset_bridge(pdev);
 
                if (ret)
                        return ret;
index 4fd61d6cacff920d53f5652c1cdfc06689e8bd51..5984ad9ef6c46eaf93d09dcff69100780f95e838 100644 (file)
@@ -5597,14 +5597,44 @@ static int pci_bus_reset(struct pci_bus *bus, bool probe)
 }
 
 /**
- * pci_bus_error_reset - reset the bridge's subordinate bus
- * @bridge: The parent device that connects to the bus to reset
+ * pci_try_reset_bus - Try to reset a PCI bus
+ * @bus: top level PCI bus to reset
+ *
+ * Same as above except return -EAGAIN if the bus cannot be locked
+ */
+static int pci_try_reset_bus(struct pci_bus *bus)
+{
+       int rc;
+
+       rc = pci_bus_reset(bus, PCI_RESET_PROBE);
+       if (rc)
+               return rc;
+
+       if (pci_bus_trylock(bus)) {
+               pci_bus_save_and_disable_locked(bus);
+               might_sleep();
+               rc = pci_bridge_secondary_bus_reset(bus->self);
+               pci_bus_restore_locked(bus);
+               pci_bus_unlock(bus);
+       } else
+               rc = -EAGAIN;
+
+       return rc;
+}
+
+#define PCI_RESET_RESTORE true
+#define PCI_RESET_NO_RESTORE false
+/**
+ * pci_reset_bridge - reset a bridge's subordinate bus
+ * @bridge: bridge that connects to the bus to reset
+ * @restore: when true use a reset method that invokes pci_dev_restore() post
+ *           reset for affected devices
  *
  * This function will first try to reset the slots on this bus if the method is
  * available. If slot reset fails or is not available, this will fall back to a
  * secondary bus reset.
  */
-int pci_bus_error_reset(struct pci_dev *bridge)
+static int pci_reset_bridge(struct pci_dev *bridge, bool restore)
 {
        struct pci_bus *bus = bridge->subordinate;
        struct pci_slot *slot;
@@ -5620,17 +5650,42 @@ int pci_bus_error_reset(struct pci_dev *bridge)
                if (pci_probe_reset_slot(slot))
                        goto bus_reset;
 
-       list_for_each_entry(slot, &bus->slots, list)
-               if (pci_slot_reset(slot, PCI_RESET_DO_RESET))
+       list_for_each_entry(slot, &bus->slots, list) {
+               int ret;
+
+               if (restore)
+                       ret = pci_try_reset_slot(slot);
+               else
+                       ret = pci_slot_reset(slot, PCI_RESET_DO_RESET);
+
+               if (ret)
                        goto bus_reset;
+       }
 
        mutex_unlock(&pci_slot_mutex);
        return 0;
 bus_reset:
        mutex_unlock(&pci_slot_mutex);
+
+       if (restore)
+               return pci_try_reset_bus(bus);
        return pci_bus_reset(bridge->subordinate, PCI_RESET_DO_RESET);
 }
 
+/**
+ * pci_bus_error_reset - reset the bridge's subordinate bus
+ * @bridge: The parent device that connects to the bus to reset
+ */
+int pci_bus_error_reset(struct pci_dev *bridge)
+{
+       return pci_reset_bridge(bridge, PCI_RESET_NO_RESTORE);
+}
+
+int pci_try_reset_bridge(struct pci_dev *bridge)
+{
+       return pci_reset_bridge(bridge, PCI_RESET_RESTORE);
+}
+
 /**
  * pci_probe_reset_bus - probe whether a PCI bus can be reset
  * @bus: PCI bus to probe
@@ -5643,32 +5698,6 @@ int pci_probe_reset_bus(struct pci_bus *bus)
 }
 EXPORT_SYMBOL_GPL(pci_probe_reset_bus);
 
-/**
- * pci_try_reset_bus - Try to reset a PCI bus
- * @bus: top level PCI bus to reset
- *
- * Same as above except return -EAGAIN if the bus cannot be locked
- */
-int pci_try_reset_bus(struct pci_bus *bus)
-{
-       int rc;
-
-       rc = pci_bus_reset(bus, PCI_RESET_PROBE);
-       if (rc)
-               return rc;
-
-       if (pci_bus_trylock(bus)) {
-               pci_bus_save_and_disable_locked(bus);
-               might_sleep();
-               rc = pci_bridge_secondary_bus_reset(bus->self);
-               pci_bus_restore_locked(bus);
-               pci_bus_unlock(bus);
-       } else
-               rc = -EAGAIN;
-
-       return rc;
-}
-
 /**
  * pci_reset_bus - Try to reset a PCI bus
  * @pdev: top level PCI device to reset via slot/bus
index e319417da5caa6c1c8551fbeeca5505082d11ad9..a1d2ecb5620775e23c9cc037c87aa3d6e8443b99 100644 (file)
@@ -231,7 +231,7 @@ bool pci_reset_supported(struct pci_dev *dev);
 void pci_init_reset_methods(struct pci_dev *dev);
 int pci_bridge_secondary_bus_reset(struct pci_dev *dev);
 int pci_bus_error_reset(struct pci_dev *dev);
-int pci_try_reset_bus(struct pci_bus *bus);
+int pci_try_reset_bridge(struct pci_dev *bridge);
 
 struct pci_cap_saved_data {
        u16             cap_nr;