.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * kernel/power/main.c - PM subsystem core functionality. |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (c) 2003 Patrick Mochel |
---|
5 | 6 | * Copyright (c) 2003 Open Source Development Lab |
---|
6 | | - * |
---|
7 | | - * This file is released under the GPLv2 |
---|
8 | | - * |
---|
9 | 7 | */ |
---|
10 | 8 | |
---|
11 | 9 | #include <linux/export.h> |
---|
.. | .. |
---|
16 | 14 | #include <linux/debugfs.h> |
---|
17 | 15 | #include <linux/seq_file.h> |
---|
18 | 16 | #include <linux/suspend.h> |
---|
| 17 | +#include <linux/syscalls.h> |
---|
| 18 | +#include <linux/pm_runtime.h> |
---|
19 | 19 | |
---|
20 | 20 | #include "power.h" |
---|
21 | 21 | |
---|
.. | .. |
---|
51 | 51 | } |
---|
52 | 52 | EXPORT_SYMBOL_GPL(unlock_system_sleep); |
---|
53 | 53 | |
---|
| 54 | +void ksys_sync_helper(void) |
---|
| 55 | +{ |
---|
| 56 | + ktime_t start; |
---|
| 57 | + long elapsed_msecs; |
---|
| 58 | + |
---|
| 59 | + start = ktime_get(); |
---|
| 60 | + ksys_sync(); |
---|
| 61 | + elapsed_msecs = ktime_to_ms(ktime_sub(ktime_get(), start)); |
---|
| 62 | + pr_info("Filesystems sync: %ld.%03ld seconds\n", |
---|
| 63 | + elapsed_msecs / MSEC_PER_SEC, elapsed_msecs % MSEC_PER_SEC); |
---|
| 64 | +} |
---|
| 65 | +EXPORT_SYMBOL_GPL(ksys_sync_helper); |
---|
| 66 | + |
---|
54 | 67 | /* Routines for PM-transition notifications */ |
---|
55 | 68 | |
---|
56 | 69 | static BLOCKING_NOTIFIER_HEAD(pm_chain_head); |
---|
.. | .. |
---|
67 | 80 | } |
---|
68 | 81 | EXPORT_SYMBOL_GPL(unregister_pm_notifier); |
---|
69 | 82 | |
---|
70 | | -int __pm_notifier_call_chain(unsigned long val, int nr_to_call, int *nr_calls) |
---|
| 83 | +int pm_notifier_call_chain_robust(unsigned long val_up, unsigned long val_down) |
---|
71 | 84 | { |
---|
72 | 85 | int ret; |
---|
73 | 86 | |
---|
74 | | - ret = __blocking_notifier_call_chain(&pm_chain_head, val, NULL, |
---|
75 | | - nr_to_call, nr_calls); |
---|
| 87 | + ret = blocking_notifier_call_chain_robust(&pm_chain_head, val_up, val_down, NULL); |
---|
76 | 88 | |
---|
77 | 89 | return notifier_to_errno(ret); |
---|
78 | 90 | } |
---|
| 91 | + |
---|
79 | 92 | int pm_notifier_call_chain(unsigned long val) |
---|
80 | 93 | { |
---|
81 | | - return __pm_notifier_call_chain(val, -1, NULL); |
---|
| 94 | + return blocking_notifier_call_chain(&pm_chain_head, val, NULL); |
---|
82 | 95 | } |
---|
83 | 96 | |
---|
84 | 97 | /* If set, devices may be suspended and resumed asynchronously. */ |
---|
.. | .. |
---|
177 | 190 | } |
---|
178 | 191 | |
---|
179 | 192 | power_attr(mem_sleep); |
---|
| 193 | + |
---|
| 194 | +/* |
---|
| 195 | + * sync_on_suspend: invoke ksys_sync_helper() before suspend. |
---|
| 196 | + * |
---|
| 197 | + * show() returns whether ksys_sync_helper() is invoked before suspend. |
---|
| 198 | + * store() accepts 0 or 1. 0 disables ksys_sync_helper() and 1 enables it. |
---|
| 199 | + */ |
---|
| 200 | +bool sync_on_suspend_enabled = !IS_ENABLED(CONFIG_SUSPEND_SKIP_SYNC); |
---|
| 201 | + |
---|
| 202 | +static ssize_t sync_on_suspend_show(struct kobject *kobj, |
---|
| 203 | + struct kobj_attribute *attr, char *buf) |
---|
| 204 | +{ |
---|
| 205 | + return sprintf(buf, "%d\n", sync_on_suspend_enabled); |
---|
| 206 | +} |
---|
| 207 | + |
---|
| 208 | +static ssize_t sync_on_suspend_store(struct kobject *kobj, |
---|
| 209 | + struct kobj_attribute *attr, |
---|
| 210 | + const char *buf, size_t n) |
---|
| 211 | +{ |
---|
| 212 | + unsigned long val; |
---|
| 213 | + |
---|
| 214 | + if (kstrtoul(buf, 10, &val)) |
---|
| 215 | + return -EINVAL; |
---|
| 216 | + |
---|
| 217 | + if (val > 1) |
---|
| 218 | + return -EINVAL; |
---|
| 219 | + |
---|
| 220 | + sync_on_suspend_enabled = !!val; |
---|
| 221 | + return n; |
---|
| 222 | +} |
---|
| 223 | + |
---|
| 224 | +power_attr(sync_on_suspend); |
---|
180 | 225 | #endif /* CONFIG_SUSPEND */ |
---|
181 | 226 | |
---|
182 | 227 | #ifdef CONFIG_PM_SLEEP_DEBUG |
---|
.. | .. |
---|
403 | 448 | |
---|
404 | 449 | return 0; |
---|
405 | 450 | } |
---|
406 | | - |
---|
407 | | -static int suspend_stats_open(struct inode *inode, struct file *file) |
---|
408 | | -{ |
---|
409 | | - return single_open(file, suspend_stats_show, NULL); |
---|
410 | | -} |
---|
411 | | - |
---|
412 | | -static const struct file_operations suspend_stats_operations = { |
---|
413 | | - .open = suspend_stats_open, |
---|
414 | | - .read = seq_read, |
---|
415 | | - .llseek = seq_lseek, |
---|
416 | | - .release = single_release, |
---|
417 | | -}; |
---|
| 451 | +DEFINE_SHOW_ATTRIBUTE(suspend_stats); |
---|
418 | 452 | |
---|
419 | 453 | static int __init pm_debugfs_init(void) |
---|
420 | 454 | { |
---|
421 | 455 | debugfs_create_file("suspend_stats", S_IFREG | S_IRUGO, |
---|
422 | | - NULL, NULL, &suspend_stats_operations); |
---|
| 456 | + NULL, NULL, &suspend_stats_fops); |
---|
423 | 457 | return 0; |
---|
424 | 458 | } |
---|
425 | 459 | |
---|
.. | .. |
---|
470 | 504 | struct kobj_attribute *attr, |
---|
471 | 505 | char *buf) |
---|
472 | 506 | { |
---|
473 | | - return pm_wakeup_irq ? sprintf(buf, "%u\n", pm_wakeup_irq) : -ENODATA; |
---|
| 507 | + if (!pm_wakeup_irq()) |
---|
| 508 | + return -ENODATA; |
---|
| 509 | + |
---|
| 510 | + return sprintf(buf, "%u\n", pm_wakeup_irq()); |
---|
474 | 511 | } |
---|
475 | 512 | |
---|
476 | 513 | power_attr_ro(pm_wakeup_irq); |
---|
.. | .. |
---|
500 | 537 | } |
---|
501 | 538 | |
---|
502 | 539 | power_attr(pm_debug_messages); |
---|
| 540 | + |
---|
| 541 | +static int __init pm_debug_messages_setup(char *str) |
---|
| 542 | +{ |
---|
| 543 | + pm_debug_messages_on = true; |
---|
| 544 | + return 1; |
---|
| 545 | +} |
---|
| 546 | +__setup("pm_debug_messages", pm_debug_messages_setup); |
---|
503 | 547 | |
---|
504 | 548 | /** |
---|
505 | 549 | * __pm_pr_dbg - Print a suspend debug message to the kernel log. |
---|
.. | .. |
---|
535 | 579 | #endif /* CONFIG_PM_SLEEP_DEBUG */ |
---|
536 | 580 | |
---|
537 | 581 | struct kobject *power_kobj; |
---|
538 | | -EXPORT_SYMBOL_GPL(power_kobj); |
---|
539 | 582 | |
---|
540 | 583 | /** |
---|
541 | 584 | * state - control system sleep states. |
---|
.. | .. |
---|
580 | 623 | len = p ? p - buf : n; |
---|
581 | 624 | |
---|
582 | 625 | /* Check hibernation first. */ |
---|
583 | | - if (len == 4 && !strncmp(buf, "disk", len)) |
---|
| 626 | + if (len == 4 && str_has_prefix(buf, "disk")) |
---|
584 | 627 | return PM_SUSPEND_MAX; |
---|
585 | 628 | |
---|
586 | 629 | #ifdef CONFIG_SUSPEND |
---|
.. | .. |
---|
854 | 897 | &wakeup_count_attr.attr, |
---|
855 | 898 | #ifdef CONFIG_SUSPEND |
---|
856 | 899 | &mem_sleep_attr.attr, |
---|
| 900 | + &sync_on_suspend_attr.attr, |
---|
857 | 901 | #endif |
---|
858 | 902 | #ifdef CONFIG_PM_AUTOSLEEP |
---|
859 | 903 | &autosleep_attr.attr, |
---|