Commit f086594a authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branch 'pm-sleep'

Merge updates related to system suspend and hibernation for 6.19-rc1:

 - Replace snprintf() with scnprintf() in show_trace_dev_match()
   (Kaushlendra Kumar)

 - Fix memory allocation error handling in pm_vt_switch_required()
   (Malaya Kumar Rout)

 - Introduce CALL_PM_OP() macro and use it to simplify code in
   generic PM operations (Kaushlendra Kumar)

 - Add module param to backtrace all CPUs in the device power management
   watchdog (Sergey Senozhatsky)

 - Rework message printing in swsusp_save() (Rafael Wysocki)

 - Make it possible to change the number of hibernation compression
   threads (Xueqin Luo)

 - Clarify that only cgroup1 freezer uses PM freezer (Tejun Heo)

 - Add document on debugging shutdown hangs to PM documentation and
   correct a mistaken configuration option in it (Mario Limonciello)

 - Shut down wakeup source timer before removing the wakeup source from
   the list (Kaushlendra Kumar, Rafael Wysocki)

 - Introduce new PMSG_POWEROFF event for system shutdown handling with
   the help of PM device callbacks (Mario Limonciello)

 - Make pm_test delay interruptible by wakeup events (Riwen Lu)

 - Clean up kernel-doc comment style usage in the core hibernation
   code and remove unuseful comments from it (Sunday Adelodun, Rafael
   Wysocki)

 - Add support for handling wakeup events and aborting the suspend
   process while it is syncing file systems (Samuel Wu, Rafael Wysocki)

* pm-sleep: (21 commits)
  PM: hibernate: Extra cleanup of comments in swap handling code
  PM: sleep: Call pm_sleep_fs_sync() instead of ksys_sync_helper()
  PM: sleep: Add support for wakeup during filesystem sync
  PM: hibernate: Clean up kernel-doc comment style usage
  PM: suspend: Make pm_test delay interruptible by wakeup events
  usb: sl811-hcd: Add PM_EVENT_POWEROFF into suspend callbacks
  scsi: Add PM_EVENT_POWEROFF into suspend callbacks
  PM: Introduce new PMSG_POWEROFF event
  PM: wakeup: Update after recent wakeup source removal ordering change
  PM: wakeup: Delete timer before removing wakeup source from list
  Documentation: power: Correct a mistaken configuration option
  Documentation: power: Add document on debugging shutdown hangs
  freezer: Clarify that only cgroup1 freezer uses PM freezer
  PM: hibernate: add sysfs interface for hibernate_compression_threads
  PM: hibernate: make compression threads configurable
  PM: hibernate: dynamically allocate crc->unc_len/unc for configurable threads
  PM: hibernate: Rework message printing in swsusp_save()
  PM: dpm_watchdog: add module param to backtrace all CPUs
  PM: sleep: Introduce CALL_PM_OP() macro to simplify code
  PM: console: Fix memory allocation error handling in pm_vt_switch_required()
  ...
parents 60d69a7e c03aef88
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -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
+10 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ Power Management
    power_supply_class
    runtime_pm
    s2ram
    shutdown-debugging
    suspend-and-cpuhotplug
    suspend-and-interrupts
    swsusp-and-swap-files
+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
+25 −60
Original line number Diff line number Diff line
@@ -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.
@@ -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);

@@ -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 */
@@ -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);

@@ -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);

@@ -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);

@@ -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);

@@ -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);

@@ -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);

@@ -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);

@@ -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);

@@ -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);

@@ -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);

@@ -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);

@@ -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);

@@ -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);

@@ -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);

@@ -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);

@@ -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