Commit | Line | Data |
---|---|---|
55716d26 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
1da177e4 LT |
2 | /* |
3 | * kernel/power/main.c - PM subsystem core functionality. | |
4 | * | |
5 | * Copyright (c) 2003 Patrick Mochel | |
6 | * Copyright (c) 2003 Open Source Development Lab | |
1da177e4 LT |
7 | */ |
8 | ||
b52124a7 | 9 | #include <linux/acpi.h> |
6e5fdeed | 10 | #include <linux/export.h> |
1da177e4 LT |
11 | #include <linux/kobject.h> |
12 | #include <linux/string.h> | |
431d452a | 13 | #include <linux/pm-trace.h> |
5e928f77 | 14 | #include <linux/workqueue.h> |
2a77c46d SL |
15 | #include <linux/debugfs.h> |
16 | #include <linux/seq_file.h> | |
55f2503c | 17 | #include <linux/suspend.h> |
b5dee313 | 18 | #include <linux/syscalls.h> |
f49249d5 | 19 | #include <linux/pm_runtime.h> |
1da177e4 LT |
20 | |
21 | #include "power.h" | |
22 | ||
cd51e61c | 23 | #ifdef CONFIG_PM_SLEEP |
07f44ac3 KW |
24 | /* |
25 | * The following functions are used by the suspend/hibernate code to temporarily | |
26 | * change gfp_allowed_mask in order to avoid using I/O during memory allocations | |
27 | * while devices are suspended. To avoid races with the suspend/hibernate code, | |
28 | * they should always be called with system_transition_mutex held | |
29 | * (gfp_allowed_mask also should only be modified with system_transition_mutex | |
30 | * held, unless the suspend/hibernate code is guaranteed not to run in parallel | |
31 | * with that modification). | |
32 | */ | |
33 | static gfp_t saved_gfp_mask; | |
34 | ||
35 | void pm_restore_gfp_mask(void) | |
36 | { | |
37 | WARN_ON(!mutex_is_locked(&system_transition_mutex)); | |
38 | if (saved_gfp_mask) { | |
39 | gfp_allowed_mask = saved_gfp_mask; | |
40 | saved_gfp_mask = 0; | |
41 | } | |
42 | } | |
43 | ||
44 | void pm_restrict_gfp_mask(void) | |
45 | { | |
46 | WARN_ON(!mutex_is_locked(&system_transition_mutex)); | |
47 | WARN_ON(saved_gfp_mask); | |
48 | saved_gfp_mask = gfp_allowed_mask; | |
49 | gfp_allowed_mask &= ~(__GFP_IO | __GFP_FS); | |
50 | } | |
cd51e61c | 51 | |
5950e5d5 | 52 | unsigned int lock_system_sleep(void) |
4bf236a3 | 53 | { |
5950e5d5 | 54 | unsigned int flags = current->flags; |
f5d39b02 | 55 | current->flags |= PF_NOFREEZE; |
55f2503c | 56 | mutex_lock(&system_transition_mutex); |
5950e5d5 | 57 | return flags; |
4bf236a3 BVA |
58 | } |
59 | EXPORT_SYMBOL_GPL(lock_system_sleep); | |
60 | ||
5950e5d5 | 61 | void unlock_system_sleep(unsigned int flags) |
4bf236a3 | 62 | { |
f5d39b02 PZ |
63 | if (!(flags & PF_NOFREEZE)) |
64 | current->flags &= ~PF_NOFREEZE; | |
55f2503c | 65 | mutex_unlock(&system_transition_mutex); |
4bf236a3 BVA |
66 | } |
67 | EXPORT_SYMBOL_GPL(unlock_system_sleep); | |
68 | ||
b5dee313 HP |
69 | void ksys_sync_helper(void) |
70 | { | |
c64546b1 HP |
71 | ktime_t start; |
72 | long elapsed_msecs; | |
73 | ||
74 | start = ktime_get(); | |
b5dee313 | 75 | ksys_sync(); |
c64546b1 HP |
76 | elapsed_msecs = ktime_to_ms(ktime_sub(ktime_get(), start)); |
77 | pr_info("Filesystems sync: %ld.%03ld seconds\n", | |
78 | elapsed_msecs / MSEC_PER_SEC, elapsed_msecs % MSEC_PER_SEC); | |
b5dee313 HP |
79 | } |
80 | EXPORT_SYMBOL_GPL(ksys_sync_helper); | |
81 | ||
82525756 AS |
82 | /* Routines for PM-transition notifications */ |
83 | ||
84 | static BLOCKING_NOTIFIER_HEAD(pm_chain_head); | |
85 | ||
86 | int register_pm_notifier(struct notifier_block *nb) | |
87 | { | |
88 | return blocking_notifier_chain_register(&pm_chain_head, nb); | |
89 | } | |
90 | EXPORT_SYMBOL_GPL(register_pm_notifier); | |
91 | ||
92 | int unregister_pm_notifier(struct notifier_block *nb) | |
93 | { | |
94 | return blocking_notifier_chain_unregister(&pm_chain_head, nb); | |
95 | } | |
96 | EXPORT_SYMBOL_GPL(unregister_pm_notifier); | |
97 | ||
b52124a7 ML |
98 | void pm_report_hw_sleep_time(u64 t) |
99 | { | |
100 | suspend_stats.last_hw_sleep = t; | |
101 | suspend_stats.total_hw_sleep += t; | |
102 | } | |
103 | EXPORT_SYMBOL_GPL(pm_report_hw_sleep_time); | |
104 | ||
105 | void pm_report_max_hw_sleep(u64 t) | |
106 | { | |
107 | suspend_stats.max_hw_sleep = t; | |
108 | } | |
109 | EXPORT_SYMBOL_GPL(pm_report_max_hw_sleep); | |
110 | ||
70d93298 | 111 | int pm_notifier_call_chain_robust(unsigned long val_up, unsigned long val_down) |
82525756 | 112 | { |
ea00f4f4 LW |
113 | int ret; |
114 | ||
70d93298 | 115 | ret = blocking_notifier_call_chain_robust(&pm_chain_head, val_up, val_down, NULL); |
f0c077a8 AM |
116 | |
117 | return notifier_to_errno(ret); | |
82525756 | 118 | } |
70d93298 | 119 | |
ea00f4f4 LW |
120 | int pm_notifier_call_chain(unsigned long val) |
121 | { | |
70d93298 | 122 | return blocking_notifier_call_chain(&pm_chain_head, val, NULL); |
ea00f4f4 | 123 | } |
82525756 | 124 | |
0e06b4a8 RW |
125 | /* If set, devices may be suspended and resumed asynchronously. */ |
126 | int pm_async_enabled = 1; | |
127 | ||
128 | static ssize_t pm_async_show(struct kobject *kobj, struct kobj_attribute *attr, | |
129 | char *buf) | |
130 | { | |
131 | return sprintf(buf, "%d\n", pm_async_enabled); | |
132 | } | |
133 | ||
134 | static ssize_t pm_async_store(struct kobject *kobj, struct kobj_attribute *attr, | |
135 | const char *buf, size_t n) | |
136 | { | |
137 | unsigned long val; | |
138 | ||
883ee4f7 | 139 | if (kstrtoul(buf, 10, &val)) |
0e06b4a8 RW |
140 | return -EINVAL; |
141 | ||
142 | if (val > 1) | |
143 | return -EINVAL; | |
144 | ||
145 | pm_async_enabled = val; | |
146 | return n; | |
147 | } | |
148 | ||
149 | power_attr(pm_async); | |
150 | ||
406e7938 RW |
151 | #ifdef CONFIG_SUSPEND |
152 | static ssize_t mem_sleep_show(struct kobject *kobj, struct kobj_attribute *attr, | |
153 | char *buf) | |
154 | { | |
155 | char *s = buf; | |
156 | suspend_state_t i; | |
157 | ||
9ea4dcf4 DW |
158 | for (i = PM_SUSPEND_MIN; i < PM_SUSPEND_MAX; i++) { |
159 | if (i >= PM_SUSPEND_MEM && cxl_mem_active()) | |
160 | continue; | |
406e7938 RW |
161 | if (mem_sleep_states[i]) { |
162 | const char *label = mem_sleep_states[i]; | |
163 | ||
164 | if (mem_sleep_current == i) | |
165 | s += sprintf(s, "[%s] ", label); | |
166 | else | |
167 | s += sprintf(s, "%s ", label); | |
168 | } | |
9ea4dcf4 | 169 | } |
406e7938 RW |
170 | |
171 | /* Convert the last space to a newline if needed. */ | |
172 | if (s != buf) | |
173 | *(s-1) = '\n'; | |
174 | ||
175 | return (s - buf); | |
176 | } | |
177 | ||
178 | static suspend_state_t decode_suspend_state(const char *buf, size_t n) | |
179 | { | |
180 | suspend_state_t state; | |
181 | char *p; | |
182 | int len; | |
183 | ||
184 | p = memchr(buf, '\n', n); | |
185 | len = p ? p - buf : n; | |
186 | ||
187 | for (state = PM_SUSPEND_MIN; state < PM_SUSPEND_MAX; state++) { | |
188 | const char *label = mem_sleep_states[state]; | |
189 | ||
190 | if (label && len == strlen(label) && !strncmp(buf, label, len)) | |
191 | return state; | |
192 | } | |
193 | ||
194 | return PM_SUSPEND_ON; | |
195 | } | |
196 | ||
197 | static ssize_t mem_sleep_store(struct kobject *kobj, struct kobj_attribute *attr, | |
198 | const char *buf, size_t n) | |
199 | { | |
200 | suspend_state_t state; | |
201 | int error; | |
202 | ||
203 | error = pm_autosleep_lock(); | |
204 | if (error) | |
205 | return error; | |
206 | ||
207 | if (pm_autosleep_state() > PM_SUSPEND_ON) { | |
208 | error = -EBUSY; | |
209 | goto out; | |
210 | } | |
211 | ||
212 | state = decode_suspend_state(buf, n); | |
213 | if (state < PM_SUSPEND_MAX && state > PM_SUSPEND_ON) | |
214 | mem_sleep_current = state; | |
215 | else | |
216 | error = -EINVAL; | |
217 | ||
218 | out: | |
219 | pm_autosleep_unlock(); | |
220 | return error ? error : n; | |
221 | } | |
222 | ||
223 | power_attr(mem_sleep); | |
c052bf82 JM |
224 | |
225 | /* | |
226 | * sync_on_suspend: invoke ksys_sync_helper() before suspend. | |
227 | * | |
228 | * show() returns whether ksys_sync_helper() is invoked before suspend. | |
229 | * store() accepts 0 or 1. 0 disables ksys_sync_helper() and 1 enables it. | |
230 | */ | |
231 | bool sync_on_suspend_enabled = !IS_ENABLED(CONFIG_SUSPEND_SKIP_SYNC); | |
232 | ||
233 | static ssize_t sync_on_suspend_show(struct kobject *kobj, | |
234 | struct kobj_attribute *attr, char *buf) | |
235 | { | |
236 | return sprintf(buf, "%d\n", sync_on_suspend_enabled); | |
237 | } | |
238 | ||
239 | static ssize_t sync_on_suspend_store(struct kobject *kobj, | |
240 | struct kobj_attribute *attr, | |
241 | const char *buf, size_t n) | |
242 | { | |
243 | unsigned long val; | |
244 | ||
245 | if (kstrtoul(buf, 10, &val)) | |
246 | return -EINVAL; | |
247 | ||
248 | if (val > 1) | |
249 | return -EINVAL; | |
250 | ||
251 | sync_on_suspend_enabled = !!val; | |
252 | return n; | |
253 | } | |
254 | ||
255 | power_attr(sync_on_suspend); | |
406e7938 RW |
256 | #endif /* CONFIG_SUSPEND */ |
257 | ||
e516a1db | 258 | #ifdef CONFIG_PM_SLEEP_DEBUG |
0e7d56e3 RW |
259 | int pm_test_level = TEST_NONE; |
260 | ||
0e7d56e3 RW |
261 | static const char * const pm_tests[__TEST_AFTER_LAST] = { |
262 | [TEST_NONE] = "none", | |
263 | [TEST_CORE] = "core", | |
264 | [TEST_CPUS] = "processors", | |
265 | [TEST_PLATFORM] = "platform", | |
266 | [TEST_DEVICES] = "devices", | |
267 | [TEST_FREEZER] = "freezer", | |
268 | }; | |
269 | ||
039a75c6 RW |
270 | static ssize_t pm_test_show(struct kobject *kobj, struct kobj_attribute *attr, |
271 | char *buf) | |
0e7d56e3 RW |
272 | { |
273 | char *s = buf; | |
274 | int level; | |
275 | ||
276 | for (level = TEST_FIRST; level <= TEST_MAX; level++) | |
277 | if (pm_tests[level]) { | |
278 | if (level == pm_test_level) | |
279 | s += sprintf(s, "[%s] ", pm_tests[level]); | |
280 | else | |
281 | s += sprintf(s, "%s ", pm_tests[level]); | |
282 | } | |
283 | ||
284 | if (s != buf) | |
285 | /* convert the last space to a newline */ | |
286 | *(s-1) = '\n'; | |
287 | ||
288 | return (s - buf); | |
289 | } | |
290 | ||
039a75c6 RW |
291 | static ssize_t pm_test_store(struct kobject *kobj, struct kobj_attribute *attr, |
292 | const char *buf, size_t n) | |
0e7d56e3 | 293 | { |
5950e5d5 | 294 | unsigned int sleep_flags; |
0e7d56e3 | 295 | const char * const *s; |
5950e5d5 | 296 | int error = -EINVAL; |
0e7d56e3 RW |
297 | int level; |
298 | char *p; | |
299 | int len; | |
0e7d56e3 RW |
300 | |
301 | p = memchr(buf, '\n', n); | |
302 | len = p ? p - buf : n; | |
303 | ||
5950e5d5 | 304 | sleep_flags = lock_system_sleep(); |
0e7d56e3 RW |
305 | |
306 | level = TEST_FIRST; | |
307 | for (s = &pm_tests[level]; level <= TEST_MAX; s++, level++) | |
308 | if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) { | |
309 | pm_test_level = level; | |
310 | error = 0; | |
311 | break; | |
312 | } | |
313 | ||
5950e5d5 | 314 | unlock_system_sleep(sleep_flags); |
0e7d56e3 RW |
315 | |
316 | return error ? error : n; | |
317 | } | |
318 | ||
319 | power_attr(pm_test); | |
e516a1db | 320 | #endif /* CONFIG_PM_SLEEP_DEBUG */ |
0e7d56e3 | 321 | |
2a77c46d SL |
322 | static char *suspend_step_name(enum suspend_stat_step step) |
323 | { | |
324 | switch (step) { | |
325 | case SUSPEND_FREEZE: | |
326 | return "freeze"; | |
327 | case SUSPEND_PREPARE: | |
328 | return "prepare"; | |
329 | case SUSPEND_SUSPEND: | |
330 | return "suspend"; | |
331 | case SUSPEND_SUSPEND_NOIRQ: | |
332 | return "suspend_noirq"; | |
333 | case SUSPEND_RESUME_NOIRQ: | |
334 | return "resume_noirq"; | |
335 | case SUSPEND_RESUME: | |
336 | return "resume"; | |
337 | default: | |
338 | return ""; | |
339 | } | |
340 | } | |
341 | ||
b52124a7 | 342 | #define suspend_attr(_name, format_str) \ |
2c8db5be KS |
343 | static ssize_t _name##_show(struct kobject *kobj, \ |
344 | struct kobj_attribute *attr, char *buf) \ | |
345 | { \ | |
b52124a7 | 346 | return sprintf(buf, format_str, suspend_stats._name); \ |
2c8db5be KS |
347 | } \ |
348 | static struct kobj_attribute _name = __ATTR_RO(_name) | |
349 | ||
b52124a7 ML |
350 | suspend_attr(success, "%d\n"); |
351 | suspend_attr(fail, "%d\n"); | |
352 | suspend_attr(failed_freeze, "%d\n"); | |
353 | suspend_attr(failed_prepare, "%d\n"); | |
354 | suspend_attr(failed_suspend, "%d\n"); | |
355 | suspend_attr(failed_suspend_late, "%d\n"); | |
356 | suspend_attr(failed_suspend_noirq, "%d\n"); | |
357 | suspend_attr(failed_resume, "%d\n"); | |
358 | suspend_attr(failed_resume_early, "%d\n"); | |
359 | suspend_attr(failed_resume_noirq, "%d\n"); | |
360 | suspend_attr(last_hw_sleep, "%llu\n"); | |
361 | suspend_attr(total_hw_sleep, "%llu\n"); | |
362 | suspend_attr(max_hw_sleep, "%llu\n"); | |
2c8db5be KS |
363 | |
364 | static ssize_t last_failed_dev_show(struct kobject *kobj, | |
365 | struct kobj_attribute *attr, char *buf) | |
366 | { | |
367 | int index; | |
368 | char *last_failed_dev = NULL; | |
369 | ||
370 | index = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1; | |
371 | index %= REC_FAILED_NUM; | |
372 | last_failed_dev = suspend_stats.failed_devs[index]; | |
373 | ||
374 | return sprintf(buf, "%s\n", last_failed_dev); | |
375 | } | |
376 | static struct kobj_attribute last_failed_dev = __ATTR_RO(last_failed_dev); | |
377 | ||
378 | static ssize_t last_failed_errno_show(struct kobject *kobj, | |
379 | struct kobj_attribute *attr, char *buf) | |
380 | { | |
381 | int index; | |
382 | int last_failed_errno; | |
383 | ||
384 | index = suspend_stats.last_failed_errno + REC_FAILED_NUM - 1; | |
385 | index %= REC_FAILED_NUM; | |
386 | last_failed_errno = suspend_stats.errno[index]; | |
387 | ||
388 | return sprintf(buf, "%d\n", last_failed_errno); | |
389 | } | |
390 | static struct kobj_attribute last_failed_errno = __ATTR_RO(last_failed_errno); | |
391 | ||
392 | static ssize_t last_failed_step_show(struct kobject *kobj, | |
393 | struct kobj_attribute *attr, char *buf) | |
394 | { | |
395 | int index; | |
396 | enum suspend_stat_step step; | |
397 | char *last_failed_step = NULL; | |
398 | ||
399 | index = suspend_stats.last_failed_step + REC_FAILED_NUM - 1; | |
400 | index %= REC_FAILED_NUM; | |
401 | step = suspend_stats.failed_steps[index]; | |
402 | last_failed_step = suspend_step_name(step); | |
403 | ||
404 | return sprintf(buf, "%s\n", last_failed_step); | |
405 | } | |
406 | static struct kobj_attribute last_failed_step = __ATTR_RO(last_failed_step); | |
407 | ||
408 | static struct attribute *suspend_attrs[] = { | |
409 | &success.attr, | |
410 | &fail.attr, | |
411 | &failed_freeze.attr, | |
412 | &failed_prepare.attr, | |
413 | &failed_suspend.attr, | |
414 | &failed_suspend_late.attr, | |
415 | &failed_suspend_noirq.attr, | |
416 | &failed_resume.attr, | |
417 | &failed_resume_early.attr, | |
418 | &failed_resume_noirq.attr, | |
419 | &last_failed_dev.attr, | |
420 | &last_failed_errno.attr, | |
421 | &last_failed_step.attr, | |
b52124a7 ML |
422 | &last_hw_sleep.attr, |
423 | &total_hw_sleep.attr, | |
424 | &max_hw_sleep.attr, | |
2c8db5be KS |
425 | NULL, |
426 | }; | |
427 | ||
b52124a7 ML |
428 | static umode_t suspend_attr_is_visible(struct kobject *kobj, struct attribute *attr, int idx) |
429 | { | |
430 | if (attr != &last_hw_sleep.attr && | |
431 | attr != &total_hw_sleep.attr && | |
432 | attr != &max_hw_sleep.attr) | |
433 | return 0444; | |
434 | ||
435 | #ifdef CONFIG_ACPI | |
436 | if (acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) | |
437 | return 0444; | |
438 | #endif | |
439 | return 0; | |
440 | } | |
441 | ||
15560574 | 442 | static const struct attribute_group suspend_attr_group = { |
2c8db5be KS |
443 | .name = "suspend_stats", |
444 | .attrs = suspend_attrs, | |
b52124a7 | 445 | .is_visible = suspend_attr_is_visible, |
2c8db5be KS |
446 | }; |
447 | ||
448 | #ifdef CONFIG_DEBUG_FS | |
2a77c46d SL |
449 | static int suspend_stats_show(struct seq_file *s, void *unused) |
450 | { | |
451 | int i, index, last_dev, last_errno, last_step; | |
452 | ||
453 | last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1; | |
454 | last_dev %= REC_FAILED_NUM; | |
455 | last_errno = suspend_stats.last_failed_errno + REC_FAILED_NUM - 1; | |
456 | last_errno %= REC_FAILED_NUM; | |
457 | last_step = suspend_stats.last_failed_step + REC_FAILED_NUM - 1; | |
458 | last_step %= REC_FAILED_NUM; | |
cf579dfb RW |
459 | seq_printf(s, "%s: %d\n%s: %d\n%s: %d\n%s: %d\n%s: %d\n" |
460 | "%s: %d\n%s: %d\n%s: %d\n%s: %d\n%s: %d\n", | |
2a77c46d SL |
461 | "success", suspend_stats.success, |
462 | "fail", suspend_stats.fail, | |
463 | "failed_freeze", suspend_stats.failed_freeze, | |
464 | "failed_prepare", suspend_stats.failed_prepare, | |
465 | "failed_suspend", suspend_stats.failed_suspend, | |
cf579dfb RW |
466 | "failed_suspend_late", |
467 | suspend_stats.failed_suspend_late, | |
2a77c46d SL |
468 | "failed_suspend_noirq", |
469 | suspend_stats.failed_suspend_noirq, | |
470 | "failed_resume", suspend_stats.failed_resume, | |
cf579dfb RW |
471 | "failed_resume_early", |
472 | suspend_stats.failed_resume_early, | |
2a77c46d SL |
473 | "failed_resume_noirq", |
474 | suspend_stats.failed_resume_noirq); | |
475 | seq_printf(s, "failures:\n last_failed_dev:\t%-s\n", | |
476 | suspend_stats.failed_devs[last_dev]); | |
477 | for (i = 1; i < REC_FAILED_NUM; i++) { | |
478 | index = last_dev + REC_FAILED_NUM - i; | |
479 | index %= REC_FAILED_NUM; | |
480 | seq_printf(s, "\t\t\t%-s\n", | |
481 | suspend_stats.failed_devs[index]); | |
482 | } | |
483 | seq_printf(s, " last_failed_errno:\t%-d\n", | |
484 | suspend_stats.errno[last_errno]); | |
485 | for (i = 1; i < REC_FAILED_NUM; i++) { | |
486 | index = last_errno + REC_FAILED_NUM - i; | |
487 | index %= REC_FAILED_NUM; | |
488 | seq_printf(s, "\t\t\t%-d\n", | |
489 | suspend_stats.errno[index]); | |
490 | } | |
491 | seq_printf(s, " last_failed_step:\t%-s\n", | |
492 | suspend_step_name( | |
493 | suspend_stats.failed_steps[last_step])); | |
494 | for (i = 1; i < REC_FAILED_NUM; i++) { | |
495 | index = last_step + REC_FAILED_NUM - i; | |
496 | index %= REC_FAILED_NUM; | |
497 | seq_printf(s, "\t\t\t%-s\n", | |
498 | suspend_step_name( | |
499 | suspend_stats.failed_steps[index])); | |
500 | } | |
501 | ||
502 | return 0; | |
503 | } | |
943a10f8 | 504 | DEFINE_SHOW_ATTRIBUTE(suspend_stats); |
2a77c46d SL |
505 | |
506 | static int __init pm_debugfs_init(void) | |
507 | { | |
508 | debugfs_create_file("suspend_stats", S_IFREG | S_IRUGO, | |
943a10f8 | 509 | NULL, NULL, &suspend_stats_fops); |
2a77c46d SL |
510 | return 0; |
511 | } | |
512 | ||
513 | late_initcall(pm_debugfs_init); | |
514 | #endif /* CONFIG_DEBUG_FS */ | |
515 | ||
ca123102 RW |
516 | #endif /* CONFIG_PM_SLEEP */ |
517 | ||
b2df1d4f RW |
518 | #ifdef CONFIG_PM_SLEEP_DEBUG |
519 | /* | |
520 | * pm_print_times: print time taken by devices to suspend and resume. | |
521 | * | |
522 | * show() returns whether printing of suspend and resume times is enabled. | |
523 | * store() accepts 0 or 1. 0 disables printing and 1 enables it. | |
524 | */ | |
525 | bool pm_print_times_enabled; | |
526 | ||
527 | static ssize_t pm_print_times_show(struct kobject *kobj, | |
528 | struct kobj_attribute *attr, char *buf) | |
529 | { | |
530 | return sprintf(buf, "%d\n", pm_print_times_enabled); | |
531 | } | |
532 | ||
533 | static ssize_t pm_print_times_store(struct kobject *kobj, | |
534 | struct kobj_attribute *attr, | |
535 | const char *buf, size_t n) | |
536 | { | |
537 | unsigned long val; | |
538 | ||
539 | if (kstrtoul(buf, 10, &val)) | |
540 | return -EINVAL; | |
541 | ||
542 | if (val > 1) | |
543 | return -EINVAL; | |
544 | ||
545 | pm_print_times_enabled = !!val; | |
546 | return n; | |
547 | } | |
548 | ||
549 | power_attr(pm_print_times); | |
550 | ||
551 | static inline void pm_print_times_init(void) | |
552 | { | |
553 | pm_print_times_enabled = !!initcall_debug; | |
554 | } | |
a6f5f0dd AY |
555 | |
556 | static ssize_t pm_wakeup_irq_show(struct kobject *kobj, | |
557 | struct kobj_attribute *attr, | |
558 | char *buf) | |
559 | { | |
cb1f65c1 RW |
560 | if (!pm_wakeup_irq()) |
561 | return -ENODATA; | |
562 | ||
563 | return sprintf(buf, "%u\n", pm_wakeup_irq()); | |
a6f5f0dd AY |
564 | } |
565 | ||
a1e9ca69 | 566 | power_attr_ro(pm_wakeup_irq); |
a6f5f0dd | 567 | |
726fb6b4 | 568 | bool pm_debug_messages_on __read_mostly; |
8d8b2441 | 569 | |
cdb8c100 ML |
570 | bool pm_debug_messages_should_print(void) |
571 | { | |
572 | return pm_debug_messages_on && pm_suspend_target_state != PM_SUSPEND_ON; | |
573 | } | |
574 | EXPORT_SYMBOL_GPL(pm_debug_messages_should_print); | |
575 | ||
8d8b2441 RW |
576 | static ssize_t pm_debug_messages_show(struct kobject *kobj, |
577 | struct kobj_attribute *attr, char *buf) | |
578 | { | |
579 | return sprintf(buf, "%d\n", pm_debug_messages_on); | |
580 | } | |
581 | ||
582 | static ssize_t pm_debug_messages_store(struct kobject *kobj, | |
583 | struct kobj_attribute *attr, | |
584 | const char *buf, size_t n) | |
585 | { | |
586 | unsigned long val; | |
587 | ||
588 | if (kstrtoul(buf, 10, &val)) | |
589 | return -EINVAL; | |
590 | ||
591 | if (val > 1) | |
592 | return -EINVAL; | |
593 | ||
594 | pm_debug_messages_on = !!val; | |
595 | return n; | |
596 | } | |
597 | ||
598 | power_attr(pm_debug_messages); | |
599 | ||
db96a759 CY |
600 | static int __init pm_debug_messages_setup(char *str) |
601 | { | |
602 | pm_debug_messages_on = true; | |
603 | return 1; | |
604 | } | |
605 | __setup("pm_debug_messages", pm_debug_messages_setup); | |
606 | ||
819b1bb3 | 607 | #else /* !CONFIG_PM_SLEEP_DEBUG */ |
b2df1d4f RW |
608 | static inline void pm_print_times_init(void) {} |
609 | #endif /* CONFIG_PM_SLEEP_DEBUG */ | |
610 | ||
d76e15fb | 611 | struct kobject *power_kobj; |
1da177e4 | 612 | |
dbcfa715 | 613 | /* |
0399d4db | 614 | * state - control system sleep states. |
1da177e4 | 615 | * |
0399d4db | 616 | * show() returns available sleep state labels, which may be "mem", "standby", |
44348e8a MCC |
617 | * "freeze" and "disk" (hibernation). |
618 | * See Documentation/admin-guide/pm/sleep-states.rst for a description of | |
619 | * what they mean. | |
1da177e4 | 620 | * |
0399d4db RW |
621 | * store() accepts one of those strings, translates it into the proper |
622 | * enumerated value, and initiates a suspend transition. | |
1da177e4 | 623 | */ |
386f275f KS |
624 | static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, |
625 | char *buf) | |
1da177e4 | 626 | { |
296699de RW |
627 | char *s = buf; |
628 | #ifdef CONFIG_SUSPEND | |
27ddcc65 RW |
629 | suspend_state_t i; |
630 | ||
631 | for (i = PM_SUSPEND_MIN; i < PM_SUSPEND_MAX; i++) | |
d431cbc5 RW |
632 | if (pm_states[i]) |
633 | s += sprintf(s,"%s ", pm_states[i]); | |
1da177e4 | 634 | |
296699de | 635 | #endif |
a6e15a39 KC |
636 | if (hibernation_available()) |
637 | s += sprintf(s, "disk "); | |
a3d25c27 RW |
638 | if (s != buf) |
639 | /* convert the last space to a newline */ | |
640 | *(s-1) = '\n'; | |
1da177e4 LT |
641 | return (s - buf); |
642 | } | |
643 | ||
7483b4a4 | 644 | static suspend_state_t decode_state(const char *buf, size_t n) |
1da177e4 | 645 | { |
296699de | 646 | #ifdef CONFIG_SUSPEND |
d431cbc5 | 647 | suspend_state_t state; |
296699de | 648 | #endif |
1da177e4 | 649 | char *p; |
1da177e4 LT |
650 | int len; |
651 | ||
652 | p = memchr(buf, '\n', n); | |
653 | len = p ? p - buf : n; | |
654 | ||
7483b4a4 | 655 | /* Check hibernation first. */ |
d30bdfc0 | 656 | if (len == 4 && str_has_prefix(buf, "disk")) |
7483b4a4 | 657 | return PM_SUSPEND_MAX; |
a3d25c27 | 658 | |
296699de | 659 | #ifdef CONFIG_SUSPEND |
d431cbc5 RW |
660 | for (state = PM_SUSPEND_MIN; state < PM_SUSPEND_MAX; state++) { |
661 | const char *label = pm_states[state]; | |
662 | ||
663 | if (label && len == strlen(label) && !strncmp(buf, label, len)) | |
664 | return state; | |
665 | } | |
296699de RW |
666 | #endif |
667 | ||
7483b4a4 RW |
668 | return PM_SUSPEND_ON; |
669 | } | |
670 | ||
671 | static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, | |
672 | const char *buf, size_t n) | |
673 | { | |
674 | suspend_state_t state; | |
675 | int error; | |
676 | ||
677 | error = pm_autosleep_lock(); | |
678 | if (error) | |
679 | return error; | |
680 | ||
681 | if (pm_autosleep_state() > PM_SUSPEND_ON) { | |
682 | error = -EBUSY; | |
683 | goto out; | |
684 | } | |
685 | ||
686 | state = decode_state(buf, n); | |
406e7938 RW |
687 | if (state < PM_SUSPEND_MAX) { |
688 | if (state == PM_SUSPEND_MEM) | |
689 | state = mem_sleep_current; | |
690 | ||
7483b4a4 | 691 | error = pm_suspend(state); |
406e7938 | 692 | } else if (state == PM_SUSPEND_MAX) { |
7483b4a4 | 693 | error = hibernate(); |
406e7938 | 694 | } else { |
7483b4a4 | 695 | error = -EINVAL; |
406e7938 | 696 | } |
7483b4a4 RW |
697 | |
698 | out: | |
699 | pm_autosleep_unlock(); | |
1da177e4 LT |
700 | return error ? error : n; |
701 | } | |
702 | ||
703 | power_attr(state); | |
704 | ||
c125e96f RW |
705 | #ifdef CONFIG_PM_SLEEP |
706 | /* | |
707 | * The 'wakeup_count' attribute, along with the functions defined in | |
708 | * drivers/base/power/wakeup.c, provides a means by which wakeup events can be | |
709 | * handled in a non-racy way. | |
710 | * | |
711 | * If a wakeup event occurs when the system is in a sleep state, it simply is | |
712 | * woken up. In turn, if an event that would wake the system up from a sleep | |
713 | * state occurs when it is undergoing a transition to that sleep state, the | |
714 | * transition should be aborted. Moreover, if such an event occurs when the | |
715 | * system is in the working state, an attempt to start a transition to the | |
716 | * given sleep state should fail during certain period after the detection of | |
717 | * the event. Using the 'state' attribute alone is not sufficient to satisfy | |
718 | * these requirements, because a wakeup event may occur exactly when 'state' | |
719 | * is being written to and may be delivered to user space right before it is | |
720 | * frozen, so the event will remain only partially processed until the system is | |
721 | * woken up by another event. In particular, it won't cause the transition to | |
722 | * a sleep state to be aborted. | |
723 | * | |
724 | * This difficulty may be overcome if user space uses 'wakeup_count' before | |
725 | * writing to 'state'. It first should read from 'wakeup_count' and store | |
726 | * the read value. Then, after carrying out its own preparations for the system | |
727 | * transition to a sleep state, it should write the stored value to | |
25985edc | 728 | * 'wakeup_count'. If that fails, at least one wakeup event has occurred since |
c125e96f RW |
729 | * 'wakeup_count' was read and 'state' should not be written to. Otherwise, it |
730 | * is allowed to write to 'state', but the transition will be aborted if there | |
731 | * are any wakeup events detected after 'wakeup_count' was written to. | |
732 | */ | |
733 | ||
734 | static ssize_t wakeup_count_show(struct kobject *kobj, | |
735 | struct kobj_attribute *attr, | |
736 | char *buf) | |
737 | { | |
074037ec | 738 | unsigned int val; |
c125e96f | 739 | |
7483b4a4 RW |
740 | return pm_get_wakeup_count(&val, true) ? |
741 | sprintf(buf, "%u\n", val) : -EINTR; | |
c125e96f RW |
742 | } |
743 | ||
744 | static ssize_t wakeup_count_store(struct kobject *kobj, | |
745 | struct kobj_attribute *attr, | |
746 | const char *buf, size_t n) | |
747 | { | |
074037ec | 748 | unsigned int val; |
7483b4a4 RW |
749 | int error; |
750 | ||
751 | error = pm_autosleep_lock(); | |
752 | if (error) | |
753 | return error; | |
c125e96f | 754 | |
7483b4a4 RW |
755 | if (pm_autosleep_state() > PM_SUSPEND_ON) { |
756 | error = -EBUSY; | |
757 | goto out; | |
758 | } | |
759 | ||
760 | error = -EINVAL; | |
074037ec | 761 | if (sscanf(buf, "%u", &val) == 1) { |
c125e96f | 762 | if (pm_save_wakeup_count(val)) |
7483b4a4 | 763 | error = n; |
bb177fed JW |
764 | else |
765 | pm_print_active_wakeup_sources(); | |
c125e96f | 766 | } |
7483b4a4 RW |
767 | |
768 | out: | |
769 | pm_autosleep_unlock(); | |
770 | return error; | |
c125e96f RW |
771 | } |
772 | ||
773 | power_attr(wakeup_count); | |
7483b4a4 RW |
774 | |
775 | #ifdef CONFIG_PM_AUTOSLEEP | |
776 | static ssize_t autosleep_show(struct kobject *kobj, | |
777 | struct kobj_attribute *attr, | |
778 | char *buf) | |
779 | { | |
780 | suspend_state_t state = pm_autosleep_state(); | |
781 | ||
782 | if (state == PM_SUSPEND_ON) | |
783 | return sprintf(buf, "off\n"); | |
784 | ||
785 | #ifdef CONFIG_SUSPEND | |
786 | if (state < PM_SUSPEND_MAX) | |
d431cbc5 RW |
787 | return sprintf(buf, "%s\n", pm_states[state] ? |
788 | pm_states[state] : "error"); | |
7483b4a4 RW |
789 | #endif |
790 | #ifdef CONFIG_HIBERNATION | |
791 | return sprintf(buf, "disk\n"); | |
792 | #else | |
793 | return sprintf(buf, "error"); | |
794 | #endif | |
795 | } | |
796 | ||
797 | static ssize_t autosleep_store(struct kobject *kobj, | |
798 | struct kobj_attribute *attr, | |
799 | const char *buf, size_t n) | |
800 | { | |
801 | suspend_state_t state = decode_state(buf, n); | |
802 | int error; | |
803 | ||
804 | if (state == PM_SUSPEND_ON | |
040e5bf6 | 805 | && strcmp(buf, "off") && strcmp(buf, "off\n")) |
7483b4a4 RW |
806 | return -EINVAL; |
807 | ||
406e7938 RW |
808 | if (state == PM_SUSPEND_MEM) |
809 | state = mem_sleep_current; | |
810 | ||
7483b4a4 RW |
811 | error = pm_autosleep_set_state(state); |
812 | return error ? error : n; | |
813 | } | |
814 | ||
815 | power_attr(autosleep); | |
816 | #endif /* CONFIG_PM_AUTOSLEEP */ | |
b86ff982 RW |
817 | |
818 | #ifdef CONFIG_PM_WAKELOCKS | |
819 | static ssize_t wake_lock_show(struct kobject *kobj, | |
820 | struct kobj_attribute *attr, | |
821 | char *buf) | |
822 | { | |
823 | return pm_show_wakelocks(buf, true); | |
824 | } | |
825 | ||
826 | static ssize_t wake_lock_store(struct kobject *kobj, | |
827 | struct kobj_attribute *attr, | |
828 | const char *buf, size_t n) | |
829 | { | |
830 | int error = pm_wake_lock(buf); | |
831 | return error ? error : n; | |
832 | } | |
833 | ||
834 | power_attr(wake_lock); | |
835 | ||
836 | static ssize_t wake_unlock_show(struct kobject *kobj, | |
837 | struct kobj_attribute *attr, | |
838 | char *buf) | |
839 | { | |
840 | return pm_show_wakelocks(buf, false); | |
841 | } | |
842 | ||
843 | static ssize_t wake_unlock_store(struct kobject *kobj, | |
844 | struct kobj_attribute *attr, | |
845 | const char *buf, size_t n) | |
846 | { | |
847 | int error = pm_wake_unlock(buf); | |
848 | return error ? error : n; | |
849 | } | |
850 | ||
851 | power_attr(wake_unlock); | |
852 | ||
853 | #endif /* CONFIG_PM_WAKELOCKS */ | |
c125e96f RW |
854 | #endif /* CONFIG_PM_SLEEP */ |
855 | ||
c5c6ba4e RW |
856 | #ifdef CONFIG_PM_TRACE |
857 | int pm_trace_enabled; | |
858 | ||
386f275f KS |
859 | static ssize_t pm_trace_show(struct kobject *kobj, struct kobj_attribute *attr, |
860 | char *buf) | |
c5c6ba4e RW |
861 | { |
862 | return sprintf(buf, "%d\n", pm_trace_enabled); | |
863 | } | |
864 | ||
865 | static ssize_t | |
386f275f KS |
866 | pm_trace_store(struct kobject *kobj, struct kobj_attribute *attr, |
867 | const char *buf, size_t n) | |
c5c6ba4e RW |
868 | { |
869 | int val; | |
870 | ||
871 | if (sscanf(buf, "%d", &val) == 1) { | |
872 | pm_trace_enabled = !!val; | |
9dceefe4 SK |
873 | if (pm_trace_enabled) { |
874 | pr_warn("PM: Enabling pm_trace changes system date and time during resume.\n" | |
875 | "PM: Correct system time has to be restored manually after resume.\n"); | |
876 | } | |
c5c6ba4e RW |
877 | return n; |
878 | } | |
879 | return -EINVAL; | |
880 | } | |
881 | ||
882 | power_attr(pm_trace); | |
d33ac60b JH |
883 | |
884 | static ssize_t pm_trace_dev_match_show(struct kobject *kobj, | |
885 | struct kobj_attribute *attr, | |
886 | char *buf) | |
887 | { | |
888 | return show_trace_dev_match(buf, PAGE_SIZE); | |
889 | } | |
890 | ||
a1e9ca69 | 891 | power_attr_ro(pm_trace_dev_match); |
d33ac60b | 892 | |
0e7d56e3 | 893 | #endif /* CONFIG_PM_TRACE */ |
c5c6ba4e | 894 | |
957d1282 LF |
895 | #ifdef CONFIG_FREEZER |
896 | static ssize_t pm_freeze_timeout_show(struct kobject *kobj, | |
897 | struct kobj_attribute *attr, char *buf) | |
898 | { | |
899 | return sprintf(buf, "%u\n", freeze_timeout_msecs); | |
900 | } | |
901 | ||
902 | static ssize_t pm_freeze_timeout_store(struct kobject *kobj, | |
903 | struct kobj_attribute *attr, | |
904 | const char *buf, size_t n) | |
905 | { | |
906 | unsigned long val; | |
907 | ||
908 | if (kstrtoul(buf, 10, &val)) | |
909 | return -EINVAL; | |
910 | ||
911 | freeze_timeout_msecs = val; | |
912 | return n; | |
913 | } | |
914 | ||
915 | power_attr(pm_freeze_timeout); | |
916 | ||
917 | #endif /* CONFIG_FREEZER*/ | |
918 | ||
c5c6ba4e RW |
919 | static struct attribute * g[] = { |
920 | &state_attr.attr, | |
0e7d56e3 | 921 | #ifdef CONFIG_PM_TRACE |
c5c6ba4e | 922 | &pm_trace_attr.attr, |
d33ac60b | 923 | &pm_trace_dev_match_attr.attr, |
0e7d56e3 | 924 | #endif |
0e06b4a8 RW |
925 | #ifdef CONFIG_PM_SLEEP |
926 | &pm_async_attr.attr, | |
c125e96f | 927 | &wakeup_count_attr.attr, |
406e7938 RW |
928 | #ifdef CONFIG_SUSPEND |
929 | &mem_sleep_attr.attr, | |
c052bf82 | 930 | &sync_on_suspend_attr.attr, |
406e7938 | 931 | #endif |
7483b4a4 RW |
932 | #ifdef CONFIG_PM_AUTOSLEEP |
933 | &autosleep_attr.attr, | |
934 | #endif | |
b86ff982 RW |
935 | #ifdef CONFIG_PM_WAKELOCKS |
936 | &wake_lock_attr.attr, | |
937 | &wake_unlock_attr.attr, | |
938 | #endif | |
b2df1d4f | 939 | #ifdef CONFIG_PM_SLEEP_DEBUG |
e516a1db | 940 | &pm_test_attr.attr, |
4b7760ba | 941 | &pm_print_times_attr.attr, |
a6f5f0dd | 942 | &pm_wakeup_irq_attr.attr, |
8d8b2441 | 943 | &pm_debug_messages_attr.attr, |
0e06b4a8 | 944 | #endif |
957d1282 LF |
945 | #endif |
946 | #ifdef CONFIG_FREEZER | |
947 | &pm_freeze_timeout_attr.attr, | |
0e7d56e3 | 948 | #endif |
c5c6ba4e RW |
949 | NULL, |
950 | }; | |
1da177e4 | 951 | |
1d0c6e59 | 952 | static const struct attribute_group attr_group = { |
1da177e4 LT |
953 | .attrs = g, |
954 | }; | |
955 | ||
2c8db5be KS |
956 | static const struct attribute_group *attr_groups[] = { |
957 | &attr_group, | |
958 | #ifdef CONFIG_PM_SLEEP | |
959 | &suspend_attr_group, | |
960 | #endif | |
961 | NULL, | |
962 | }; | |
963 | ||
5e928f77 | 964 | struct workqueue_struct *pm_wq; |
7b199ca2 | 965 | EXPORT_SYMBOL_GPL(pm_wq); |
5e928f77 RW |
966 | |
967 | static int __init pm_start_workqueue(void) | |
968 | { | |
58a69cb4 | 969 | pm_wq = alloc_workqueue("pm", WQ_FREEZABLE, 0); |
5e928f77 RW |
970 | |
971 | return pm_wq ? 0 : -ENOMEM; | |
972 | } | |
5e928f77 | 973 | |
1da177e4 LT |
974 | static int __init pm_init(void) |
975 | { | |
5e928f77 RW |
976 | int error = pm_start_workqueue(); |
977 | if (error) | |
978 | return error; | |
ac5c24ec | 979 | hibernate_image_size_init(); |
ddeb6487 | 980 | hibernate_reserved_size_init(); |
fa7fd6fa | 981 | pm_states_init(); |
d76e15fb GKH |
982 | power_kobj = kobject_create_and_add("power", NULL); |
983 | if (!power_kobj) | |
039a5dcd | 984 | return -ENOMEM; |
2c8db5be | 985 | error = sysfs_create_groups(power_kobj, attr_groups); |
7483b4a4 RW |
986 | if (error) |
987 | return error; | |
b2df1d4f | 988 | pm_print_times_init(); |
7483b4a4 | 989 | return pm_autosleep_init(); |
1da177e4 LT |
990 | } |
991 | ||
992 | core_initcall(pm_init); |