Merge patch series "Extend freeze support to suspend and hibernate"

Christian Brauner <brauner@kernel.org> says:

Add the necessary infrastructure changes to support freezing for suspend
and hibernate. This should all that's needed to wire up power.

* patches from https://lore.kernel.org/r/20250329-work-freeze-v2-0-a47af37ecc3d@kernel.org:
  super: add filesystem freezing helpers for suspend and hibernate
  gfs2: pass through holder from the VFS for freeze/thaw
  super: use common iterator (Part 2)
  super: use a common iterator (Part 1)
  super: skip dying superblocks early
  super: simplify user_get_super()
  super: remove pointless s_root checks

Link: https://lore.kernel.org/r/20250329-work-freeze-v2-0-a47af37ecc3d@kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
Christian Brauner 2025-03-29 09:44:56 +01:00
commit 6e5af8e3ca
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2
10 changed files with 292 additions and 130 deletions

View File

@ -2271,12 +2271,12 @@ int f2fs_resize_fs(struct file *filp, __u64 block_count)
if (err)
return err;
err = freeze_super(sbi->sb, FREEZE_HOLDER_USERSPACE);
err = freeze_super(sbi->sb, FREEZE_HOLDER_USERSPACE, NULL);
if (err)
return err;
if (f2fs_readonly(sbi->sb)) {
err = thaw_super(sbi->sb, FREEZE_HOLDER_USERSPACE);
err = thaw_super(sbi->sb, FREEZE_HOLDER_USERSPACE, NULL);
if (err)
return err;
return -EROFS;
@ -2333,6 +2333,6 @@ int f2fs_resize_fs(struct file *filp, __u64 block_count)
out_err:
f2fs_up_write(&sbi->cp_global_sem);
f2fs_up_write(&sbi->gc_lock);
thaw_super(sbi->sb, FREEZE_HOLDER_USERSPACE);
thaw_super(sbi->sb, FREEZE_HOLDER_USERSPACE, NULL);
return err;
}

View File

@ -674,7 +674,7 @@ static int gfs2_sync_fs(struct super_block *sb, int wait)
return sdp->sd_log_error;
}
static int gfs2_do_thaw(struct gfs2_sbd *sdp)
static int gfs2_do_thaw(struct gfs2_sbd *sdp, enum freeze_holder who, const void *freeze_owner)
{
struct super_block *sb = sdp->sd_vfs;
int error;
@ -682,7 +682,7 @@ static int gfs2_do_thaw(struct gfs2_sbd *sdp)
error = gfs2_freeze_lock_shared(sdp);
if (error)
goto fail;
error = thaw_super(sb, FREEZE_HOLDER_USERSPACE);
error = thaw_super(sb, who, freeze_owner);
if (!error)
return 0;
@ -703,14 +703,14 @@ void gfs2_freeze_func(struct work_struct *work)
if (test_bit(SDF_FROZEN, &sdp->sd_flags))
goto freeze_failed;
error = freeze_super(sb, FREEZE_HOLDER_USERSPACE);
error = freeze_super(sb, FREEZE_HOLDER_USERSPACE, NULL);
if (error)
goto freeze_failed;
gfs2_freeze_unlock(sdp);
set_bit(SDF_FROZEN, &sdp->sd_flags);
error = gfs2_do_thaw(sdp);
error = gfs2_do_thaw(sdp, FREEZE_HOLDER_USERSPACE, NULL);
if (error)
goto out;
@ -728,10 +728,13 @@ void gfs2_freeze_func(struct work_struct *work)
/**
* gfs2_freeze_super - prevent further writes to the filesystem
* @sb: the VFS structure for the filesystem
* @who: freeze flags
* @freeze_owner: owner of the freeze
*
*/
static int gfs2_freeze_super(struct super_block *sb, enum freeze_holder who)
static int gfs2_freeze_super(struct super_block *sb, enum freeze_holder who,
const void *freeze_owner)
{
struct gfs2_sbd *sdp = sb->s_fs_info;
int error;
@ -744,7 +747,7 @@ static int gfs2_freeze_super(struct super_block *sb, enum freeze_holder who)
}
for (;;) {
error = freeze_super(sb, FREEZE_HOLDER_USERSPACE);
error = freeze_super(sb, who, freeze_owner);
if (error) {
fs_info(sdp, "GFS2: couldn't freeze filesystem: %d\n",
error);
@ -758,7 +761,7 @@ static int gfs2_freeze_super(struct super_block *sb, enum freeze_holder who)
break;
}
error = gfs2_do_thaw(sdp);
error = gfs2_do_thaw(sdp, who, freeze_owner);
if (error)
goto out;
@ -796,10 +799,13 @@ static int gfs2_freeze_fs(struct super_block *sb)
/**
* gfs2_thaw_super - reallow writes to the filesystem
* @sb: the VFS structure for the filesystem
* @who: freeze flags
* @freeze_owner: owner of the freeze
*
*/
static int gfs2_thaw_super(struct super_block *sb, enum freeze_holder who)
static int gfs2_thaw_super(struct super_block *sb, enum freeze_holder who,
const void *freeze_owner)
{
struct gfs2_sbd *sdp = sb->s_fs_info;
int error;
@ -814,7 +820,7 @@ static int gfs2_thaw_super(struct super_block *sb, enum freeze_holder who)
atomic_inc(&sb->s_active);
gfs2_freeze_unlock(sdp);
error = gfs2_do_thaw(sdp);
error = gfs2_do_thaw(sdp, who, freeze_owner);
if (!error) {
clear_bit(SDF_FREEZE_INITIATOR, &sdp->sd_flags);

View File

@ -174,10 +174,10 @@ static ssize_t freeze_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
switch (n) {
case 0:
error = thaw_super(sdp->sd_vfs, FREEZE_HOLDER_USERSPACE);
error = thaw_super(sdp->sd_vfs, FREEZE_HOLDER_USERSPACE, NULL);
break;
case 1:
error = freeze_super(sdp->sd_vfs, FREEZE_HOLDER_USERSPACE);
error = freeze_super(sdp->sd_vfs, FREEZE_HOLDER_USERSPACE, NULL);
break;
default:
return -EINVAL;

View File

@ -396,8 +396,8 @@ static int ioctl_fsfreeze(struct file *filp)
/* Freeze */
if (sb->s_op->freeze_super)
return sb->s_op->freeze_super(sb, FREEZE_HOLDER_USERSPACE);
return freeze_super(sb, FREEZE_HOLDER_USERSPACE);
return sb->s_op->freeze_super(sb, FREEZE_HOLDER_USERSPACE, NULL);
return freeze_super(sb, FREEZE_HOLDER_USERSPACE, NULL);
}
static int ioctl_fsthaw(struct file *filp)
@ -409,8 +409,8 @@ static int ioctl_fsthaw(struct file *filp)
/* Thaw */
if (sb->s_op->thaw_super)
return sb->s_op->thaw_super(sb, FREEZE_HOLDER_USERSPACE);
return thaw_super(sb, FREEZE_HOLDER_USERSPACE);
return sb->s_op->thaw_super(sb, FREEZE_HOLDER_USERSPACE, NULL);
return thaw_super(sb, FREEZE_HOLDER_USERSPACE, NULL);
}
static int ioctl_file_dedupe_range(struct file *file,

View File

@ -39,7 +39,8 @@
#include <uapi/linux/mount.h>
#include "internal.h"
static int thaw_super_locked(struct super_block *sb, enum freeze_holder who);
static int thaw_super_locked(struct super_block *sb, enum freeze_holder who,
const void *freeze_owner);
static LIST_HEAD(super_blocks);
static DEFINE_SPINLOCK(sb_lock);
@ -887,52 +888,48 @@ void drop_super_exclusive(struct super_block *sb)
}
EXPORT_SYMBOL(drop_super_exclusive);
static void __iterate_supers(void (*f)(struct super_block *))
enum super_iter_flags_t {
SUPER_ITER_EXCL = (1U << 0),
SUPER_ITER_UNLOCKED = (1U << 1),
SUPER_ITER_REVERSE = (1U << 2),
};
static inline struct super_block *first_super(enum super_iter_flags_t flags)
{
if (flags & SUPER_ITER_REVERSE)
return list_last_entry(&super_blocks, struct super_block, s_list);
return list_first_entry(&super_blocks, struct super_block, s_list);
}
static inline struct super_block *next_super(struct super_block *sb,
enum super_iter_flags_t flags)
{
if (flags & SUPER_ITER_REVERSE)
return list_prev_entry(sb, s_list);
return list_next_entry(sb, s_list);
}
static void __iterate_supers(void (*f)(struct super_block *, void *), void *arg,
enum super_iter_flags_t flags)
{
struct super_block *sb, *p = NULL;
bool excl = flags & SUPER_ITER_EXCL;
spin_lock(&sb_lock);
list_for_each_entry(sb, &super_blocks, s_list) {
guard(spinlock)(&sb_lock);
for (sb = first_super(flags);
!list_entry_is_head(sb, &super_blocks, s_list);
sb = next_super(sb, flags)) {
if (super_flags(sb, SB_DYING))
continue;
sb->s_count++;
spin_unlock(&sb_lock);
f(sb);
spin_lock(&sb_lock);
if (p)
__put_super(p);
p = sb;
}
if (p)
__put_super(p);
spin_unlock(&sb_lock);
}
/**
* iterate_supers - call function for all active superblocks
* @f: function to call
* @arg: argument to pass to it
*
* Scans the superblock list and calls given function, passing it
* locked superblock and given argument.
*/
void iterate_supers(void (*f)(struct super_block *, void *), void *arg)
{
struct super_block *sb, *p = NULL;
spin_lock(&sb_lock);
list_for_each_entry(sb, &super_blocks, s_list) {
bool locked;
sb->s_count++;
spin_unlock(&sb_lock);
locked = super_lock_shared(sb);
if (locked) {
if (sb->s_root)
f(sb, arg);
super_unlock_shared(sb);
if (flags & SUPER_ITER_UNLOCKED) {
f(sb, arg);
} else if (super_lock(sb, excl)) {
f(sb, arg);
super_unlock(sb, excl);
}
spin_lock(&sb_lock);
@ -942,7 +939,11 @@ void iterate_supers(void (*f)(struct super_block *, void *), void *arg)
}
if (p)
__put_super(p);
spin_unlock(&sb_lock);
}
void iterate_supers(void (*f)(struct super_block *, void *), void *arg)
{
__iterate_supers(f, arg, 0);
}
/**
@ -963,15 +964,15 @@ void iterate_supers_type(struct file_system_type *type,
hlist_for_each_entry(sb, &type->fs_supers, s_instances) {
bool locked;
if (super_flags(sb, SB_DYING))
continue;
sb->s_count++;
spin_unlock(&sb_lock);
locked = super_lock_shared(sb);
if (locked) {
if (sb->s_root)
f(sb, arg);
super_unlock_shared(sb);
}
if (locked)
f(sb, arg);
spin_lock(&sb_lock);
if (p)
@ -991,23 +992,21 @@ struct super_block *user_get_super(dev_t dev, bool excl)
spin_lock(&sb_lock);
list_for_each_entry(sb, &super_blocks, s_list) {
if (sb->s_dev == dev) {
bool locked;
bool locked;
sb->s_count++;
spin_unlock(&sb_lock);
/* still alive? */
locked = super_lock(sb, excl);
if (locked) {
if (sb->s_root)
return sb;
super_unlock(sb, excl);
}
/* nope, got unmounted */
spin_lock(&sb_lock);
__put_super(sb);
break;
}
if (sb->s_dev != dev)
continue;
sb->s_count++;
spin_unlock(&sb_lock);
locked = super_lock(sb, excl);
if (locked)
return sb;
spin_lock(&sb_lock);
__put_super(sb);
break;
}
spin_unlock(&sb_lock);
return NULL;
@ -1111,11 +1110,9 @@ int reconfigure_super(struct fs_context *fc)
return retval;
}
static void do_emergency_remount_callback(struct super_block *sb)
static void do_emergency_remount_callback(struct super_block *sb, void *unused)
{
bool locked = super_lock_excl(sb);
if (locked && sb->s_root && sb->s_bdev && !sb_rdonly(sb)) {
if (sb->s_bdev && !sb_rdonly(sb)) {
struct fs_context *fc;
fc = fs_context_for_reconfigure(sb->s_root,
@ -1126,13 +1123,12 @@ static void do_emergency_remount_callback(struct super_block *sb)
put_fs_context(fc);
}
}
if (locked)
super_unlock_excl(sb);
}
static void do_emergency_remount(struct work_struct *work)
{
__iterate_supers(do_emergency_remount_callback);
__iterate_supers(do_emergency_remount_callback, NULL,
SUPER_ITER_EXCL | SUPER_ITER_REVERSE);
kfree(work);
printk("Emergency Remount complete\n");
}
@ -1148,24 +1144,18 @@ void emergency_remount(void)
}
}
static void do_thaw_all_callback(struct super_block *sb)
static void do_thaw_all_callback(struct super_block *sb, void *unused)
{
bool locked = super_lock_excl(sb);
if (locked && sb->s_root) {
if (IS_ENABLED(CONFIG_BLOCK))
while (sb->s_bdev && !bdev_thaw(sb->s_bdev))
pr_warn("Emergency Thaw on %pg\n", sb->s_bdev);
thaw_super_locked(sb, FREEZE_HOLDER_USERSPACE);
return;
}
if (locked)
super_unlock_excl(sb);
if (IS_ENABLED(CONFIG_BLOCK))
while (sb->s_bdev && !bdev_thaw(sb->s_bdev))
pr_warn("Emergency Thaw on %pg\n", sb->s_bdev);
thaw_super_locked(sb, FREEZE_HOLDER_USERSPACE, NULL);
return;
}
static void do_thaw_all(struct work_struct *work)
{
__iterate_supers(do_thaw_all_callback);
__iterate_supers(do_thaw_all_callback, NULL, SUPER_ITER_EXCL);
kfree(work);
printk(KERN_WARNING "Emergency Thaw complete\n");
}
@ -1186,6 +1176,66 @@ void emergency_thaw_all(void)
}
}
static inline bool get_active_super(struct super_block *sb)
{
bool active = false;
if (super_lock_excl(sb)) {
active = atomic_inc_not_zero(&sb->s_active);
super_unlock_excl(sb);
}
return active;
}
static const char *filesystems_freeze_ptr = "filesystems_freeze";
static void filesystems_freeze_callback(struct super_block *sb, void *unused)
{
if (!sb->s_op->freeze_fs && !sb->s_op->freeze_super)
return;
if (!get_active_super(sb))
return;
if (sb->s_op->freeze_super)
sb->s_op->freeze_super(sb, FREEZE_EXCL | FREEZE_HOLDER_KERNEL,
filesystems_freeze_ptr);
else
freeze_super(sb, FREEZE_EXCL | FREEZE_HOLDER_KERNEL,
filesystems_freeze_ptr);
deactivate_super(sb);
}
void filesystems_freeze(void)
{
__iterate_supers(filesystems_freeze_callback, NULL,
SUPER_ITER_UNLOCKED | SUPER_ITER_REVERSE);
}
static void filesystems_thaw_callback(struct super_block *sb, void *unused)
{
if (!sb->s_op->freeze_fs && !sb->s_op->freeze_super)
return;
if (!get_active_super(sb))
return;
if (sb->s_op->thaw_super)
sb->s_op->thaw_super(sb, FREEZE_EXCL | FREEZE_HOLDER_KERNEL,
filesystems_freeze_ptr);
else
thaw_super(sb, FREEZE_EXCL | FREEZE_HOLDER_KERNEL,
filesystems_freeze_ptr);
deactivate_super(sb);
}
void filesystems_thaw(void)
{
__iterate_supers(filesystems_thaw_callback, NULL, SUPER_ITER_UNLOCKED);
}
static DEFINE_IDA(unnamed_dev_ida);
/**
@ -1479,10 +1529,10 @@ static int fs_bdev_freeze(struct block_device *bdev)
if (sb->s_op->freeze_super)
error = sb->s_op->freeze_super(sb,
FREEZE_MAY_NEST | FREEZE_HOLDER_USERSPACE);
FREEZE_MAY_NEST | FREEZE_HOLDER_USERSPACE, NULL);
else
error = freeze_super(sb,
FREEZE_MAY_NEST | FREEZE_HOLDER_USERSPACE);
FREEZE_MAY_NEST | FREEZE_HOLDER_USERSPACE, NULL);
if (!error)
error = sync_blockdev(bdev);
deactivate_super(sb);
@ -1528,10 +1578,10 @@ static int fs_bdev_thaw(struct block_device *bdev)
if (sb->s_op->thaw_super)
error = sb->s_op->thaw_super(sb,
FREEZE_MAY_NEST | FREEZE_HOLDER_USERSPACE);
FREEZE_MAY_NEST | FREEZE_HOLDER_USERSPACE, NULL);
else
error = thaw_super(sb,
FREEZE_MAY_NEST | FREEZE_HOLDER_USERSPACE);
FREEZE_MAY_NEST | FREEZE_HOLDER_USERSPACE, NULL);
deactivate_super(sb);
return error;
}
@ -1903,7 +1953,7 @@ static int wait_for_partially_frozen(struct super_block *sb)
}
#define FREEZE_HOLDERS (FREEZE_HOLDER_KERNEL | FREEZE_HOLDER_USERSPACE)
#define FREEZE_FLAGS (FREEZE_HOLDERS | FREEZE_MAY_NEST)
#define FREEZE_FLAGS (FREEZE_HOLDERS | FREEZE_MAY_NEST | FREEZE_EXCL)
static inline int freeze_inc(struct super_block *sb, enum freeze_holder who)
{
@ -1929,11 +1979,34 @@ static inline int freeze_dec(struct super_block *sb, enum freeze_holder who)
return sb->s_writers.freeze_kcount + sb->s_writers.freeze_ucount;
}
static inline bool may_freeze(struct super_block *sb, enum freeze_holder who)
static inline bool may_freeze(struct super_block *sb, enum freeze_holder who,
const void *freeze_owner)
{
lockdep_assert_held(&sb->s_umount);
WARN_ON_ONCE((who & ~FREEZE_FLAGS));
WARN_ON_ONCE(hweight32(who & FREEZE_HOLDERS) > 1);
if (who & FREEZE_EXCL) {
if (WARN_ON_ONCE(!(who & FREEZE_HOLDER_KERNEL)))
return false;
if (WARN_ON_ONCE(who & ~(FREEZE_EXCL | FREEZE_HOLDER_KERNEL)))
return false;
if (WARN_ON_ONCE(!freeze_owner))
return false;
/* This freeze already has a specific owner. */
if (sb->s_writers.freeze_owner)
return false;
/*
* This is already frozen multiple times so we're just
* going to take a reference count and mark the freeze as
* being owned by the caller.
*/
if (sb->s_writers.freeze_kcount + sb->s_writers.freeze_ucount)
sb->s_writers.freeze_owner = freeze_owner;
return true;
}
if (who & FREEZE_HOLDER_KERNEL)
return (who & FREEZE_MAY_NEST) ||
sb->s_writers.freeze_kcount == 0;
@ -1943,10 +2016,61 @@ static inline bool may_freeze(struct super_block *sb, enum freeze_holder who)
return false;
}
static inline bool may_unfreeze(struct super_block *sb, enum freeze_holder who,
const void *freeze_owner)
{
lockdep_assert_held(&sb->s_umount);
WARN_ON_ONCE((who & ~FREEZE_FLAGS));
WARN_ON_ONCE(hweight32(who & FREEZE_HOLDERS) > 1);
if (who & FREEZE_EXCL) {
if (WARN_ON_ONCE(!(who & FREEZE_HOLDER_KERNEL)))
return false;
if (WARN_ON_ONCE(who & ~(FREEZE_EXCL | FREEZE_HOLDER_KERNEL)))
return false;
if (WARN_ON_ONCE(!freeze_owner))
return false;
if (WARN_ON_ONCE(sb->s_writers.freeze_kcount == 0))
return false;
/* This isn't exclusively frozen. */
if (!sb->s_writers.freeze_owner)
return false;
/* This isn't exclusively frozen by us. */
if (sb->s_writers.freeze_owner != freeze_owner)
return false;
/*
* This is still frozen multiple times so we're just
* going to drop our reference count and undo our
* exclusive freeze.
*/
if ((sb->s_writers.freeze_kcount + sb->s_writers.freeze_ucount) > 1)
sb->s_writers.freeze_owner = NULL;
return true;
}
if (who & FREEZE_HOLDER_KERNEL) {
/*
* Someone's trying to steal the reference belonging to
* @sb->s_writers.freeze_owner.
*/
if (sb->s_writers.freeze_kcount == 1 &&
sb->s_writers.freeze_owner)
return false;
return sb->s_writers.freeze_kcount > 0;
}
if (who & FREEZE_HOLDER_USERSPACE)
return sb->s_writers.freeze_ucount > 0;
return false;
}
/**
* freeze_super - lock the filesystem and force it into a consistent state
* @sb: the super to lock
* @who: context that wants to freeze
* @freeze_owner: owner of the freeze
*
* Syncs the super to make sure the filesystem is consistent and calls the fs's
* freeze_fs. Subsequent calls to this without first thawing the fs may return
@ -1998,7 +2122,7 @@ static inline bool may_freeze(struct super_block *sb, enum freeze_holder who)
* Return: If the freeze was successful zero is returned. If the freeze
* failed a negative error code is returned.
*/
int freeze_super(struct super_block *sb, enum freeze_holder who)
int freeze_super(struct super_block *sb, enum freeze_holder who, const void *freeze_owner)
{
int ret;
@ -2010,7 +2134,7 @@ int freeze_super(struct super_block *sb, enum freeze_holder who)
retry:
if (sb->s_writers.frozen == SB_FREEZE_COMPLETE) {
if (may_freeze(sb, who))
if (may_freeze(sb, who, freeze_owner))
ret = !!WARN_ON_ONCE(freeze_inc(sb, who) == 1);
else
ret = -EBUSY;
@ -2032,6 +2156,7 @@ int freeze_super(struct super_block *sb, enum freeze_holder who)
if (sb_rdonly(sb)) {
/* Nothing to do really... */
WARN_ON_ONCE(freeze_inc(sb, who) > 1);
sb->s_writers.freeze_owner = freeze_owner;
sb->s_writers.frozen = SB_FREEZE_COMPLETE;
wake_up_var(&sb->s_writers.frozen);
super_unlock_excl(sb);
@ -2079,6 +2204,7 @@ int freeze_super(struct super_block *sb, enum freeze_holder who)
* when frozen is set to SB_FREEZE_COMPLETE, and for thaw_super().
*/
WARN_ON_ONCE(freeze_inc(sb, who) > 1);
sb->s_writers.freeze_owner = freeze_owner;
sb->s_writers.frozen = SB_FREEZE_COMPLETE;
wake_up_var(&sb->s_writers.frozen);
lockdep_sb_freeze_release(sb);
@ -2093,13 +2219,17 @@ EXPORT_SYMBOL(freeze_super);
* removes that state without releasing the other state or unlocking the
* filesystem.
*/
static int thaw_super_locked(struct super_block *sb, enum freeze_holder who)
static int thaw_super_locked(struct super_block *sb, enum freeze_holder who,
const void *freeze_owner)
{
int error = -EINVAL;
if (sb->s_writers.frozen != SB_FREEZE_COMPLETE)
goto out_unlock;
if (!may_unfreeze(sb, who, freeze_owner))
goto out_unlock;
/*
* All freezers share a single active reference.
* So just unlock in case there are any left.
@ -2109,6 +2239,7 @@ static int thaw_super_locked(struct super_block *sb, enum freeze_holder who)
if (sb_rdonly(sb)) {
sb->s_writers.frozen = SB_UNFROZEN;
sb->s_writers.freeze_owner = NULL;
wake_up_var(&sb->s_writers.frozen);
goto out_deactivate;
}
@ -2126,6 +2257,7 @@ static int thaw_super_locked(struct super_block *sb, enum freeze_holder who)
}
sb->s_writers.frozen = SB_UNFROZEN;
sb->s_writers.freeze_owner = NULL;
wake_up_var(&sb->s_writers.frozen);
sb_freeze_unlock(sb, SB_FREEZE_FS);
out_deactivate:
@ -2141,6 +2273,7 @@ static int thaw_super_locked(struct super_block *sb, enum freeze_holder who)
* thaw_super -- unlock filesystem
* @sb: the super to thaw
* @who: context that wants to freeze
* @freeze_owner: owner of the freeze
*
* Unlocks the filesystem and marks it writeable again after freeze_super()
* if there are no remaining freezes on the filesystem.
@ -2154,13 +2287,14 @@ static int thaw_super_locked(struct super_block *sb, enum freeze_holder who)
* have been frozen through the block layer via multiple block devices.
* The filesystem remains frozen until all block devices are unfrozen.
*/
int thaw_super(struct super_block *sb, enum freeze_holder who)
int thaw_super(struct super_block *sb, enum freeze_holder who,
const void *freeze_owner)
{
if (!super_lock_excl(sb)) {
WARN_ON_ONCE("Dying superblock while thawing!");
return -EINVAL;
}
return thaw_super_locked(sb, who);
return thaw_super_locked(sb, who, freeze_owner);
}
EXPORT_SYMBOL(thaw_super);

View File

@ -123,7 +123,7 @@ xchk_fsfreeze(
{
int error;
error = freeze_super(sc->mp->m_super, FREEZE_HOLDER_KERNEL);
error = freeze_super(sc->mp->m_super, FREEZE_HOLDER_KERNEL, NULL);
trace_xchk_fsfreeze(sc, error);
return error;
}
@ -135,7 +135,7 @@ xchk_fsthaw(
int error;
/* This should always succeed, we have a kernel freeze */
error = thaw_super(sc->mp->m_super, FREEZE_HOLDER_KERNEL);
error = thaw_super(sc->mp->m_super, FREEZE_HOLDER_KERNEL, NULL);
trace_xchk_fsthaw(sc, error);
return error;
}

View File

@ -127,7 +127,7 @@ xfs_dax_notify_failure_freeze(
struct super_block *sb = mp->m_super;
int error;
error = freeze_super(sb, FREEZE_HOLDER_KERNEL);
error = freeze_super(sb, FREEZE_HOLDER_KERNEL, NULL);
if (error)
xfs_emerg(mp, "already frozen by kernel, err=%d", error);
@ -143,7 +143,7 @@ xfs_dax_notify_failure_thaw(
int error;
if (kernel_frozen) {
error = thaw_super(sb, FREEZE_HOLDER_KERNEL);
error = thaw_super(sb, FREEZE_HOLDER_KERNEL, NULL);
if (error)
xfs_emerg(mp, "still frozen after notify failure, err=%d",
error);
@ -153,7 +153,7 @@ xfs_dax_notify_failure_thaw(
* Also thaw userspace call anyway because the device is about to be
* removed immediately.
*/
thaw_super(sb, FREEZE_HOLDER_USERSPACE);
thaw_super(sb, FREEZE_HOLDER_USERSPACE, NULL);
}
static int

View File

@ -1307,6 +1307,7 @@ struct sb_writers {
unsigned short frozen; /* Is sb frozen? */
int freeze_kcount; /* How many kernel freeze requests? */
int freeze_ucount; /* How many userspace freeze requests? */
const void *freeze_owner; /* Owner of the freeze */
struct percpu_rw_semaphore rw_sem[SB_FREEZE_LEVELS];
};
@ -1780,7 +1781,7 @@ static inline void __sb_end_write(struct super_block *sb, int level)
static inline void __sb_start_write(struct super_block *sb, int level)
{
percpu_down_read(sb->s_writers.rw_sem + level - 1);
percpu_down_read_freezable(sb->s_writers.rw_sem + level - 1, true);
}
static inline bool __sb_start_write_trylock(struct super_block *sb, int level)
@ -2269,6 +2270,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_EXCL: a freeze that can only be undone by the owner
*
* Indicate who the owner of the freeze or thaw request is and whether
* the freeze needs to be exclusive or can nest.
@ -2282,6 +2284,7 @@ enum freeze_holder {
FREEZE_HOLDER_KERNEL = (1U << 0),
FREEZE_HOLDER_USERSPACE = (1U << 1),
FREEZE_MAY_NEST = (1U << 2),
FREEZE_EXCL = (1U << 3),
};
struct super_operations {
@ -2295,9 +2298,9 @@ struct super_operations {
void (*evict_inode) (struct inode *);
void (*put_super) (struct super_block *);
int (*sync_fs)(struct super_block *sb, int wait);
int (*freeze_super) (struct super_block *, enum freeze_holder who);
int (*freeze_super) (struct super_block *, enum freeze_holder who, const void *owner);
int (*freeze_fs) (struct super_block *);
int (*thaw_super) (struct super_block *, enum freeze_holder who);
int (*thaw_super) (struct super_block *, enum freeze_holder who, const void *owner);
int (*unfreeze_fs) (struct super_block *);
int (*statfs) (struct dentry *, struct kstatfs *);
int (*remount_fs) (struct super_block *, int *, char *);
@ -2705,8 +2708,10 @@ extern int unregister_filesystem(struct file_system_type *);
extern int vfs_statfs(const struct path *, struct kstatfs *);
extern int user_statfs(const char __user *, struct kstatfs *);
extern int fd_statfs(int, struct kstatfs *);
int freeze_super(struct super_block *super, enum freeze_holder who);
int thaw_super(struct super_block *super, enum freeze_holder who);
int freeze_super(struct super_block *super, enum freeze_holder who,
const void *freeze_owner);
int thaw_super(struct super_block *super, enum freeze_holder who,
const void *freeze_owner);
extern __printf(2, 3)
int super_setup_bdi_name(struct super_block *sb, char *fmt, ...);
extern int super_setup_bdi(struct super_block *sb);
@ -3515,9 +3520,11 @@ extern void put_filesystem(struct file_system_type *fs);
extern struct file_system_type *get_fs_type(const char *name);
extern void drop_super(struct super_block *sb);
extern void drop_super_exclusive(struct super_block *sb);
extern void iterate_supers(void (*)(struct super_block *, void *), void *);
extern void iterate_supers(void (*f)(struct super_block *, void *), void *arg);
extern void iterate_supers_type(struct file_system_type *,
void (*)(struct super_block *, void *), void *);
void filesystems_freeze(void);
void filesystems_thaw(void);
extern int dcache_dir_open(struct inode *, struct file *);
extern int dcache_dir_close(struct inode *, struct file *);

View File

@ -43,9 +43,10 @@ is_static struct percpu_rw_semaphore name = { \
#define DEFINE_STATIC_PERCPU_RWSEM(name) \
__DEFINE_PERCPU_RWSEM(name, static)
extern bool __percpu_down_read(struct percpu_rw_semaphore *, bool);
extern bool __percpu_down_read(struct percpu_rw_semaphore *, bool, bool);
static inline void percpu_down_read(struct percpu_rw_semaphore *sem)
static inline void percpu_down_read_internal(struct percpu_rw_semaphore *sem,
bool freezable)
{
might_sleep();
@ -63,7 +64,7 @@ static inline void percpu_down_read(struct percpu_rw_semaphore *sem)
if (likely(rcu_sync_is_idle(&sem->rss)))
this_cpu_inc(*sem->read_count);
else
__percpu_down_read(sem, false); /* Unconditional memory barrier */
__percpu_down_read(sem, false, freezable); /* Unconditional memory barrier */
/*
* The preempt_enable() prevents the compiler from
* bleeding the critical section out.
@ -71,6 +72,17 @@ static inline void percpu_down_read(struct percpu_rw_semaphore *sem)
preempt_enable();
}
static inline void percpu_down_read(struct percpu_rw_semaphore *sem)
{
percpu_down_read_internal(sem, false);
}
static inline void percpu_down_read_freezable(struct percpu_rw_semaphore *sem,
bool freeze)
{
percpu_down_read_internal(sem, freeze);
}
static inline bool percpu_down_read_trylock(struct percpu_rw_semaphore *sem)
{
bool ret = true;
@ -82,7 +94,7 @@ static inline bool percpu_down_read_trylock(struct percpu_rw_semaphore *sem)
if (likely(rcu_sync_is_idle(&sem->rss)))
this_cpu_inc(*sem->read_count);
else
ret = __percpu_down_read(sem, true); /* Unconditional memory barrier */
ret = __percpu_down_read(sem, true, false); /* Unconditional memory barrier */
preempt_enable();
/*
* The barrier() from preempt_enable() prevents the compiler from

View File

@ -138,7 +138,8 @@ static int percpu_rwsem_wake_function(struct wait_queue_entry *wq_entry,
return !reader; /* wake (readers until) 1 writer */
}
static void percpu_rwsem_wait(struct percpu_rw_semaphore *sem, bool reader)
static void percpu_rwsem_wait(struct percpu_rw_semaphore *sem, bool reader,
bool freeze)
{
DEFINE_WAIT_FUNC(wq_entry, percpu_rwsem_wake_function);
bool wait;
@ -156,7 +157,8 @@ static void percpu_rwsem_wait(struct percpu_rw_semaphore *sem, bool reader)
spin_unlock_irq(&sem->waiters.lock);
while (wait) {
set_current_state(TASK_UNINTERRUPTIBLE);
set_current_state(TASK_UNINTERRUPTIBLE |
(freeze ? TASK_FREEZABLE : 0));
if (!smp_load_acquire(&wq_entry.private))
break;
schedule();
@ -164,7 +166,8 @@ static void percpu_rwsem_wait(struct percpu_rw_semaphore *sem, bool reader)
__set_current_state(TASK_RUNNING);
}
bool __sched __percpu_down_read(struct percpu_rw_semaphore *sem, bool try)
bool __sched __percpu_down_read(struct percpu_rw_semaphore *sem, bool try,
bool freeze)
{
if (__percpu_down_read_trylock(sem))
return true;
@ -174,7 +177,7 @@ bool __sched __percpu_down_read(struct percpu_rw_semaphore *sem, bool try)
trace_contention_begin(sem, LCB_F_PERCPU | LCB_F_READ);
preempt_enable();
percpu_rwsem_wait(sem, /* .reader = */ true);
percpu_rwsem_wait(sem, /* .reader = */ true, freeze);
preempt_disable();
trace_contention_end(sem, 0);
@ -237,7 +240,7 @@ void __sched percpu_down_write(struct percpu_rw_semaphore *sem)
*/
if (!__percpu_down_write_trylock(sem)) {
trace_contention_begin(sem, LCB_F_PERCPU | LCB_F_WRITE);
percpu_rwsem_wait(sem, /* .reader = */ false);
percpu_rwsem_wait(sem, /* .reader = */ false, false);
contended = true;
}