]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
PCI: Add 'reset_subordinate' to reset hierarchy below bridge
authorKeith Busch <kbusch@kernel.org>
Fri, 25 Oct 2024 22:27:54 +0000 (15:27 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 14 Dec 2024 18:48:31 +0000 (19:48 +0100)
[ Upstream commit 2fa046449a82a7d0f6d9721dd83e348816038444 ]

The "bus" and "cxl_bus" reset methods reset a device by asserting Secondary
Bus Reset on the bridge leading to the device.  These only work if the
device is the only device below the bridge.

Add a sysfs 'reset_subordinate' attribute on bridges that can assert
Secondary Bus Reset regardless of how many devices are below the bridge.

This resets all the devices below a bridge in a single command, including
the locking and config space save/restore that reset methods normally do.

This may be the only way to reset devices that don't support other reset
methods (ACPI, FLR, PM reset, etc).

Link: https://lore.kernel.org/r/20241025222755.3756162-1-kbusch@meta.com
Signed-off-by: Keith Busch <kbusch@kernel.org>
[bhelgaas: commit log, add capable(CAP_SYS_ADMIN) check]
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Alex Williamson <alex.williamson@redhat.com>
Reviewed-by: Amey Narkhede <ameynarkhede03@gmail.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Documentation/ABI/testing/sysfs-bus-pci
drivers/pci/pci-sysfs.c
drivers/pci/pci.c
drivers/pci/pci.h

index 77ad9ec3c801966213147ee5c18273f48edda9b0..da33ab66ddfe73399b75e3681093d5c7deb5097a 100644 (file)
@@ -131,6 +131,17 @@ Description:
                will be present in sysfs.  Writing 1 to this file
                will perform reset.
 
+What:          /sys/bus/pci/devices/.../reset_subordinate
+Date:          October 2024
+Contact:       linux-pci@vger.kernel.org
+Description:
+               This is visible only for bridge devices. If you want to reset
+               all devices attached through the subordinate bus of a specific
+               bridge device, writing 1 to this will try to do it.  This will
+               affect all devices attached to the system through this bridge
+               similiar to writing 1 to their individual "reset" file, so use
+               with caution.
+
 What:          /sys/bus/pci/devices/.../vpd
 Date:          February 2008
 Contact:       Ben Hutchings <bwh@kernel.org>
index e14c83f59b48a0e63e6d617f435140b9d5f6640a..d27bc5a5d2f8657938d8274acad210253c1ece14 100644 (file)
@@ -488,6 +488,31 @@ static ssize_t bus_rescan_store(struct device *dev,
 static struct device_attribute dev_attr_bus_rescan = __ATTR(rescan, 0200, NULL,
                                                            bus_rescan_store);
 
+static ssize_t reset_subordinate_store(struct device *dev,
+                               struct device_attribute *attr,
+                               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))
+               return -EPERM;
+
+       if (kstrtoul(buf, 0, &val) < 0)
+               return -EINVAL;
+
+       if (val) {
+               int ret = __pci_reset_bus(bus);
+
+               if (ret)
+                       return ret;
+       }
+
+       return count;
+}
+static DEVICE_ATTR_WO(reset_subordinate);
+
 #if defined(CONFIG_PM) && defined(CONFIG_ACPI)
 static ssize_t d3cold_allowed_store(struct device *dev,
                                    struct device_attribute *attr,
@@ -611,6 +636,7 @@ static struct attribute *pci_dev_attrs[] = {
 static struct attribute *pci_bridge_attrs[] = {
        &dev_attr_subordinate_bus_number.attr,
        &dev_attr_secondary_bus_number.attr,
+       &dev_attr_reset_subordinate.attr,
        NULL,
 };
 
index 800df0f1417d8f3f42b0960d9ca359a101f1a065..1d4585b07de3bbc4297e1281e047f61567dce632 100644 (file)
@@ -5598,7 +5598,7 @@ EXPORT_SYMBOL_GPL(pci_probe_reset_bus);
  *
  * Same as above except return -EAGAIN if the bus cannot be locked
  */
-static int __pci_reset_bus(struct pci_bus *bus)
+int __pci_reset_bus(struct pci_bus *bus)
 {
        int rc;
 
index da40f29036d6567ac1d774fad464847c34302618..c2fd92a9ee1adb70b15ee78cb6adb5d7eced0764 100644 (file)
@@ -42,6 +42,7 @@ int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vmai,
 int pci_probe_reset_function(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);
 
 #define PCI_PM_D2_DELAY         200    /* usec; see PCIe r4.0, sec 5.9.1 */
 #define PCI_PM_D3HOT_WAIT       10     /* msec */