]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
PCI/sysfs: Ensure devices are powered for config reads
authorBrian Norris <briannorris@google.com>
Mon, 20 Oct 2025 16:25:18 +0000 (12:25 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 29 Oct 2025 13:01:24 +0000 (14:01 +0100)
[ Upstream commit 48991e4935078b05f80616c75d1ee2ea3ae18e58 ]

The "max_link_width", "current_link_speed", "current_link_width",
"secondary_bus_number", and "subordinate_bus_number" sysfs files all access
config registers, but they don't check the runtime PM state. If the device
is in D3cold or a parent bridge is suspended, we may see -EINVAL, bogus
values, or worse, depending on implementation details.

Wrap these access in pci_config_pm_runtime_{get,put}() like most of the
rest of the similar sysfs attributes.

Notably, "max_link_speed" does not access config registers; it returns a
cached value since d2bd39c0456b ("PCI: Store all PCIe Supported Link
Speeds").

Fixes: 56c1af4606f0 ("PCI: Add sysfs max_link_speed/width, current_link_speed/width, etc")
Signed-off-by: Brian Norris <briannorris@google.com>
Signed-off-by: Brian Norris <briannorris@chromium.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20250924095711.v2.1.Ibb5b6ca1e2c059e04ec53140cd98a44f2684c668@changeid
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/pci/pci-sysfs.c

index 3fddd421bbe66c5f7f8d95e20e326509e0979b37..651887d36368c47d85d79fdee03a4d67b397c5cc 100644 (file)
@@ -174,8 +174,14 @@ static ssize_t max_link_width_show(struct device *dev,
                                   struct device_attribute *attr, char *buf)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
+       ssize_t ret;
 
-       return sysfs_emit(buf, "%u\n", pcie_get_width_cap(pdev));
+       /* We read PCI_EXP_LNKCAP, so we need the device to be accessible. */
+       pci_config_pm_runtime_get(pdev);
+       ret = sysfs_emit(buf, "%u\n", pcie_get_width_cap(pdev));
+       pci_config_pm_runtime_put(pdev);
+
+       return ret;
 }
 static DEVICE_ATTR_RO(max_link_width);
 
@@ -187,7 +193,10 @@ static ssize_t current_link_speed_show(struct device *dev,
        int err;
        enum pci_bus_speed speed;
 
+       pci_config_pm_runtime_get(pci_dev);
        err = pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, &linkstat);
+       pci_config_pm_runtime_put(pci_dev);
+
        if (err)
                return -EINVAL;
 
@@ -204,7 +213,10 @@ static ssize_t current_link_width_show(struct device *dev,
        u16 linkstat;
        int err;
 
+       pci_config_pm_runtime_get(pci_dev);
        err = pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, &linkstat);
+       pci_config_pm_runtime_put(pci_dev);
+
        if (err)
                return -EINVAL;
 
@@ -221,7 +233,10 @@ static ssize_t secondary_bus_number_show(struct device *dev,
        u8 sec_bus;
        int err;
 
+       pci_config_pm_runtime_get(pci_dev);
        err = pci_read_config_byte(pci_dev, PCI_SECONDARY_BUS, &sec_bus);
+       pci_config_pm_runtime_put(pci_dev);
+
        if (err)
                return -EINVAL;
 
@@ -237,7 +252,10 @@ static ssize_t subordinate_bus_number_show(struct device *dev,
        u8 sub_bus;
        int err;
 
+       pci_config_pm_runtime_get(pci_dev);
        err = pci_read_config_byte(pci_dev, PCI_SUBORDINATE_BUS, &sub_bus);
+       pci_config_pm_runtime_put(pci_dev);
+
        if (err)
                return -EINVAL;