From 84226677e04154000d13bb2792c4837b691ccb34 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 17 Feb 2026 08:08:34 -0800 Subject: [PATCH 1/5] PCI: Rename __pci_bus_reset() and __pci_slot_reset() Make the code a little easier to navigate with more descriptive function names. The two renamed functions here "try" to do to a reset, so make that clear in the name to distinguish them from other similarly named functions: __pci_reset_bus() -> pci_try_reset_bus() __pci_reset_slot() -> pci_try_reset_slot() Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas Reviewed-by: Dan Williams Link: https://patch.msgid.link/20260217160836.2709885-2-kbusch@meta.com --- drivers/pci/pci-sysfs.c | 2 +- drivers/pci/pci.c | 10 +++++----- drivers/pci/pci.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 16eaaf749ba9..aaf92195da32 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -563,7 +563,7 @@ static ssize_t reset_subordinate_store(struct device *dev, return -EINVAL; if (val) { - int ret = __pci_reset_bus(bus); + int ret = pci_try_reset_bus(bus); if (ret) return ret; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8479c2e1f74f..4fd61d6cacff 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5541,7 +5541,7 @@ int pci_probe_reset_slot(struct pci_slot *slot) EXPORT_SYMBOL_GPL(pci_probe_reset_slot); /** - * __pci_reset_slot - Try to reset a PCI slot + * pci_try_reset_slot - Try to reset a PCI slot * @slot: PCI slot to reset * * A PCI bus may host multiple slots, each slot may support a reset mechanism @@ -5555,7 +5555,7 @@ EXPORT_SYMBOL_GPL(pci_probe_reset_slot); * * Same as above except return -EAGAIN if the slot cannot be locked */ -static int __pci_reset_slot(struct pci_slot *slot) +static int pci_try_reset_slot(struct pci_slot *slot) { int rc; @@ -5644,12 +5644,12 @@ int pci_probe_reset_bus(struct pci_bus *bus) EXPORT_SYMBOL_GPL(pci_probe_reset_bus); /** - * __pci_reset_bus - Try to reset a PCI 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_reset_bus(struct pci_bus *bus) +int pci_try_reset_bus(struct pci_bus *bus) { int rc; @@ -5678,7 +5678,7 @@ int __pci_reset_bus(struct pci_bus *bus) int pci_reset_bus(struct pci_dev *pdev) { return (!pci_probe_reset_slot(pdev->slot)) ? - __pci_reset_slot(pdev->slot) : __pci_reset_bus(pdev->bus); + pci_try_reset_slot(pdev->slot) : pci_try_reset_bus(pdev->bus); } EXPORT_SYMBOL_GPL(pci_reset_bus); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 13d998fbacce..e319417da5ca 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -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_reset_bus(struct pci_bus *bus); +int pci_try_reset_bus(struct pci_bus *bus); struct pci_cap_saved_data { u16 cap_nr; From 102c8b26b54e363f85c4c86099ca049a0a76bb58 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 17 Feb 2026 08:08:35 -0800 Subject: [PATCH 2/5] PCI: Allow all bus devices to use the same slot A PCIe hotplug slot applies to the entire secondary bus. Thus, pciehp only allocates a single hotplug_slot for the bridge to that bus. The existing PCI slot, though, would only match to functions on device 0, meaning any devices beyond that, e.g., ARI functions, are not matched to any slot even though they share it. A slot reset will break all the missing devices because the handling skips them. For example, ARI devices with more than 8 functions fail because their state is not properly handled, nor is the attached driver notified of the reset. In the best case, the device will appear unresponsive to the driver, resulting in unexpected errors. A worse possibility may panic the kernel if in-flight transactions trigger hardware reported errors like this real observation: vfio-pci 0000:01:00.0: resetting vfio-pci 0000:01:00.0: reset done {1}[Hardware Error]: Error 1, type: fatal {1}[Hardware Error]: section_type: PCIe error {1}[Hardware Error]: port_type: 0, PCIe end point {1}[Hardware Error]: version: 0.2 {1}[Hardware Error]: command: 0x0140, status: 0x0010 {1}[Hardware Error]: device_id: 0000:01:01.0 {1}[Hardware Error]: slot: 0 {1}[Hardware Error]: secondary_bus: 0x00 {1}[Hardware Error]: vendor_id: 0x1d9b, device_id: 0x0207 {1}[Hardware Error]: class_code: 020000 {1}[Hardware Error]: bridge: secondary_status: 0x0000, control: 0x0000 {1}[Hardware Error]: aer_cor_status: 0x00008000, aer_cor_mask: 0x00002000 {1}[Hardware Error]: aer_uncor_status: 0x00010000, aer_uncor_mask: 0x00100000 {1}[Hardware Error]: aer_uncor_severity: 0x006f6030 {1}[Hardware Error]: TLP Header: 0a412800 00192080 60000004 00000004 GHES: Fatal hardware error but panic disabled Kernel panic - not syncing: GHES: Fatal hardware error Allow a slot to be created to claim all devices on a bus, not just a matching device. This is done by introducing a sentinel value, named PCI_SLOT_ALL_DEVICES, which then has the PCI slot match to any device on the bus. This fixes slot resets for pciehp. Since 0xff already has special meaning, the chosen value for this new feature is 0xfe. This will not clash with any actual slot number since they are limited to 5 bits. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas Reviewed-by: Dan Williams Link: https://patch.msgid.link/20260217160836.2709885-3-kbusch@meta.com --- drivers/pci/hotplug/pciehp_core.c | 3 ++- drivers/pci/slot.c | 31 +++++++++++++++++++++++++++---- include/linux/pci.h | 10 +++++++++- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 1e9158d7bac7..2cafd3b26f34 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -79,7 +79,8 @@ static int init_slot(struct controller *ctrl) snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl)); retval = pci_hp_initialize(&ctrl->hotplug_slot, - ctrl->pcie->port->subordinate, 0, name); + ctrl->pcie->port->subordinate, + PCI_SLOT_ALL_DEVICES, name); if (retval) { ctrl_err(ctrl, "pci_hp_initialize failed: error %d\n", retval); kfree(ops); diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index 787311614e5b..e0b7fb43423c 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -42,6 +42,15 @@ static ssize_t address_read_file(struct pci_slot *slot, char *buf) pci_domain_nr(slot->bus), slot->bus->number); + /* + * Preserve legacy ABI expectations that hotplug drivers that manage + * multiple devices per slot emit 0 for the device number. + */ + if (slot->number == PCI_SLOT_ALL_DEVICES) + return sysfs_emit(buf, "%04x:%02x:00\n", + pci_domain_nr(slot->bus), + slot->bus->number); + return sysfs_emit(buf, "%04x:%02x:%02x\n", pci_domain_nr(slot->bus), slot->bus->number, @@ -73,7 +82,8 @@ static void pci_slot_release(struct kobject *kobj) down_read(&pci_bus_sem); list_for_each_entry(dev, &slot->bus->devices, bus_list) - if (PCI_SLOT(dev->devfn) == slot->number) + if (slot->number == PCI_SLOT_ALL_DEVICES || + PCI_SLOT(dev->devfn) == slot->number) dev->slot = NULL; up_read(&pci_bus_sem); @@ -166,7 +176,8 @@ void pci_dev_assign_slot(struct pci_dev *dev) mutex_lock(&pci_slot_mutex); list_for_each_entry(slot, &dev->bus->slots, list) - if (PCI_SLOT(dev->devfn) == slot->number) + if (slot->number == PCI_SLOT_ALL_DEVICES || + PCI_SLOT(dev->devfn) == slot->number) dev->slot = slot; mutex_unlock(&pci_slot_mutex); } @@ -188,7 +199,8 @@ static struct pci_slot *get_slot(struct pci_bus *parent, int slot_nr) /** * pci_create_slot - create or increment refcount for physical PCI slot * @parent: struct pci_bus of parent bridge - * @slot_nr: PCI_SLOT(pci_dev->devfn) or -1 for placeholder + * @slot_nr: PCI_SLOT(pci_dev->devfn), -1 for placeholder, or + * PCI_SLOT_ALL_DEVICES * @name: user visible string presented in /sys/bus/pci/slots/ * @hotplug: set if caller is hotplug driver, NULL otherwise * @@ -222,6 +234,16 @@ static struct pci_slot *get_slot(struct pci_bus *parent, int slot_nr) * consist solely of a dddd:bb tuple, where dddd is the PCI domain of the * %struct pci_bus and bb is the bus number. In other words, the devfn of * the 'placeholder' slot will not be displayed. + * + * Bus-wide slots: + * For PCIe hotplug, the physical slot encompasses the entire secondary + * bus, not just a single device number. If the device supports ARI and ARI + * Forwarding is enabled in the upstream bridge, a multi-function device + * may include functions that appear to have several different device + * numbers, i.e., PCI_SLOT() values. Pass @slot_nr == PCI_SLOT_ALL_DEVICES + * to create a slot that matches all devices on the bus. Unlike placeholder + * slots, bus-wide slots go through normal slot lookup and reuse existing + * slots if present. */ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, const char *name, @@ -285,7 +307,8 @@ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, down_read(&pci_bus_sem); list_for_each_entry(dev, &parent->devices, bus_list) - if (PCI_SLOT(dev->devfn) == slot_nr) + if (slot_nr == PCI_SLOT_ALL_DEVICES || + PCI_SLOT(dev->devfn) == slot_nr) dev->slot = slot; up_read(&pci_bus_sem); diff --git a/include/linux/pci.h b/include/linux/pci.h index 1c270f1d5123..5ae2dfdb2d6f 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -72,12 +72,20 @@ /* return bus from PCI devid = ((u16)bus_number) << 8) | devfn */ #define PCI_BUS_NUM(x) (((x) >> 8) & 0xff) +/* + * PCI_SLOT_ALL_DEVICES indicates a slot that covers all devices on the bus. + * Used for PCIe hotplug where the physical slot is the entire secondary bus, + * and, if ARI Forwarding is enabled, functions may appear to be on multiple + * devices. + */ +#define PCI_SLOT_ALL_DEVICES 0xfe + /* pci_slot represents a physical slot */ struct pci_slot { struct pci_bus *bus; /* Bus this slot is on */ struct list_head list; /* Node in list of slots */ struct hotplug_slot *hotplug; /* Hotplug info (move here) */ - unsigned char number; /* PCI_SLOT(pci_dev->devfn) */ + unsigned char number; /* Device nr, or PCI_SLOT_ALL_DEVICES */ struct kobject kobj; }; From 8238cb69c01fe4dbb4e3be277fff3ed680ac0108 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 17 Feb 2026 08:08:36 -0800 Subject: [PATCH 3/5] PCI: Make reset_subordinate hotplug safe 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 Signed-off-by: Bjorn Helgaas Reviewed-by: Dan Williams Link: https://patch.msgid.link/20260217160836.2709885-4-kbusch@meta.com --- drivers/pci/pci-sysfs.c | 3 +- drivers/pci/pci.c | 125 +++++++++++++++++++++++++--------------- drivers/pci/pci.h | 2 +- 3 files changed, 79 insertions(+), 51 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index aaf92195da32..a2f8a5d6190f 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -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; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 4fd61d6cacff..5984ad9ef6c4 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5596,60 +5596,13 @@ static int pci_bus_reset(struct pci_bus *bus, bool probe) return ret; } -/** - * pci_bus_error_reset - reset the bridge's subordinate bus - * @bridge: The parent device that connects to the bus to reset - * - * 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) -{ - struct pci_bus *bus = bridge->subordinate; - struct pci_slot *slot; - - if (!bus) - return -ENOTTY; - - mutex_lock(&pci_slot_mutex); - if (list_empty(&bus->slots)) - goto bus_reset; - - list_for_each_entry(slot, &bus->slots, list) - 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)) - goto bus_reset; - - mutex_unlock(&pci_slot_mutex); - return 0; -bus_reset: - mutex_unlock(&pci_slot_mutex); - return pci_bus_reset(bridge->subordinate, PCI_RESET_DO_RESET); -} - -/** - * pci_probe_reset_bus - probe whether a PCI bus can be reset - * @bus: PCI bus to probe - * - * Return 0 if bus can be reset, negative if a bus reset is not supported. - */ -int pci_probe_reset_bus(struct pci_bus *bus) -{ - return pci_bus_reset(bus, PCI_RESET_PROBE); -} -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) +static int pci_try_reset_bus(struct pci_bus *bus) { int rc; @@ -5669,6 +5622,82 @@ int pci_try_reset_bus(struct pci_bus *bus) 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. + */ +static int pci_reset_bridge(struct pci_dev *bridge, bool restore) +{ + struct pci_bus *bus = bridge->subordinate; + struct pci_slot *slot; + + if (!bus) + return -ENOTTY; + + mutex_lock(&pci_slot_mutex); + if (list_empty(&bus->slots)) + goto bus_reset; + + list_for_each_entry(slot, &bus->slots, list) + if (pci_probe_reset_slot(slot)) + goto bus_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 + * + * Return 0 if bus can be reset, negative if a bus reset is not supported. + */ +int pci_probe_reset_bus(struct pci_bus *bus) +{ + return pci_bus_reset(bus, PCI_RESET_PROBE); +} +EXPORT_SYMBOL_GPL(pci_probe_reset_bus); + /** * pci_reset_bus - Try to reset a PCI bus * @pdev: top level PCI device to reset via slot/bus diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index e319417da5ca..a1d2ecb56207 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -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; From 7b193af58b7f65715dc19e235e03e447a454b377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Wed, 4 Mar 2026 14:21:38 +0200 Subject: [PATCH 4/5] PCI: Consolidate pci_bus/slot_lock/unlock/trylock() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pci_bus/slot_lock/unlock/trylock() largely duplicate the bus iteration loop with variation only due to slot filter handling. The only differences in the loops is where the struct bus is found (directly in the argument vs in slot->bus) and whether slot filter is applied. Those differences are simple to handle using function parameters. Consolidate the bus iteration loop to one place by creating __pci_bus_{lock,unlock,trylock}() and call them from the non-underscore locking functions. Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260304122139.1479-1-ilpo.jarvinen@linux.intel.com --- drivers/pci/pci.c | 119 ++++++++++++++++++++-------------------------- 1 file changed, 51 insertions(+), 68 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 5984ad9ef6c4..0090b4034ec6 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5292,13 +5292,21 @@ static bool pci_bus_resettable(struct pci_bus *bus) return true; } -/* Lock devices from the top of the tree down */ -static void pci_bus_lock(struct pci_bus *bus) -{ - struct pci_dev *dev; +static void pci_bus_lock(struct pci_bus *bus); +static void pci_bus_unlock(struct pci_bus *bus); +static int pci_bus_trylock(struct pci_bus *bus); + +/* Lock devices from the top of the tree down */ +static void __pci_bus_lock(struct pci_bus *bus, struct pci_slot *slot) +{ + struct pci_dev *dev, *bridge = bus->self; + + if (bridge) + pci_dev_lock(bridge); - pci_dev_lock(bus->self); list_for_each_entry(dev, &bus->devices, bus_list) { + if (slot && (!dev->slot || dev->slot != slot)) + continue; if (dev->subordinate) pci_bus_lock(dev->subordinate); else @@ -5307,28 +5315,34 @@ static void pci_bus_lock(struct pci_bus *bus) } /* Unlock devices from the bottom of the tree up */ -static void pci_bus_unlock(struct pci_bus *bus) +static void __pci_bus_unlock(struct pci_bus *bus, struct pci_slot *slot) { - struct pci_dev *dev; + struct pci_dev *dev, *bridge = bus->self; list_for_each_entry(dev, &bus->devices, bus_list) { + if (slot && (!dev->slot || dev->slot != slot)) + continue; if (dev->subordinate) pci_bus_unlock(dev->subordinate); else pci_dev_unlock(dev); } - pci_dev_unlock(bus->self); + + if (bridge) + pci_dev_unlock(bridge); } /* Return 1 on successful lock, 0 on contention */ -static int pci_bus_trylock(struct pci_bus *bus) +static int __pci_bus_trylock(struct pci_bus *bus, struct pci_slot *slot) { - struct pci_dev *dev; + struct pci_dev *dev, *bridge = bus->self; - if (!pci_dev_trylock(bus->self)) + if (bridge && !pci_dev_trylock(bridge)) return 0; list_for_each_entry(dev, &bus->devices, bus_list) { + if (slot && (!dev->slot || dev->slot != slot)) + continue; if (dev->subordinate) { if (!pci_bus_trylock(dev->subordinate)) goto unlock; @@ -5339,15 +5353,37 @@ static int pci_bus_trylock(struct pci_bus *bus) unlock: list_for_each_entry_continue_reverse(dev, &bus->devices, bus_list) { + if (slot && (!dev->slot || dev->slot != slot)) + continue; if (dev->subordinate) pci_bus_unlock(dev->subordinate); else pci_dev_unlock(dev); } - pci_dev_unlock(bus->self); + + if (bridge) + pci_dev_unlock(bridge); return 0; } +/* Lock devices from the top of the tree down */ +static void pci_bus_lock(struct pci_bus *bus) +{ + __pci_bus_lock(bus, NULL); +} + +/* Unlock devices from the bottom of the tree up */ +static void pci_bus_unlock(struct pci_bus *bus) +{ + __pci_bus_unlock(bus, NULL); +} + +/* Return 1 on successful lock, 0 on contention */ +static int pci_bus_trylock(struct pci_bus *bus) +{ + return __pci_bus_trylock(bus, NULL); +} + /* Do any devices on or below this slot prevent a bus reset? */ static bool pci_slot_resettable(struct pci_slot *slot) { @@ -5370,72 +5406,19 @@ static bool pci_slot_resettable(struct pci_slot *slot) /* Lock devices from the top of the tree down */ static void pci_slot_lock(struct pci_slot *slot) { - struct pci_dev *dev, *bridge = slot->bus->self; - - if (bridge) - pci_dev_lock(bridge); - - list_for_each_entry(dev, &slot->bus->devices, bus_list) { - if (!dev->slot || dev->slot != slot) - continue; - if (dev->subordinate) - pci_bus_lock(dev->subordinate); - else - pci_dev_lock(dev); - } + __pci_bus_lock(slot->bus, slot); } /* Unlock devices from the bottom of the tree up */ static void pci_slot_unlock(struct pci_slot *slot) { - struct pci_dev *dev, *bridge = slot->bus->self; - - list_for_each_entry(dev, &slot->bus->devices, bus_list) { - if (!dev->slot || dev->slot != slot) - continue; - if (dev->subordinate) - pci_bus_unlock(dev->subordinate); - else - pci_dev_unlock(dev); - } - - if (bridge) - pci_dev_unlock(bridge); + __pci_bus_unlock(slot->bus, slot); } /* Return 1 on successful lock, 0 on contention */ static int pci_slot_trylock(struct pci_slot *slot) { - struct pci_dev *dev, *bridge = slot->bus->self; - - if (bridge && !pci_dev_trylock(bridge)) - return 0; - - list_for_each_entry(dev, &slot->bus->devices, bus_list) { - if (!dev->slot || dev->slot != slot) - continue; - if (dev->subordinate) { - if (!pci_bus_trylock(dev->subordinate)) - goto unlock; - } else if (!pci_dev_trylock(dev)) - goto unlock; - } - return 1; - -unlock: - list_for_each_entry_continue_reverse(dev, - &slot->bus->devices, bus_list) { - if (!dev->slot || dev->slot != slot) - continue; - if (dev->subordinate) - pci_bus_unlock(dev->subordinate); - else - pci_dev_unlock(dev); - } - - if (bridge) - pci_dev_unlock(bridge); - return 0; + return __pci_bus_trylock(slot->bus, slot); } /* From 702c1d56c7177a0481abd2814bab9495f1150967 Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Wed, 25 Feb 2026 19:08:01 +0530 Subject: [PATCH 5/5] PCI/CXL: Hide SBR from reset_methods if masked by CXL Per CXL r3.1, sec 8.1.5.2, the Secondary Bus Reset (SBR) bit in the Bridge Control register of a CXL port has no effect unless the "Unmask SBR" bit in the Port Control Extensions Register is set. After b1956e2d0713 ("PCI/CXL: Fail bus reset if upstream CXL Port has SBR masked"), Linux checks the "Unmask SBR" bit in pci_reset_bus_function(). But when probe==true, it previously returned 0, incorrectly indicating that SBR is a viable reset method for the device. As a result, "bus" is listed in the device's "reset_method" attribute even though the hardware is incapable of performing it. If a user writes "bus" to "reset_method" or triggers a reset that falls back to SBR, the operation fails with "write error: Inappropriate ioctl for device". If the link is operating in CXL mode (pcie_is_cxl()), return -ENOTTY immediately unless "Unmask SBR" is set, regardless of the probe argument. This ensures that "bus" is not advertised in "reset_methods" when the hardware prevents it, improving clarity for users and aligning the sysfs capability report with actual hardware behavior. Signed-off-by: Vidya Sagar [bhelgaas: commit log, use pcie_is_cxl()] Signed-off-by: Bjorn Helgaas Reviewed-by: Jonathan Cameron Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260225133801.30231-1-vidyas@nvidia.com --- drivers/pci/pci.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 0090b4034ec6..3536c5ab8118 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4914,12 +4914,8 @@ static int pci_reset_bus_function(struct pci_dev *dev, bool probe) * If "dev" is below a CXL port that has SBR control masked, SBR * won't do anything, so return error. */ - if (bridge && cxl_sbr_masked(bridge)) { - if (probe) - return 0; - + if (bridge && pcie_is_cxl(bridge) && cxl_sbr_masked(bridge)) return -ENOTTY; - } rc = pci_dev_reset_iommu_prepare(dev); if (rc) {