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