Unverified Commit 6e5af8e3 authored by Christian Brauner's avatar Christian Brauner
Browse files

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: default avatarChristian Brauner <brauner@kernel.org>
parents 0af2f6be 1af33317
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -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;
}
+15 −9
Original line number Diff line number Diff line
@@ -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);
+2 −2
Original line number Diff line number Diff line
@@ -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;
+4 −4
Original line number Diff line number Diff line
@@ -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,
+225 −91
Original line number Diff line number Diff line
@@ -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 *))
{
	struct super_block *sb, *p = NULL;

	spin_lock(&sb_lock);
	list_for_each_entry(sb, &super_blocks, s_list) {
		if (super_flags(sb, SB_DYING))
			continue;
		sb->s_count++;
		spin_unlock(&sb_lock);

		f(sb);
enum super_iter_flags_t {
	SUPER_ITER_EXCL		= (1U << 0),
	SUPER_ITER_UNLOCKED	= (1U << 1),
	SUPER_ITER_REVERSE	= (1U << 2),
};

		spin_lock(&sb_lock);
		if (p)
			__put_super(p);
		p = sb;
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);
}
	if (p)
		__put_super(p);
	spin_unlock(&sb_lock);

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);
}
/**
 *	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)

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) {
		bool locked;
	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);

		locked = super_lock_shared(sb);
		if (locked) {
			if (sb->s_root)
		if (flags & SUPER_ITER_UNLOCKED) {
			f(sb, arg);
			super_unlock_shared(sb);
		} 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)
		if (locked)
			f(sb, arg);
			super_unlock_shared(sb);
		}

		spin_lock(&sb_lock);
		if (p)
@@ -991,24 +992,22 @@ 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;

		if (sb->s_dev != dev)
			continue;

		sb->s_count++;
		spin_unlock(&sb_lock);
			/* still alive? */

		locked = super_lock(sb, excl);
			if (locked) {
				if (sb->s_root)
		if (locked)
			return sb;
				super_unlock(sb, excl);
			}
			/* nope, got unmounted */

		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);
	thaw_super_locked(sb, FREEZE_HOLDER_USERSPACE, NULL);
	return;
}
	if (locked)
		super_unlock_excl(sb);
}

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);

Loading