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