Skip to content

Commit 1cf9c4f

Browse files
committed
Merge back system sleep material for 6.19
2 parents 35e4a69 + 8e4ec90 commit 1cf9c4f

12 files changed

Lines changed: 190 additions & 98 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

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: 10 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"
@@ -515,6 +516,11 @@ struct dpm_watchdog {
515516
#define DECLARE_DPM_WATCHDOG_ON_STACK(wd) \
516517
struct dpm_watchdog wd
517518

519+
static bool __read_mostly dpm_watchdog_all_cpu_backtrace;
520+
module_param(dpm_watchdog_all_cpu_backtrace, bool, 0644);
521+
MODULE_PARM_DESC(dpm_watchdog_all_cpu_backtrace,
522+
"Backtrace all CPUs on DPM watchdog timeout");
523+
518524
/**
519525
* dpm_watchdog_handler - Driver suspend / resume watchdog handler.
520526
* @t: The timer that PM watchdog depends on.
@@ -530,8 +536,12 @@ static void dpm_watchdog_handler(struct timer_list *t)
530536
unsigned int time_left;
531537

532538
if (wd->fatal) {
539+
unsigned int this_cpu = smp_processor_id();
540+
533541
dev_emerg(wd->dev, "**** DPM device timeout ****\n");
534542
show_stack(wd->tsk, NULL, KERN_EMERG);
543+
if (dpm_watchdog_all_cpu_backtrace)
544+
trigger_allbutcpu_cpu_backtrace(this_cpu);
535545
panic("%s %s: unrecoverable failure\n",
536546
dev_driver_string(wd->dev), dev_name(wd->dev));
537547
}

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;

include/linux/freezer.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,18 @@ extern bool pm_nosig_freezing; /* PM nosig freezing in effect */
2222
extern unsigned int freeze_timeout_msecs;
2323

2424
/*
25-
* Check if a process has been frozen
25+
* Check if a process has been frozen for PM or cgroup1 freezer. Note that
26+
* cgroup2 freezer uses the job control mechanism and does not interact with
27+
* the PM freezer.
2628
*/
2729
extern bool frozen(struct task_struct *p);
2830

2931
extern bool freezing_slow_path(struct task_struct *p);
3032

3133
/*
32-
* Check if there is a request to freeze a process
34+
* Check if there is a request to freeze a task from PM or cgroup1 freezer.
35+
* Note that cgroup2 freezer uses the job control mechanism and does not
36+
* interact with the PM freezer.
3337
*/
3438
static inline bool freezing(struct task_struct *p)
3539
{
@@ -63,9 +67,9 @@ extern bool freeze_task(struct task_struct *p);
6367
extern bool set_freezable(void);
6468

6569
#ifdef CONFIG_CGROUP_FREEZER
66-
extern bool cgroup_freezing(struct task_struct *task);
70+
extern bool cgroup1_freezing(struct task_struct *task);
6771
#else /* !CONFIG_CGROUP_FREEZER */
68-
static inline bool cgroup_freezing(struct task_struct *task)
72+
static inline bool cgroup1_freezing(struct task_struct *task)
6973
{
7074
return false;
7175
}

include/linux/pm.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ extern void (*pm_power_off)(void);
2525

2626
struct device; /* we have a circular dep with device.h */
2727
#ifdef CONFIG_VT_CONSOLE_SLEEP
28-
extern void pm_vt_switch_required(struct device *dev, bool required);
28+
extern int pm_vt_switch_required(struct device *dev, bool required);
2929
extern void pm_vt_switch_unregister(struct device *dev);
3030
#else
31-
static inline void pm_vt_switch_required(struct device *dev, bool required)
31+
static inline int pm_vt_switch_required(struct device *dev, bool required)
3232
{
33+
return 0;
3334
}
3435
static inline void pm_vt_switch_unregister(struct device *dev)
3536
{

kernel/cgroup/legacy_freezer.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ static struct freezer *parent_freezer(struct freezer *freezer)
6363
return css_freezer(freezer->css.parent);
6464
}
6565

66-
bool cgroup_freezing(struct task_struct *task)
66+
bool cgroup1_freezing(struct task_struct *task)
6767
{
6868
bool ret;
6969

kernel/freezer.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ bool freezing_slow_path(struct task_struct *p)
4444
if (tsk_is_oom_victim(p))
4545
return false;
4646

47-
if (pm_nosig_freezing || cgroup_freezing(p))
47+
if (pm_nosig_freezing || cgroup1_freezing(p))
4848
return true;
4949

5050
if (pm_freezing && !(p->flags & PF_KTHREAD))

kernel/power/console.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@ static LIST_HEAD(pm_vt_switch_list);
4444
* no_console_suspend argument has been passed on the command line, VT
4545
* switches will occur.
4646
*/
47-
void pm_vt_switch_required(struct device *dev, bool required)
47+
int pm_vt_switch_required(struct device *dev, bool required)
4848
{
4949
struct pm_vt_switch *entry, *tmp;
50+
int ret = 0;
5051

5152
mutex_lock(&vt_switch_mutex);
5253
list_for_each_entry(tmp, &pm_vt_switch_list, head) {
@@ -58,15 +59,18 @@ void pm_vt_switch_required(struct device *dev, bool required)
5859
}
5960

6061
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
61-
if (!entry)
62+
if (!entry) {
63+
ret = -ENOMEM;
6264
goto out;
65+
}
6366

6467
entry->required = required;
6568
entry->dev = dev;
6669

6770
list_add(&entry->head, &pm_vt_switch_list);
6871
out:
6972
mutex_unlock(&vt_switch_mutex);
73+
return ret;
7074
}
7175
EXPORT_SYMBOL(pm_vt_switch_required);
7276

0 commit comments

Comments
 (0)