]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
PCI/PM: Prevent runtime suspend until devices are fully initialized
authorBrian Norris <briannorris@chromium.org>
Thu, 22 Jan 2026 17:48:15 +0000 (09:48 -0800)
committerBjorn Helgaas <bhelgaas@google.com>
Thu, 22 Jan 2026 19:02:55 +0000 (13:02 -0600)
Previously, it was possible for a PCI device to be runtime-suspended before
it was fully initialized. When that happened, the suspend process could
save invalid device state, for example, before BAR assignment. Restoring
the invalid state during resume may leave the device non-functional.

Prevent runtime suspend for PCI devices until they are fully initialized by
deferring pm_runtime_enable().

More details on how exactly this may occur:

  1. PCI device is created by pci_scan_slot() or similar

  2. As part of pci_scan_slot(), pci_pm_init() puts the device in D0 and
     prevents runtime suspend prevented via pm_runtime_forbid()

  3. pci_device_add() adds the underlying 'struct device' via device_add(),
     which means user space can allow runtime suspend, e.g.,

       echo auto > /sys/bus/pci/devices/.../power/control

  4. PCI device receives BAR configuration
     (pci_assign_unassigned_bus_resources(), etc.)

  5. pci_bus_add_device() applies final fixups, saves device state, and
     tries to attach a driver

The device may potentially be suspended between #3 and #5, so this is racy
with user space (udev or similar).

Many PCI devices are enumerated at subsys_initcall time and so will not
race with user space, but devices created later by hotplug or modular
pwrctrl or host controller drivers are susceptible to this race.

More runtime PM details at the first Link: below.

Link: https://lore.kernel.org/all/0e35a4e1-894a-47c1-9528-fc5ffbafd9e2@samsung.com/
Signed-off-by: Brian Norris <briannorris@chromium.org>
[bhelgaas: update comments per https://lore.kernel.org/r/CAJZ5v0iBNOmMtqfqEbrYyuK2u+2J2+zZ-iQd1FvyCPjdvU2TJg@mail.gmail.com]
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20260122094815.v5.1.I60a53c170a8596661883bd2b4ef475155c7aa72b@changeid
drivers/pci/bus.c
drivers/pci/pci.c

index 4383a36fd6ca09bc68f6025a9a79c8f49b34fea1..41e5c45e38b5eca36c874b6dcf1cebcbddab9ce4 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/proc_fs.h>
 #include <linux/slab.h>
 
@@ -379,6 +380,13 @@ void pci_bus_add_device(struct pci_dev *dev)
                put_device(&pdev->dev);
        }
 
+       /*
+        * Enable runtime PM, which potentially allows the device to
+        * suspend immediately, only after the PCI state has been
+        * configured completely.
+        */
+       pm_runtime_enable(&dev->dev);
+
        if (!dn || of_device_is_available(dn))
                pci_dev_allow_binding(dev);
 
index 86ccbd0efb4954362b369745fc287c3a0845fed6..d5f4b52899adabf424ff8cc4ca2a66cb4cf00a1c 100644 (file)
@@ -3199,8 +3199,14 @@ void pci_pm_init(struct pci_dev *dev)
 poweron:
        pci_pm_power_up_and_verify_state(dev);
        pm_runtime_forbid(&dev->dev);
+
+       /*
+        * Runtime PM will be enabled for the device when it has been fully
+        * configured, but since its parent and suppliers may suspend in
+        * the meantime, prevent them from doing so by changing the
+        * device's runtime PM status to "active".
+        */
        pm_runtime_set_active(&dev->dev);
-       pm_runtime_enable(&dev->dev);
 }
 
 static unsigned long pci_ea_flags(struct pci_dev *dev, u8 prop)