Merge patch series "power: wire-up filesystem freeze/thaw with suspend/resume"

Christian Brauner <brauner@kernel.org> says:

Now all the pieces are in place to actually allow the power subsystem to
freeze/thaw filesystems during suspend/resume. Filesystems are only
frozen and thawed if the power subsystem does actually own the freeze.

Othwerwise it risks thawing filesystems it didn't own. This could be
done differently be e.g., keeping the filesystems that were actually
frozen on a list and then unfreezing them from that list. This is
disgustingly unclean though and reeks of an ugly hack.

If the filesystem is already frozen by the time we've frozen all
userspace processes we don't care to freeze it again. That's userspace's
job once the process resumes. We only actually freeze filesystems if we
absolutely have to and we ignore other failures to freeze.

We could bubble up errors and fail suspend/resume if the error isn't
EBUSY (aka it's already frozen) but I don't think that this is worth it.
Filesystem freezing during suspend/resume is best-effort. If the user
has 500 ext4 filesystems mounted and 4 fail to freeze for whatever
reason then we simply skip them.

What we have now is already a big improvement and let's see how we fare
with it before making our lives even harder (and uglier) than we have
to.

* patches from https://lore.kernel.org/r/20250402-work-freeze-v2-0-6719a97b52ac@kernel.org:
  kernfs: add warning about implementing freeze/thaw
  power: freeze filesystems during suspend/resume

Link: https://lore.kernel.org/r/20250402-work-freeze-v2-0-6719a97b52ac@kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
Christian Brauner 2025-04-03 09:24:20 +02:00
commit 05b158d4fd
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2
5 changed files with 72 additions and 1 deletions

View File

@ -62,6 +62,21 @@ const struct super_operations kernfs_sops = {
.show_options = kernfs_sop_show_options,
.show_path = kernfs_sop_show_path,
/*
* sysfs is built on top of kernfs and sysfs provides the power
* management infrastructure to support suspend/hibernate by
* writing to various files in /sys/power/. As filesystems may
* be automatically frozen during suspend/hibernate implementing
* freeze/thaw support for kernfs generically will cause
* deadlocks as the suspending/hibernation initiating task will
* hold a VFS lock that it will then wait upon to be released.
* If freeze/thaw for kernfs is needed talk to the VFS.
*/
.freeze_fs = NULL,
.unfreeze_fs = NULL,
.freeze_super = NULL,
.thaw_super = NULL,
};
static int kernfs_encode_fh(struct inode *inode, __u32 *fh, int *max_len,

View File

@ -778,6 +778,8 @@ int hibernate(void)
goto Restore;
ksys_sync_helper();
if (filesystem_freeze_enabled)
filesystems_freeze();
error = freeze_processes();
if (error)
@ -846,6 +848,7 @@ int hibernate(void)
/* Don't bother checking whether freezer_test_done is true */
freezer_test_done = false;
Exit:
filesystems_thaw();
pm_notifier_call_chain(PM_POST_HIBERNATION);
Restore:
pm_restore_console();
@ -882,6 +885,9 @@ int hibernate_quiet_exec(int (*func)(void *data), void *data)
if (error)
goto restore;
if (filesystem_freeze_enabled)
filesystems_freeze();
error = freeze_processes();
if (error)
goto exit;
@ -941,6 +947,7 @@ int hibernate_quiet_exec(int (*func)(void *data), void *data)
thaw_processes();
exit:
filesystems_thaw();
pm_notifier_call_chain(PM_POST_HIBERNATION);
restore:
@ -1029,19 +1036,26 @@ static int software_resume(void)
if (error)
goto Restore;
if (filesystem_freeze_enabled)
filesystems_freeze();
pm_pr_dbg("Preparing processes for hibernation restore.\n");
error = freeze_processes();
if (error)
if (error) {
filesystems_thaw();
goto Close_Finish;
}
error = freeze_kernel_threads();
if (error) {
thaw_processes();
filesystems_thaw();
goto Close_Finish;
}
error = load_image_and_restore();
thaw_processes();
filesystems_thaw();
Finish:
pm_notifier_call_chain(PM_POST_RESTORE);
Restore:

View File

@ -962,6 +962,34 @@ power_attr(pm_freeze_timeout);
#endif /* CONFIG_FREEZER*/
#if defined(CONFIG_SUSPEND) || defined(CONFIG_HIBERNATION)
bool filesystem_freeze_enabled = false;
static ssize_t freeze_filesystems_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return sysfs_emit(buf, "%d\n", filesystem_freeze_enabled);
}
static ssize_t freeze_filesystems_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;
filesystem_freeze_enabled = !!val;
return n;
}
power_attr(freeze_filesystems);
#endif /* CONFIG_SUSPEND || CONFIG_HIBERNATION */
static struct attribute * g[] = {
&state_attr.attr,
#ifdef CONFIG_PM_TRACE
@ -991,6 +1019,9 @@ static struct attribute * g[] = {
#endif
#ifdef CONFIG_FREEZER
&pm_freeze_timeout_attr.attr,
#endif
#if defined(CONFIG_SUSPEND) || defined(CONFIG_HIBERNATION)
&freeze_filesystems_attr.attr,
#endif
NULL,
};

View File

@ -18,6 +18,10 @@ struct swsusp_info {
unsigned long size;
} __aligned(PAGE_SIZE);
#if defined(CONFIG_SUSPEND) || defined(CONFIG_HIBERNATION)
extern bool filesystem_freeze_enabled;
#endif
#ifdef CONFIG_HIBERNATION
/* kernel/power/snapshot.c */
extern void __init hibernate_reserved_size_init(void);

View File

@ -30,6 +30,7 @@
#include <trace/events/power.h>
#include <linux/compiler.h>
#include <linux/moduleparam.h>
#include <linux/fs.h>
#include "power.h"
@ -374,6 +375,8 @@ static int suspend_prepare(suspend_state_t state)
if (error)
goto Restore;
if (filesystem_freeze_enabled)
filesystems_freeze();
trace_suspend_resume(TPS("freeze_processes"), 0, true);
error = suspend_freeze_processes();
trace_suspend_resume(TPS("freeze_processes"), 0, false);
@ -550,6 +553,7 @@ int suspend_devices_and_enter(suspend_state_t state)
static void suspend_finish(void)
{
suspend_thaw_processes();
filesystems_thaw();
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
}
@ -588,6 +592,8 @@ static int enter_state(suspend_state_t state)
ksys_sync_helper();
trace_suspend_resume(TPS("sync_filesystems"), 0, false);
}
if (filesystem_freeze_enabled)
filesystems_freeze();
pm_pr_dbg("Preparing system for sleep (%s)\n", mem_sleep_labels[state]);
pm_suspend_clear_flags();
@ -609,6 +615,7 @@ static int enter_state(suspend_state_t state)
pm_pr_dbg("Finishing wakeup.\n");
suspend_finish();
Unlock:
filesystems_thaw();
mutex_unlock(&system_transition_mutex);
return error;
}