| .. | .. |
|---|
| 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, |
|---|