Skip to content

Commit f086594

Browse files
committed
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() ...
2 parents 60d69a7 + c03aef8 commit f086594

24 files changed

Lines changed: 403 additions & 214 deletions

File tree

Documentation/ABI/testing/sysfs-power

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,3 +454,19 @@ Description:
454454
disables it. Reads from the file return the current value.
455455
The default is "1" if the build-time "SUSPEND_SKIP_SYNC" config
456456
flag is unset, or "0" otherwise.
457+
458+
What: /sys/power/hibernate_compression_threads
459+
Date: October 2025
460+
Contact: <luoxueqin@kylinos.cn>
461+
Description:
462+
Controls the number of threads used for compression
463+
and decompression of hibernation images.
464+
465+
The value can be adjusted at runtime to balance
466+
performance and CPU utilization.
467+
468+
The change takes effect on the next hibernation or
469+
resume operation.
470+
471+
Minimum value: 1
472+
Default value: 3

Documentation/admin-guide/kernel-parameters.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1907,6 +1907,16 @@
19071907
/sys/power/pm_test). Only available when CONFIG_PM_DEBUG
19081908
is set. Default value is 5.
19091909

1910+
hibernate_compression_threads=
1911+
[HIBERNATION]
1912+
Set the number of threads used for compressing or decompressing
1913+
hibernation images.
1914+
1915+
Format: <integer>
1916+
Default: 3
1917+
Minimum: 1
1918+
Example: hibernate_compression_threads=4
1919+
19101920
highmem=nn[KMG] [KNL,BOOT,EARLY] forces the highmem zone to have an exact
19111921
size of <nn>. This works even on boxes that have no
19121922
highmem otherwise. This also works to reduce highmem

Documentation/power/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Power Management
1919
power_supply_class
2020
runtime_pm
2121
s2ram
22+
shutdown-debugging
2223
suspend-and-cpuhotplug
2324
suspend-and-interrupts
2425
swsusp-and-swap-files
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
.. SPDX-License-Identifier: GPL-2.0
2+
3+
Debugging Kernel Shutdown Hangs with pstore
4+
+++++++++++++++++++++++++++++++++++++++++++
5+
6+
Overview
7+
========
8+
If the system hangs while shutting down, the kernel logs may need to be
9+
retrieved to debug the issue.
10+
11+
On systems that have a UART available, it is best to configure the kernel to use
12+
this UART for kernel console output.
13+
14+
If a UART isn't available, the ``pstore`` subsystem provides a mechanism to
15+
persist this data across a system reset, allowing it to be retrieved on the next
16+
boot.
17+
18+
Kernel Configuration
19+
====================
20+
To enable ``pstore`` and enable saving kernel ring buffer logs, set the
21+
following kernel configuration options:
22+
23+
* ``CONFIG_PSTORE=y``
24+
* ``CONFIG_PSTORE_CONSOLE=y``
25+
26+
Additionally, enable a backend to store the data. Depending upon your platform
27+
some potential options include:
28+
29+
* ``CONFIG_EFI_VARS_PSTORE=y``
30+
* ``CONFIG_PSTORE_RAM=y``
31+
* ``CONFIG_CHROMEOS_PSTORE=y``
32+
* ``CONFIG_PSTORE_BLK=y``
33+
34+
Kernel Command-line Parameters
35+
==============================
36+
Add these parameters to your kernel command line:
37+
38+
* ``printk.always_kmsg_dump=Y``
39+
* Forces the kernel to dump the entire message buffer to pstore during
40+
shutdown
41+
* ``efi_pstore.pstore_disable=N``
42+
* For EFI-based systems, ensures the EFI backend is active
43+
44+
Userspace Interaction and Log Retrieval
45+
=======================================
46+
On the next boot after a hang, pstore logs will be available in the pstore
47+
filesystem (``/sys/fs/pstore``) and can be retrieved by userspace.
48+
49+
On systemd systems, the ``systemd-pstore`` service will help do the following:
50+
51+
#. Locate pstore data in ``/sys/fs/pstore``
52+
#. Read and save it to ``/var/lib/systemd/pstore``
53+
#. Clear pstore data for the next event

drivers/base/power/generic_ops.c

Lines changed: 25 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@
88
#include <linux/pm_runtime.h>
99
#include <linux/export.h>
1010

11+
#define CALL_PM_OP(dev, op) \
12+
({ \
13+
struct device *_dev = (dev); \
14+
const struct dev_pm_ops *pm = _dev->driver ? _dev->driver->pm : NULL; \
15+
pm && pm->op ? pm->op(_dev) : 0; \
16+
})
17+
1118
#ifdef CONFIG_PM
1219
/**
1320
* pm_generic_runtime_suspend - Generic runtime suspend callback for subsystems.
@@ -19,12 +26,7 @@
1926
*/
2027
int pm_generic_runtime_suspend(struct device *dev)
2128
{
22-
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
23-
int ret;
24-
25-
ret = pm && pm->runtime_suspend ? pm->runtime_suspend(dev) : 0;
26-
27-
return ret;
29+
return CALL_PM_OP(dev, runtime_suspend);
2830
}
2931
EXPORT_SYMBOL_GPL(pm_generic_runtime_suspend);
3032

@@ -38,12 +40,7 @@ EXPORT_SYMBOL_GPL(pm_generic_runtime_suspend);
3840
*/
3941
int pm_generic_runtime_resume(struct device *dev)
4042
{
41-
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
42-
int ret;
43-
44-
ret = pm && pm->runtime_resume ? pm->runtime_resume(dev) : 0;
45-
46-
return ret;
43+
return CALL_PM_OP(dev, runtime_resume);
4744
}
4845
EXPORT_SYMBOL_GPL(pm_generic_runtime_resume);
4946
#endif /* CONFIG_PM */
@@ -72,9 +69,7 @@ int pm_generic_prepare(struct device *dev)
7269
*/
7370
int pm_generic_suspend_noirq(struct device *dev)
7471
{
75-
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
76-
77-
return pm && pm->suspend_noirq ? pm->suspend_noirq(dev) : 0;
72+
return CALL_PM_OP(dev, suspend_noirq);
7873
}
7974
EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq);
8075

@@ -84,9 +79,7 @@ EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq);
8479
*/
8580
int pm_generic_suspend_late(struct device *dev)
8681
{
87-
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
88-
89-
return pm && pm->suspend_late ? pm->suspend_late(dev) : 0;
82+
return CALL_PM_OP(dev, suspend_late);
9083
}
9184
EXPORT_SYMBOL_GPL(pm_generic_suspend_late);
9285

@@ -96,9 +89,7 @@ EXPORT_SYMBOL_GPL(pm_generic_suspend_late);
9689
*/
9790
int pm_generic_suspend(struct device *dev)
9891
{
99-
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
100-
101-
return pm && pm->suspend ? pm->suspend(dev) : 0;
92+
return CALL_PM_OP(dev, suspend);
10293
}
10394
EXPORT_SYMBOL_GPL(pm_generic_suspend);
10495

@@ -108,9 +99,7 @@ EXPORT_SYMBOL_GPL(pm_generic_suspend);
10899
*/
109100
int pm_generic_freeze_noirq(struct device *dev)
110101
{
111-
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
112-
113-
return pm && pm->freeze_noirq ? pm->freeze_noirq(dev) : 0;
102+
return CALL_PM_OP(dev, freeze_noirq);
114103
}
115104
EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq);
116105

@@ -120,9 +109,7 @@ EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq);
120109
*/
121110
int pm_generic_freeze(struct device *dev)
122111
{
123-
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
124-
125-
return pm && pm->freeze ? pm->freeze(dev) : 0;
112+
return CALL_PM_OP(dev, freeze);
126113
}
127114
EXPORT_SYMBOL_GPL(pm_generic_freeze);
128115

@@ -132,9 +119,7 @@ EXPORT_SYMBOL_GPL(pm_generic_freeze);
132119
*/
133120
int pm_generic_poweroff_noirq(struct device *dev)
134121
{
135-
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
136-
137-
return pm && pm->poweroff_noirq ? pm->poweroff_noirq(dev) : 0;
122+
return CALL_PM_OP(dev, poweroff_noirq);
138123
}
139124
EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq);
140125

@@ -144,9 +129,7 @@ EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq);
144129
*/
145130
int pm_generic_poweroff_late(struct device *dev)
146131
{
147-
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
148-
149-
return pm && pm->poweroff_late ? pm->poweroff_late(dev) : 0;
132+
return CALL_PM_OP(dev, poweroff_late);
150133
}
151134
EXPORT_SYMBOL_GPL(pm_generic_poweroff_late);
152135

@@ -156,9 +139,7 @@ EXPORT_SYMBOL_GPL(pm_generic_poweroff_late);
156139
*/
157140
int pm_generic_poweroff(struct device *dev)
158141
{
159-
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
160-
161-
return pm && pm->poweroff ? pm->poweroff(dev) : 0;
142+
return CALL_PM_OP(dev, poweroff);
162143
}
163144
EXPORT_SYMBOL_GPL(pm_generic_poweroff);
164145

@@ -168,9 +149,7 @@ EXPORT_SYMBOL_GPL(pm_generic_poweroff);
168149
*/
169150
int pm_generic_thaw_noirq(struct device *dev)
170151
{
171-
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
172-
173-
return pm && pm->thaw_noirq ? pm->thaw_noirq(dev) : 0;
152+
return CALL_PM_OP(dev, thaw_noirq);
174153
}
175154
EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq);
176155

@@ -180,9 +159,7 @@ EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq);
180159
*/
181160
int pm_generic_thaw(struct device *dev)
182161
{
183-
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
184-
185-
return pm && pm->thaw ? pm->thaw(dev) : 0;
162+
return CALL_PM_OP(dev, thaw);
186163
}
187164
EXPORT_SYMBOL_GPL(pm_generic_thaw);
188165

@@ -192,9 +169,7 @@ EXPORT_SYMBOL_GPL(pm_generic_thaw);
192169
*/
193170
int pm_generic_resume_noirq(struct device *dev)
194171
{
195-
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
196-
197-
return pm && pm->resume_noirq ? pm->resume_noirq(dev) : 0;
172+
return CALL_PM_OP(dev, resume_noirq);
198173
}
199174
EXPORT_SYMBOL_GPL(pm_generic_resume_noirq);
200175

@@ -204,9 +179,7 @@ EXPORT_SYMBOL_GPL(pm_generic_resume_noirq);
204179
*/
205180
int pm_generic_resume_early(struct device *dev)
206181
{
207-
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
208-
209-
return pm && pm->resume_early ? pm->resume_early(dev) : 0;
182+
return CALL_PM_OP(dev, resume_early);
210183
}
211184
EXPORT_SYMBOL_GPL(pm_generic_resume_early);
212185

@@ -216,9 +189,7 @@ EXPORT_SYMBOL_GPL(pm_generic_resume_early);
216189
*/
217190
int pm_generic_resume(struct device *dev)
218191
{
219-
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
220-
221-
return pm && pm->resume ? pm->resume(dev) : 0;
192+
return CALL_PM_OP(dev, resume);
222193
}
223194
EXPORT_SYMBOL_GPL(pm_generic_resume);
224195

@@ -228,9 +199,7 @@ EXPORT_SYMBOL_GPL(pm_generic_resume);
228199
*/
229200
int pm_generic_restore_noirq(struct device *dev)
230201
{
231-
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
232-
233-
return pm && pm->restore_noirq ? pm->restore_noirq(dev) : 0;
202+
return CALL_PM_OP(dev, restore_noirq);
234203
}
235204
EXPORT_SYMBOL_GPL(pm_generic_restore_noirq);
236205

@@ -240,9 +209,7 @@ EXPORT_SYMBOL_GPL(pm_generic_restore_noirq);
240209
*/
241210
int pm_generic_restore_early(struct device *dev)
242211
{
243-
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
244-
245-
return pm && pm->restore_early ? pm->restore_early(dev) : 0;
212+
return CALL_PM_OP(dev, restore_early);
246213
}
247214
EXPORT_SYMBOL_GPL(pm_generic_restore_early);
248215

@@ -252,9 +219,7 @@ EXPORT_SYMBOL_GPL(pm_generic_restore_early);
252219
*/
253220
int pm_generic_restore(struct device *dev)
254221
{
255-
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
256-
257-
return pm && pm->restore ? pm->restore(dev) : 0;
222+
return CALL_PM_OP(dev, restore);
258223
}
259224
EXPORT_SYMBOL_GPL(pm_generic_restore);
260225

drivers/base/power/main.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include <linux/cpufreq.h>
3535
#include <linux/devfreq.h>
3636
#include <linux/timer.h>
37+
#include <linux/nmi.h>
3738

3839
#include "../base.h"
3940
#include "power.h"
@@ -95,6 +96,8 @@ static const char *pm_verb(int event)
9596
return "restore";
9697
case PM_EVENT_RECOVER:
9798
return "recover";
99+
case PM_EVENT_POWEROFF:
100+
return "poweroff";
98101
default:
99102
return "(unknown PM event)";
100103
}
@@ -367,6 +370,7 @@ static pm_callback_t pm_op(const struct dev_pm_ops *ops, pm_message_t state)
367370
case PM_EVENT_FREEZE:
368371
case PM_EVENT_QUIESCE:
369372
return ops->freeze;
373+
case PM_EVENT_POWEROFF:
370374
case PM_EVENT_HIBERNATE:
371375
return ops->poweroff;
372376
case PM_EVENT_THAW:
@@ -401,6 +405,7 @@ static pm_callback_t pm_late_early_op(const struct dev_pm_ops *ops,
401405
case PM_EVENT_FREEZE:
402406
case PM_EVENT_QUIESCE:
403407
return ops->freeze_late;
408+
case PM_EVENT_POWEROFF:
404409
case PM_EVENT_HIBERNATE:
405410
return ops->poweroff_late;
406411
case PM_EVENT_THAW:
@@ -435,6 +440,7 @@ static pm_callback_t pm_noirq_op(const struct dev_pm_ops *ops, pm_message_t stat
435440
case PM_EVENT_FREEZE:
436441
case PM_EVENT_QUIESCE:
437442
return ops->freeze_noirq;
443+
case PM_EVENT_POWEROFF:
438444
case PM_EVENT_HIBERNATE:
439445
return ops->poweroff_noirq;
440446
case PM_EVENT_THAW:
@@ -515,6 +521,11 @@ struct dpm_watchdog {
515521
#define DECLARE_DPM_WATCHDOG_ON_STACK(wd) \
516522
struct dpm_watchdog wd
517523

524+
static bool __read_mostly dpm_watchdog_all_cpu_backtrace;
525+
module_param(dpm_watchdog_all_cpu_backtrace, bool, 0644);
526+
MODULE_PARM_DESC(dpm_watchdog_all_cpu_backtrace,
527+
"Backtrace all CPUs on DPM watchdog timeout");
528+
518529
/**
519530
* dpm_watchdog_handler - Driver suspend / resume watchdog handler.
520531
* @t: The timer that PM watchdog depends on.
@@ -530,8 +541,12 @@ static void dpm_watchdog_handler(struct timer_list *t)
530541
unsigned int time_left;
531542

532543
if (wd->fatal) {
544+
unsigned int this_cpu = smp_processor_id();
545+
533546
dev_emerg(wd->dev, "**** DPM device timeout ****\n");
534547
show_stack(wd->tsk, NULL, KERN_EMERG);
548+
if (dpm_watchdog_all_cpu_backtrace)
549+
trigger_allbutcpu_cpu_backtrace(this_cpu);
535550
panic("%s %s: unrecoverable failure\n",
536551
dev_driver_string(wd->dev), dev_name(wd->dev));
537552
}

drivers/base/power/trace.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,10 +238,8 @@ int show_trace_dev_match(char *buf, size_t size)
238238
unsigned int hash = hash_string(DEVSEED, dev_name(dev),
239239
DEVHASH);
240240
if (hash == value) {
241-
int len = snprintf(buf, size, "%s\n",
241+
int len = scnprintf(buf, size, "%s\n",
242242
dev_driver_string(dev));
243-
if (len > size)
244-
len = size;
245243
buf += len;
246244
ret += len;
247245
size -= len;

0 commit comments

Comments
 (0)