Loading Documentation/ABI/testing/sysfs-power +16 −0 Original line number Diff line number Diff line Loading @@ -454,3 +454,19 @@ Description: disables it. Reads from the file return the current value. The default is "1" if the build-time "SUSPEND_SKIP_SYNC" config flag is unset, or "0" otherwise. What: /sys/power/hibernate_compression_threads Date: October 2025 Contact: <luoxueqin@kylinos.cn> Description: Controls the number of threads used for compression and decompression of hibernation images. The value can be adjusted at runtime to balance performance and CPU utilization. The change takes effect on the next hibernation or resume operation. Minimum value: 1 Default value: 3 Documentation/admin-guide/kernel-parameters.txt +10 −0 Original line number Diff line number Diff line Loading @@ -1907,6 +1907,16 @@ /sys/power/pm_test). Only available when CONFIG_PM_DEBUG is set. Default value is 5. hibernate_compression_threads= [HIBERNATION] Set the number of threads used for compressing or decompressing hibernation images. Format: <integer> Default: 3 Minimum: 1 Example: hibernate_compression_threads=4 highmem=nn[KMG] [KNL,BOOT,EARLY] forces the highmem zone to have an exact size of <nn>. This works even on boxes that have no highmem otherwise. This also works to reduce highmem Loading Documentation/power/index.rst +1 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ Power Management power_supply_class runtime_pm s2ram shutdown-debugging suspend-and-cpuhotplug suspend-and-interrupts swsusp-and-swap-files Loading Documentation/power/shutdown-debugging.rst 0 → 100644 +53 −0 Original line number Diff line number Diff line .. SPDX-License-Identifier: GPL-2.0 Debugging Kernel Shutdown Hangs with pstore +++++++++++++++++++++++++++++++++++++++++++ Overview ======== If the system hangs while shutting down, the kernel logs may need to be retrieved to debug the issue. On systems that have a UART available, it is best to configure the kernel to use this UART for kernel console output. If a UART isn't available, the ``pstore`` subsystem provides a mechanism to persist this data across a system reset, allowing it to be retrieved on the next boot. Kernel Configuration ==================== To enable ``pstore`` and enable saving kernel ring buffer logs, set the following kernel configuration options: * ``CONFIG_PSTORE=y`` * ``CONFIG_PSTORE_CONSOLE=y`` Additionally, enable a backend to store the data. Depending upon your platform some potential options include: * ``CONFIG_EFI_VARS_PSTORE=y`` * ``CONFIG_PSTORE_RAM=y`` * ``CONFIG_CHROMEOS_PSTORE=y`` * ``CONFIG_PSTORE_BLK=y`` Kernel Command-line Parameters ============================== Add these parameters to your kernel command line: * ``printk.always_kmsg_dump=Y`` * Forces the kernel to dump the entire message buffer to pstore during shutdown * ``efi_pstore.pstore_disable=N`` * For EFI-based systems, ensures the EFI backend is active Userspace Interaction and Log Retrieval ======================================= On the next boot after a hang, pstore logs will be available in the pstore filesystem (``/sys/fs/pstore``) and can be retrieved by userspace. On systemd systems, the ``systemd-pstore`` service will help do the following: #. Locate pstore data in ``/sys/fs/pstore`` #. Read and save it to ``/var/lib/systemd/pstore`` #. Clear pstore data for the next event drivers/base/power/generic_ops.c +25 −60 Original line number Diff line number Diff line Loading @@ -8,6 +8,13 @@ #include <linux/pm_runtime.h> #include <linux/export.h> #define CALL_PM_OP(dev, op) \ ({ \ struct device *_dev = (dev); \ const struct dev_pm_ops *pm = _dev->driver ? _dev->driver->pm : NULL; \ pm && pm->op ? pm->op(_dev) : 0; \ }) #ifdef CONFIG_PM /** * pm_generic_runtime_suspend - Generic runtime suspend callback for subsystems. Loading @@ -19,12 +26,7 @@ */ int pm_generic_runtime_suspend(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int ret; ret = pm && pm->runtime_suspend ? pm->runtime_suspend(dev) : 0; return ret; return CALL_PM_OP(dev, runtime_suspend); } EXPORT_SYMBOL_GPL(pm_generic_runtime_suspend); Loading @@ -38,12 +40,7 @@ EXPORT_SYMBOL_GPL(pm_generic_runtime_suspend); */ int pm_generic_runtime_resume(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int ret; ret = pm && pm->runtime_resume ? pm->runtime_resume(dev) : 0; return ret; return CALL_PM_OP(dev, runtime_resume); } EXPORT_SYMBOL_GPL(pm_generic_runtime_resume); #endif /* CONFIG_PM */ Loading Loading @@ -72,9 +69,7 @@ int pm_generic_prepare(struct device *dev) */ int pm_generic_suspend_noirq(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->suspend_noirq ? pm->suspend_noirq(dev) : 0; return CALL_PM_OP(dev, suspend_noirq); } EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq); Loading @@ -84,9 +79,7 @@ EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq); */ int pm_generic_suspend_late(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->suspend_late ? pm->suspend_late(dev) : 0; return CALL_PM_OP(dev, suspend_late); } EXPORT_SYMBOL_GPL(pm_generic_suspend_late); Loading @@ -96,9 +89,7 @@ EXPORT_SYMBOL_GPL(pm_generic_suspend_late); */ int pm_generic_suspend(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->suspend ? pm->suspend(dev) : 0; return CALL_PM_OP(dev, suspend); } EXPORT_SYMBOL_GPL(pm_generic_suspend); Loading @@ -108,9 +99,7 @@ EXPORT_SYMBOL_GPL(pm_generic_suspend); */ int pm_generic_freeze_noirq(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->freeze_noirq ? pm->freeze_noirq(dev) : 0; return CALL_PM_OP(dev, freeze_noirq); } EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq); Loading @@ -120,9 +109,7 @@ EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq); */ int pm_generic_freeze(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->freeze ? pm->freeze(dev) : 0; return CALL_PM_OP(dev, freeze); } EXPORT_SYMBOL_GPL(pm_generic_freeze); Loading @@ -132,9 +119,7 @@ EXPORT_SYMBOL_GPL(pm_generic_freeze); */ int pm_generic_poweroff_noirq(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->poweroff_noirq ? pm->poweroff_noirq(dev) : 0; return CALL_PM_OP(dev, poweroff_noirq); } EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq); Loading @@ -144,9 +129,7 @@ EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq); */ int pm_generic_poweroff_late(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->poweroff_late ? pm->poweroff_late(dev) : 0; return CALL_PM_OP(dev, poweroff_late); } EXPORT_SYMBOL_GPL(pm_generic_poweroff_late); Loading @@ -156,9 +139,7 @@ EXPORT_SYMBOL_GPL(pm_generic_poweroff_late); */ int pm_generic_poweroff(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->poweroff ? pm->poweroff(dev) : 0; return CALL_PM_OP(dev, poweroff); } EXPORT_SYMBOL_GPL(pm_generic_poweroff); Loading @@ -168,9 +149,7 @@ EXPORT_SYMBOL_GPL(pm_generic_poweroff); */ int pm_generic_thaw_noirq(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->thaw_noirq ? pm->thaw_noirq(dev) : 0; return CALL_PM_OP(dev, thaw_noirq); } EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq); Loading @@ -180,9 +159,7 @@ EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq); */ int pm_generic_thaw(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->thaw ? pm->thaw(dev) : 0; return CALL_PM_OP(dev, thaw); } EXPORT_SYMBOL_GPL(pm_generic_thaw); Loading @@ -192,9 +169,7 @@ EXPORT_SYMBOL_GPL(pm_generic_thaw); */ int pm_generic_resume_noirq(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->resume_noirq ? pm->resume_noirq(dev) : 0; return CALL_PM_OP(dev, resume_noirq); } EXPORT_SYMBOL_GPL(pm_generic_resume_noirq); Loading @@ -204,9 +179,7 @@ EXPORT_SYMBOL_GPL(pm_generic_resume_noirq); */ int pm_generic_resume_early(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->resume_early ? pm->resume_early(dev) : 0; return CALL_PM_OP(dev, resume_early); } EXPORT_SYMBOL_GPL(pm_generic_resume_early); Loading @@ -216,9 +189,7 @@ EXPORT_SYMBOL_GPL(pm_generic_resume_early); */ int pm_generic_resume(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->resume ? pm->resume(dev) : 0; return CALL_PM_OP(dev, resume); } EXPORT_SYMBOL_GPL(pm_generic_resume); Loading @@ -228,9 +199,7 @@ EXPORT_SYMBOL_GPL(pm_generic_resume); */ int pm_generic_restore_noirq(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->restore_noirq ? pm->restore_noirq(dev) : 0; return CALL_PM_OP(dev, restore_noirq); } EXPORT_SYMBOL_GPL(pm_generic_restore_noirq); Loading @@ -240,9 +209,7 @@ EXPORT_SYMBOL_GPL(pm_generic_restore_noirq); */ int pm_generic_restore_early(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->restore_early ? pm->restore_early(dev) : 0; return CALL_PM_OP(dev, restore_early); } EXPORT_SYMBOL_GPL(pm_generic_restore_early); Loading @@ -252,9 +219,7 @@ EXPORT_SYMBOL_GPL(pm_generic_restore_early); */ int pm_generic_restore(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->restore ? pm->restore(dev) : 0; return CALL_PM_OP(dev, restore); } EXPORT_SYMBOL_GPL(pm_generic_restore); Loading Loading
Documentation/ABI/testing/sysfs-power +16 −0 Original line number Diff line number Diff line Loading @@ -454,3 +454,19 @@ Description: disables it. Reads from the file return the current value. The default is "1" if the build-time "SUSPEND_SKIP_SYNC" config flag is unset, or "0" otherwise. What: /sys/power/hibernate_compression_threads Date: October 2025 Contact: <luoxueqin@kylinos.cn> Description: Controls the number of threads used for compression and decompression of hibernation images. The value can be adjusted at runtime to balance performance and CPU utilization. The change takes effect on the next hibernation or resume operation. Minimum value: 1 Default value: 3
Documentation/admin-guide/kernel-parameters.txt +10 −0 Original line number Diff line number Diff line Loading @@ -1907,6 +1907,16 @@ /sys/power/pm_test). Only available when CONFIG_PM_DEBUG is set. Default value is 5. hibernate_compression_threads= [HIBERNATION] Set the number of threads used for compressing or decompressing hibernation images. Format: <integer> Default: 3 Minimum: 1 Example: hibernate_compression_threads=4 highmem=nn[KMG] [KNL,BOOT,EARLY] forces the highmem zone to have an exact size of <nn>. This works even on boxes that have no highmem otherwise. This also works to reduce highmem Loading
Documentation/power/index.rst +1 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ Power Management power_supply_class runtime_pm s2ram shutdown-debugging suspend-and-cpuhotplug suspend-and-interrupts swsusp-and-swap-files Loading
Documentation/power/shutdown-debugging.rst 0 → 100644 +53 −0 Original line number Diff line number Diff line .. SPDX-License-Identifier: GPL-2.0 Debugging Kernel Shutdown Hangs with pstore +++++++++++++++++++++++++++++++++++++++++++ Overview ======== If the system hangs while shutting down, the kernel logs may need to be retrieved to debug the issue. On systems that have a UART available, it is best to configure the kernel to use this UART for kernel console output. If a UART isn't available, the ``pstore`` subsystem provides a mechanism to persist this data across a system reset, allowing it to be retrieved on the next boot. Kernel Configuration ==================== To enable ``pstore`` and enable saving kernel ring buffer logs, set the following kernel configuration options: * ``CONFIG_PSTORE=y`` * ``CONFIG_PSTORE_CONSOLE=y`` Additionally, enable a backend to store the data. Depending upon your platform some potential options include: * ``CONFIG_EFI_VARS_PSTORE=y`` * ``CONFIG_PSTORE_RAM=y`` * ``CONFIG_CHROMEOS_PSTORE=y`` * ``CONFIG_PSTORE_BLK=y`` Kernel Command-line Parameters ============================== Add these parameters to your kernel command line: * ``printk.always_kmsg_dump=Y`` * Forces the kernel to dump the entire message buffer to pstore during shutdown * ``efi_pstore.pstore_disable=N`` * For EFI-based systems, ensures the EFI backend is active Userspace Interaction and Log Retrieval ======================================= On the next boot after a hang, pstore logs will be available in the pstore filesystem (``/sys/fs/pstore``) and can be retrieved by userspace. On systemd systems, the ``systemd-pstore`` service will help do the following: #. Locate pstore data in ``/sys/fs/pstore`` #. Read and save it to ``/var/lib/systemd/pstore`` #. Clear pstore data for the next event
drivers/base/power/generic_ops.c +25 −60 Original line number Diff line number Diff line Loading @@ -8,6 +8,13 @@ #include <linux/pm_runtime.h> #include <linux/export.h> #define CALL_PM_OP(dev, op) \ ({ \ struct device *_dev = (dev); \ const struct dev_pm_ops *pm = _dev->driver ? _dev->driver->pm : NULL; \ pm && pm->op ? pm->op(_dev) : 0; \ }) #ifdef CONFIG_PM /** * pm_generic_runtime_suspend - Generic runtime suspend callback for subsystems. Loading @@ -19,12 +26,7 @@ */ int pm_generic_runtime_suspend(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int ret; ret = pm && pm->runtime_suspend ? pm->runtime_suspend(dev) : 0; return ret; return CALL_PM_OP(dev, runtime_suspend); } EXPORT_SYMBOL_GPL(pm_generic_runtime_suspend); Loading @@ -38,12 +40,7 @@ EXPORT_SYMBOL_GPL(pm_generic_runtime_suspend); */ int pm_generic_runtime_resume(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int ret; ret = pm && pm->runtime_resume ? pm->runtime_resume(dev) : 0; return ret; return CALL_PM_OP(dev, runtime_resume); } EXPORT_SYMBOL_GPL(pm_generic_runtime_resume); #endif /* CONFIG_PM */ Loading Loading @@ -72,9 +69,7 @@ int pm_generic_prepare(struct device *dev) */ int pm_generic_suspend_noirq(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->suspend_noirq ? pm->suspend_noirq(dev) : 0; return CALL_PM_OP(dev, suspend_noirq); } EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq); Loading @@ -84,9 +79,7 @@ EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq); */ int pm_generic_suspend_late(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->suspend_late ? pm->suspend_late(dev) : 0; return CALL_PM_OP(dev, suspend_late); } EXPORT_SYMBOL_GPL(pm_generic_suspend_late); Loading @@ -96,9 +89,7 @@ EXPORT_SYMBOL_GPL(pm_generic_suspend_late); */ int pm_generic_suspend(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->suspend ? pm->suspend(dev) : 0; return CALL_PM_OP(dev, suspend); } EXPORT_SYMBOL_GPL(pm_generic_suspend); Loading @@ -108,9 +99,7 @@ EXPORT_SYMBOL_GPL(pm_generic_suspend); */ int pm_generic_freeze_noirq(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->freeze_noirq ? pm->freeze_noirq(dev) : 0; return CALL_PM_OP(dev, freeze_noirq); } EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq); Loading @@ -120,9 +109,7 @@ EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq); */ int pm_generic_freeze(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->freeze ? pm->freeze(dev) : 0; return CALL_PM_OP(dev, freeze); } EXPORT_SYMBOL_GPL(pm_generic_freeze); Loading @@ -132,9 +119,7 @@ EXPORT_SYMBOL_GPL(pm_generic_freeze); */ int pm_generic_poweroff_noirq(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->poweroff_noirq ? pm->poweroff_noirq(dev) : 0; return CALL_PM_OP(dev, poweroff_noirq); } EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq); Loading @@ -144,9 +129,7 @@ EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq); */ int pm_generic_poweroff_late(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->poweroff_late ? pm->poweroff_late(dev) : 0; return CALL_PM_OP(dev, poweroff_late); } EXPORT_SYMBOL_GPL(pm_generic_poweroff_late); Loading @@ -156,9 +139,7 @@ EXPORT_SYMBOL_GPL(pm_generic_poweroff_late); */ int pm_generic_poweroff(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->poweroff ? pm->poweroff(dev) : 0; return CALL_PM_OP(dev, poweroff); } EXPORT_SYMBOL_GPL(pm_generic_poweroff); Loading @@ -168,9 +149,7 @@ EXPORT_SYMBOL_GPL(pm_generic_poweroff); */ int pm_generic_thaw_noirq(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->thaw_noirq ? pm->thaw_noirq(dev) : 0; return CALL_PM_OP(dev, thaw_noirq); } EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq); Loading @@ -180,9 +159,7 @@ EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq); */ int pm_generic_thaw(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->thaw ? pm->thaw(dev) : 0; return CALL_PM_OP(dev, thaw); } EXPORT_SYMBOL_GPL(pm_generic_thaw); Loading @@ -192,9 +169,7 @@ EXPORT_SYMBOL_GPL(pm_generic_thaw); */ int pm_generic_resume_noirq(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->resume_noirq ? pm->resume_noirq(dev) : 0; return CALL_PM_OP(dev, resume_noirq); } EXPORT_SYMBOL_GPL(pm_generic_resume_noirq); Loading @@ -204,9 +179,7 @@ EXPORT_SYMBOL_GPL(pm_generic_resume_noirq); */ int pm_generic_resume_early(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->resume_early ? pm->resume_early(dev) : 0; return CALL_PM_OP(dev, resume_early); } EXPORT_SYMBOL_GPL(pm_generic_resume_early); Loading @@ -216,9 +189,7 @@ EXPORT_SYMBOL_GPL(pm_generic_resume_early); */ int pm_generic_resume(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->resume ? pm->resume(dev) : 0; return CALL_PM_OP(dev, resume); } EXPORT_SYMBOL_GPL(pm_generic_resume); Loading @@ -228,9 +199,7 @@ EXPORT_SYMBOL_GPL(pm_generic_resume); */ int pm_generic_restore_noirq(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->restore_noirq ? pm->restore_noirq(dev) : 0; return CALL_PM_OP(dev, restore_noirq); } EXPORT_SYMBOL_GPL(pm_generic_restore_noirq); Loading @@ -240,9 +209,7 @@ EXPORT_SYMBOL_GPL(pm_generic_restore_noirq); */ int pm_generic_restore_early(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->restore_early ? pm->restore_early(dev) : 0; return CALL_PM_OP(dev, restore_early); } EXPORT_SYMBOL_GPL(pm_generic_restore_early); Loading @@ -252,9 +219,7 @@ EXPORT_SYMBOL_GPL(pm_generic_restore_early); */ int pm_generic_restore(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; return pm && pm->restore ? pm->restore(dev) : 0; return CALL_PM_OP(dev, restore); } EXPORT_SYMBOL_GPL(pm_generic_restore); Loading