Commit c052bf82 authored by Jonas Meurer's avatar Jonas Meurer Committed by Rafael J. Wysocki

PM: suspend: Add sysfs attribute to control the "sync on suspend" behavior

The sysfs attribute `/sys/power/sync_on_suspend` controls, whether or not
filesystems are synced by the kernel before system suspend.

Congruously, the behaviour of build-time switch CONFIG_SUSPEND_SKIP_SYNC
is slightly changed: It now defines the run-tim default for the new sysfs
attribute `/sys/power/sync_on_suspend`.

The run-time attribute is added because the existing corresponding
build-time Kconfig flag for (`CONFIG_SUSPEND_SKIP_SYNC`) is not flexible
enough. E.g. Linux distributions that provide pre-compiled kernels
usually want to stick with the default (sync filesystems before suspend)
but under special conditions this needs to be changed.

One example for such a special condition is user-space handling of
suspending block devices (e.g. using `cryptsetup luksSuspend` or `dmsetup
suspend`) before system suspend. The Kernel trying to sync filesystems
after the underlying block device already got suspended obviously leads
to dead-locks. Be aware that you have to take care of the filesystem sync
yourself before suspending the system in those scenarios.
Signed-off-by: default avatarJonas Meurer <jonas@freesources.org>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 5c0e9de0
...@@ -407,3 +407,16 @@ Contact: Kalesh Singh <kaleshsingh96@gmail.com> ...@@ -407,3 +407,16 @@ Contact: Kalesh Singh <kaleshsingh96@gmail.com>
Description: Description:
The /sys/power/suspend_stats/last_failed_step file contains The /sys/power/suspend_stats/last_failed_step file contains
the last failed step in the suspend/resume path. the last failed step in the suspend/resume path.
What: /sys/power/sync_on_suspend
Date: October 2019
Contact: Jonas Meurer <jonas@freesources.org>
Description:
This file controls whether or not the kernel will sync()
filesystems during system suspend (after freezing user space
and before suspending devices).
Writing a "1" to this file enables the sync() and writing a "0"
disables it. Reads from the file return the current value.
The default is "1" if the build-time "SUSPEND_SKIP_SYNC" config
flag is unset, or "0" otherwise.
...@@ -329,6 +329,7 @@ extern void arch_suspend_disable_irqs(void); ...@@ -329,6 +329,7 @@ extern void arch_suspend_disable_irqs(void);
extern void arch_suspend_enable_irqs(void); extern void arch_suspend_enable_irqs(void);
extern int pm_suspend(suspend_state_t state); extern int pm_suspend(suspend_state_t state);
extern bool sync_on_suspend_enabled;
#else /* !CONFIG_SUSPEND */ #else /* !CONFIG_SUSPEND */
#define suspend_valid_only_mem NULL #define suspend_valid_only_mem NULL
...@@ -342,6 +343,7 @@ static inline bool pm_suspend_default_s2idle(void) { return false; } ...@@ -342,6 +343,7 @@ static inline bool pm_suspend_default_s2idle(void) { return false; }
static inline void suspend_set_ops(const struct platform_suspend_ops *ops) {} static inline void suspend_set_ops(const struct platform_suspend_ops *ops) {}
static inline int pm_suspend(suspend_state_t state) { return -ENOSYS; } static inline int pm_suspend(suspend_state_t state) { return -ENOSYS; }
static inline bool sync_on_suspend_enabled(void) { return true; }
static inline bool idle_should_enter_s2idle(void) { return false; } static inline bool idle_should_enter_s2idle(void) { return false; }
static inline void __init pm_states_init(void) {} static inline void __init pm_states_init(void) {}
static inline void s2idle_set_ops(const struct platform_s2idle_ops *ops) {} static inline void s2idle_set_ops(const struct platform_s2idle_ops *ops) {}
......
...@@ -27,7 +27,10 @@ config SUSPEND_SKIP_SYNC ...@@ -27,7 +27,10 @@ config SUSPEND_SKIP_SYNC
Skip the kernel sys_sync() before freezing user processes. Skip the kernel sys_sync() before freezing user processes.
Some systems prefer not to pay this cost on every invocation Some systems prefer not to pay this cost on every invocation
of suspend, or they are content with invoking sync() from of suspend, or they are content with invoking sync() from
user-space before invoking suspend. Say Y if that's your case. user-space before invoking suspend. There's a run-time switch
at '/sys/power/sync_on_suspend' to configure this behaviour.
This setting changes the default for the run-tim switch. Say Y
to change the default to disable the kernel sys_sync().
config HIBERNATE_CALLBACKS config HIBERNATE_CALLBACKS
bool bool
......
...@@ -190,6 +190,38 @@ static ssize_t mem_sleep_store(struct kobject *kobj, struct kobj_attribute *attr ...@@ -190,6 +190,38 @@ static ssize_t mem_sleep_store(struct kobject *kobj, struct kobj_attribute *attr
} }
power_attr(mem_sleep); power_attr(mem_sleep);
/*
* sync_on_suspend: invoke ksys_sync_helper() before suspend.
*
* show() returns whether ksys_sync_helper() is invoked before suspend.
* store() accepts 0 or 1. 0 disables ksys_sync_helper() and 1 enables it.
*/
bool sync_on_suspend_enabled = !IS_ENABLED(CONFIG_SUSPEND_SKIP_SYNC);
static ssize_t sync_on_suspend_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", sync_on_suspend_enabled);
}
static ssize_t sync_on_suspend_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t n)
{
unsigned long val;
if (kstrtoul(buf, 10, &val))
return -EINVAL;
if (val > 1)
return -EINVAL;
sync_on_suspend_enabled = !!val;
return n;
}
power_attr(sync_on_suspend);
#endif /* CONFIG_SUSPEND */ #endif /* CONFIG_SUSPEND */
#ifdef CONFIG_PM_SLEEP_DEBUG #ifdef CONFIG_PM_SLEEP_DEBUG
...@@ -855,6 +887,7 @@ static struct attribute * g[] = { ...@@ -855,6 +887,7 @@ static struct attribute * g[] = {
&wakeup_count_attr.attr, &wakeup_count_attr.attr,
#ifdef CONFIG_SUSPEND #ifdef CONFIG_SUSPEND
&mem_sleep_attr.attr, &mem_sleep_attr.attr,
&sync_on_suspend_attr.attr,
#endif #endif
#ifdef CONFIG_PM_AUTOSLEEP #ifdef CONFIG_PM_AUTOSLEEP
&autosleep_attr.attr, &autosleep_attr.attr,
......
...@@ -564,7 +564,7 @@ static int enter_state(suspend_state_t state) ...@@ -564,7 +564,7 @@ static int enter_state(suspend_state_t state)
if (state == PM_SUSPEND_TO_IDLE) if (state == PM_SUSPEND_TO_IDLE)
s2idle_begin(); s2idle_begin();
if (!IS_ENABLED(CONFIG_SUSPEND_SKIP_SYNC)) { if (sync_on_suspend_enabled) {
trace_suspend_resume(TPS("sync_filesystems"), 0, true); trace_suspend_resume(TPS("sync_filesystems"), 0, true);
ksys_sync_helper(); ksys_sync_helper();
trace_suspend_resume(TPS("sync_filesystems"), 0, false); trace_suspend_resume(TPS("sync_filesystems"), 0, false);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment