Introduce a freeze function, which iterates superblocks in reverse order freezing filesystems. The indicator a filesystem is freezable is either possessing a s_bdev or a freeze_super method. So this can be used in efivarfs, whether the freeze is for hibernate is also passed in via the new FREEZE_FOR_HIBERNATE flag. Thawing is done opposite to freezing (so superblock traversal in regular order) and the whole thing is plumbed into power management. The original ksys_sync() is preserved so the whole freezing step is optional (if it fails we're no worse off than we are today) so it doesn't inhibit suspend/hibernate if there's a failure. Signed-off-by: James Bottomley <James.Bottomley@xxxxxxxxxxxxxxxxxxxxx> --- fs/super.c | 61 ++++++++++++++++++++++++++++++++++++++++ include/linux/fs.h | 5 ++++ kernel/power/hibernate.c | 12 ++++++++ kernel/power/suspend.c | 4 +++ 4 files changed, 82 insertions(+) diff --git a/fs/super.c b/fs/super.c index 76785509d906..b4b0986414b0 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1461,6 +1461,67 @@ static struct super_block *get_bdev_super(struct block_device *bdev) return sb; } +/* + * Kernel freezing and thawing is only done in the power management + * subsystem and is thus single threaded (so we don't have to worry + * here about multiple calls to filesystems_freeze/thaw(). + */ + +static int freeze_flags; + +static void filesystems_freeze_callback(struct super_block *sb) +{ + /* errors don't fail suspend so ignore them */ + if (sb->s_op->freeze_super) + sb->s_op->freeze_super(sb, FREEZE_MAY_NEST + | FREEZE_HOLDER_KERNEL + | freeze_flags); + else if (sb->s_bdev) + freeze_super(sb, FREEZE_MAY_NEST | FREEZE_HOLDER_KERNEL + | freeze_flags); + else { + pr_info("Ignoring filesystem %s\n", sb->s_type->name); + return; + } + + pr_info("frozen %s, now syncing block ...", sb->s_type->name); + sync_blockdev(sb->s_bdev); + pr_info("done."); +} + +/** + * filesystems_freeze - freeze callback for power management + * + * Freeze all active filesystems (in reverse superblock order) + */ +void filesystems_freeze(bool for_hibernate) +{ + freeze_flags = for_hibernate ? FREEZE_FOR_HIBERNATE : 0; + __iterate_supers_rev(filesystems_freeze_callback); +} + +static void filesystems_thaw_callback(struct super_block *sb) +{ + if (sb->s_op->thaw_super) + sb->s_op->thaw_super(sb, FREEZE_MAY_NEST + | FREEZE_HOLDER_KERNEL + | freeze_flags); + else if (sb->s_bdev) + thaw_super(sb, FREEZE_MAY_NEST | FREEZE_HOLDER_KERNEL + | freeze_flags); +} + +/** + * filesystems_thaw - thaw callback for power management + * + * Thaw all active filesystems (in forward superblock order) + */ +void filesystems_thaw(bool for_hibernate) +{ + freeze_flags = for_hibernate ? FREEZE_FOR_HIBERNATE : 0; + __iterate_supers(filesystems_thaw_callback); +} + /** * fs_bdev_freeze - freeze owning filesystem of block device * @bdev: block device diff --git a/include/linux/fs.h b/include/linux/fs.h index cbbb704eff74..de154e9379ec 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2272,6 +2272,7 @@ extern loff_t vfs_dedupe_file_range_one(struct file *src_file, loff_t src_pos, * @FREEZE_HOLDER_KERNEL: kernel wants to freeze or thaw filesystem * @FREEZE_HOLDER_USERSPACE: userspace wants to freeze or thaw filesystem * @FREEZE_MAY_NEST: whether nesting freeze and thaw requests is allowed + * @FREEZE_FOR_HIBERNATE: set if freeze is from power management hibernate * * Indicate who the owner of the freeze or thaw request is and whether * the freeze needs to be exclusive or can nest. @@ -2285,6 +2286,7 @@ enum freeze_holder { FREEZE_HOLDER_KERNEL = (1U << 0), FREEZE_HOLDER_USERSPACE = (1U << 1), FREEZE_MAY_NEST = (1U << 2), + FREEZE_FOR_HIBERNATE = (1U << 3), }; struct super_operations { @@ -3919,4 +3921,7 @@ static inline bool vfs_empty_path(int dfd, const char __user *path) int generic_atomic_write_valid(struct kiocb *iocb, struct iov_iter *iter); +void filesystems_freeze(bool for_hibernate); +void filesystems_thaw(bool for_hibernate); + #endif /* _LINUX_FS_H */ diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 10a01af63a80..fc2106e6685a 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -778,7 +778,12 @@ int hibernate(void) ksys_sync_helper(); + pr_info("about to freeze filesystems\n"); + filesystems_freeze(true); + pr_info("filesystem freeze done\n"); + error = freeze_processes(); + pr_info("process freeze done\n"); if (error) goto Exit; @@ -788,7 +793,9 @@ int hibernate(void) if (error) goto Thaw; + pr_info("About to create snapshot\n"); error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM); + pr_info("snapshot done\n"); if (error || freezer_test_done) goto Free_bitmaps; @@ -842,6 +849,8 @@ int hibernate(void) } thaw_processes(); + filesystems_thaw(true); + /* Don't bother checking whether freezer_test_done is true */ freezer_test_done = false; Exit: @@ -939,6 +948,8 @@ int hibernate_quiet_exec(int (*func)(void *data), void *data) thaw_processes(); + filesystems_thaw(true); + exit: pm_notifier_call_chain(PM_POST_HIBERNATION); @@ -1041,6 +1052,7 @@ static int software_resume(void) error = load_image_and_restore(); thaw_processes(); + filesystems_thaw(true); Finish: pm_notifier_call_chain(PM_POST_RESTORE); Restore: diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 09f8397bae15..34cc5b0c408c 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -544,6 +544,7 @@ int suspend_devices_and_enter(suspend_state_t state) static void suspend_finish(void) { suspend_thaw_processes(); + filesystems_thaw(false); pm_notifier_call_chain(PM_POST_SUSPEND); pm_restore_console(); } @@ -581,6 +582,7 @@ static int enter_state(suspend_state_t state) trace_suspend_resume(TPS("sync_filesystems"), 0, true); ksys_sync_helper(); trace_suspend_resume(TPS("sync_filesystems"), 0, false); + filesystems_freeze(false); } pm_pr_dbg("Preparing system for sleep (%s)\n", mem_sleep_labels[state]); @@ -603,6 +605,8 @@ static int enter_state(suspend_state_t state) pm_pr_dbg("Finishing wakeup.\n"); suspend_finish(); Unlock: + if (sync_on_suspend_enabled) + filesystems_thaw(false); mutex_unlock(&system_transition_mutex); return error; } -- 2.43.0