Skip to content

Commit bf8867e

Browse files
wusamuel6rafaeljw
authored andcommitted
PM: sleep: Add support for wakeup during filesystem sync
Add helper function pm_sleep_fs_sync() and related data structures as a preparation for allowing system suspend and hibernation to be aborted by wakeup events while syncing file systems. The new function, to be called by the suspend process in order to sync file systems, uses a dedicated ordered workqueue to run ksys_sync_helper() in parallel with the calling process. Next, it waits for the completion of the filesystem sync and periodically checks if any system wakeup events are pending, in which case it will return an error. If that happens while the filesystem sync is still in progress, it will continue, possibly after pm_sleep_fs_sync() has returned, and if that function is called again before the sync is complete, a new work item to run ksys_sync_helper() again will be queued (and waited for) to increase the likelihood of writing all of the dirty pages in memory back to persistent storage. Suggested-by: Saravana Kannan <saravanak@google.com> Signed-off-by: Samuel Wu <wusamuel@google.com> Co-developed-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> [ rjw: Subject and changelog rewrite, tags adjustment ] Link: https://patch.msgid.link/20251119171426.4086783-2-wusamuel@google.com Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent a857b53 commit bf8867e

2 files changed

Lines changed: 74 additions & 6 deletions

File tree

kernel/power/main.c

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#include <linux/suspend.h>
1919
#include <linux/syscalls.h>
2020
#include <linux/pm_runtime.h>
21+
#include <linux/atomic.h>
22+
#include <linux/wait.h>
2123

2224
#include "power.h"
2325

@@ -92,6 +94,61 @@ void ksys_sync_helper(void)
9294
}
9395
EXPORT_SYMBOL_GPL(ksys_sync_helper);
9496

97+
#if defined(CONFIG_SUSPEND) || defined(CONFIG_HIBERNATION)
98+
/* Wakeup events handling resolution while syncing file systems in jiffies */
99+
#define PM_FS_SYNC_WAKEUP_RESOLUTION 5
100+
101+
static atomic_t pm_fs_sync_count = ATOMIC_INIT(0);
102+
static struct workqueue_struct *pm_fs_sync_wq;
103+
static DECLARE_WAIT_QUEUE_HEAD(pm_fs_sync_wait);
104+
105+
static bool pm_fs_sync_completed(void)
106+
{
107+
return atomic_read(&pm_fs_sync_count) == 0;
108+
}
109+
110+
static void pm_fs_sync_work_fn(struct work_struct *work)
111+
{
112+
ksys_sync_helper();
113+
114+
if (atomic_dec_and_test(&pm_fs_sync_count))
115+
wake_up(&pm_fs_sync_wait);
116+
}
117+
static DECLARE_WORK(pm_fs_sync_work, pm_fs_sync_work_fn);
118+
119+
/**
120+
* pm_sleep_fs_sync() - Sync file systems in an interruptible way
121+
*
122+
* Return: 0 on successful file system sync, or -EBUSY if the file system sync
123+
* was aborted.
124+
*/
125+
int pm_sleep_fs_sync(void)
126+
{
127+
pm_wakeup_clear(0);
128+
129+
/*
130+
* Take back-to-back sleeps into account by queuing a subsequent fs sync
131+
* only if the previous fs sync is running or is not queued. Multiple fs
132+
* syncs increase the likelihood of saving the latest files immediately
133+
* before sleep.
134+
*/
135+
if (!work_pending(&pm_fs_sync_work)) {
136+
atomic_inc(&pm_fs_sync_count);
137+
queue_work(pm_fs_sync_wq, &pm_fs_sync_work);
138+
}
139+
140+
while (!pm_fs_sync_completed()) {
141+
if (pm_wakeup_pending())
142+
return -EBUSY;
143+
144+
wait_event_timeout(pm_fs_sync_wait, pm_fs_sync_completed(),
145+
PM_FS_SYNC_WAKEUP_RESOLUTION);
146+
}
147+
148+
return 0;
149+
}
150+
#endif /* CONFIG_SUSPEND || CONFIG_HIBERNATION */
151+
95152
/* Routines for PM-transition notifications */
96153

97154
static BLOCKING_NOTIFIER_HEAD(pm_chain_head);
@@ -231,10 +288,10 @@ static ssize_t mem_sleep_store(struct kobject *kobj, struct kobj_attribute *attr
231288
power_attr(mem_sleep);
232289

233290
/*
234-
* sync_on_suspend: invoke ksys_sync_helper() before suspend.
291+
* sync_on_suspend: Sync file systems before suspend.
235292
*
236-
* show() returns whether ksys_sync_helper() is invoked before suspend.
237-
* store() accepts 0 or 1. 0 disables ksys_sync_helper() and 1 enables it.
293+
* show() returns whether file systems sync before suspend is enabled.
294+
* store() accepts 0 or 1. 0 disables file systems sync and 1 enables it.
238295
*/
239296
bool sync_on_suspend_enabled = !IS_ENABLED(CONFIG_SUSPEND_SKIP_SYNC);
240297

@@ -1066,16 +1123,26 @@ static const struct attribute_group *attr_groups[] = {
10661123
struct workqueue_struct *pm_wq;
10671124
EXPORT_SYMBOL_GPL(pm_wq);
10681125

1069-
static int __init pm_start_workqueue(void)
1126+
static int __init pm_start_workqueues(void)
10701127
{
10711128
pm_wq = alloc_workqueue("pm", WQ_FREEZABLE, 0);
1129+
if (!pm_wq)
1130+
return -ENOMEM;
10721131

1073-
return pm_wq ? 0 : -ENOMEM;
1132+
#if defined(CONFIG_SUSPEND) || defined(CONFIG_HIBERNATION)
1133+
pm_fs_sync_wq = alloc_ordered_workqueue("pm_fs_sync", 0);
1134+
if (!pm_fs_sync_wq) {
1135+
destroy_workqueue(pm_wq);
1136+
return -ENOMEM;
1137+
}
1138+
#endif
1139+
1140+
return 0;
10741141
}
10751142

10761143
static int __init pm_init(void)
10771144
{
1078-
int error = pm_start_workqueue();
1145+
int error = pm_start_workqueues();
10791146
if (error)
10801147
return error;
10811148
hibernate_image_size_init();

kernel/power/power.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ struct swsusp_info {
1919
} __aligned(PAGE_SIZE);
2020

2121
#if defined(CONFIG_SUSPEND) || defined(CONFIG_HIBERNATION)
22+
extern int pm_sleep_fs_sync(void);
2223
extern bool filesystem_freeze_enabled;
2324
#endif
2425

0 commit comments

Comments
 (0)