[PATCH 1/2] PM: Use hibernation callbacks for system power off
From: Mario Limonciello
Date: Mon Nov 02 2026 - 09:01:04 EST
During system power-off, device_shutdown() invokes the PCI bus shutdown
callback for each PCI device. pci_device_shutdown() first calls
pm_runtime_resume(), which may return a runtime-suspended device to D0
before invoking its driver's ->shutdown() callback. Consequently, devices
may remain in D0, consuming power or causing spurious wakeups.
Devices should instead remain in a low-power state until power is removed.
ACPI 6.5 section 7.4.2.5 states that all devices are in D3 in S4. Section
7.4.2.6 defines S5 as similar to S4 except that OSPM saves no context, so
reusing the device hibernation power-off callbacks for S5 is appropriate.
For SYSTEM_POWER_OFF, run the device PM core's suspend sequence with
PMSG_POWEROFF. When CONFIG_HIBERNATE_CALLBACKS is enabled, this invokes the
hibernation ->poweroff*() callbacks. Skip device_shutdown() after a
successful sequence so the PCI shutdown callback does not resume devices to
D0 and undo the transition. If callback support is unavailable or either
phase fails, fall back to device_shutdown() so power-off can continue.
Tested-by: Eric Naim <dnaim@xxxxxxxxxxx>
Tested-by: Denis Benato <benato.denis96@xxxxxxxxx>
Tested-by: Bartu Alev <bartualev@xxxxxxxxx>
Tested-by: Merthan Karakaş <m3rthn.k@xxxxxxxxx>
Link: https://lore.kernel.org/linux-pci/20231213182656.6165-1-mario.limonciello@xxxxxxx/
Link: https://lore.kernel.org/linux-pci/20250506041934.1409302-1-superm1@xxxxxxxxxx/
Signed-off-by: Mario Limonciello <superm1@xxxxxxxxxx>
---
kernel/reboot.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/kernel/reboot.c b/kernel/reboot.c
index 9a84401adde5a..1ad8629fde4d9 100644
--- a/kernel/reboot.c
+++ b/kernel/reboot.c
@@ -14,6 +14,7 @@
#include <linux/kexec.h>
#include <linux/kmsg_dump.h>
#include <linux/rcupdate.h>
+#include <linux/pm.h>
#include <linux/reboot.h>
#include <linux/sched/signal.h>
#include <linux/suspend.h>
@@ -315,6 +316,13 @@ static void kernel_shutdown_prepare(enum system_states state)
(state == SYSTEM_HALT) ? SYS_HALT : SYS_POWER_OFF, NULL);
system_state = state;
usermodehelper_disable();
+#ifdef CONFIG_HIBERNATE_CALLBACKS
+ if (state == SYSTEM_POWER_OFF) {
+ if (!dpm_suspend_start(PMSG_POWEROFF) && !dpm_suspend_end(PMSG_POWEROFF))
+ return;
+ pr_emerg("Failed to power off devices, using shutdown instead.\n");
+ }
+#endif
device_shutdown();
}
/**
--
2.53.0