super: add filesystem freezing helpers for suspend and hibernate
authorChristian Brauner <brauner@kernel.org>
Sat, 29 Mar 2025 08:42:19 +0000 (09:42 +0100)
committerChristian Brauner <brauner@kernel.org>
Fri, 9 May 2025 10:41:02 +0000 (12:41 +0200)
Allow the power subsystem to support filesystem freeze for
suspend and hibernate.

For some kernel subsystems it is paramount that they are guaranteed that
they are the owner of the freeze to avoid any risk of deadlocks. This is
the case for the power subsystem. Enable it to recognize whether it did
actually freeze the filesystem.

If userspace has 10 filesystems and suspend/hibernate manges to freeze 5
and then fails on the 6th for whatever odd reason (current or future)
then power needs to undo the freeze of the first 5 filesystems. It can't
just walk the list again because while it's unlikely that a new
filesystem got added in the meantime it still cannot tell which
filesystems the power subsystem actually managed to get a freeze
reference count on that needs to be dropped during thaw.

There's various ways out of this ugliness. For example, record the
filesystems the power subsystem managed to freeze on a temporary list in
the callbacks and then walk that list backwards during thaw to undo the
freezing or make sure that the power subsystem just actually exclusively
freezes things it can freeze and marking such filesystems as being owned
by power for the duration of the suspend or resume cycle. I opted for
the latter as that seemed the clean thing to do even if it means more
code changes.

If hibernation races with filesystem freezing (e.g. DM reconfiguration),
then hibernation need not freeze a filesystem because it's already
frozen but userspace may thaw the filesystem before hibernation actually
happens.

If the race happens the other way around, DM reconfiguration may
unexpectedly fail with EBUSY.

So allow FREEZE_EXCL to nest with other holders. An exclusive freezer
cannot be undone by any of the other concurrent freezers.

Link: https://lore.kernel.org/r/20250329-work-freeze-v2-6-a47af37ecc3d@kernel.org
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/f2fs/gc.c
fs/gfs2/super.c
fs/gfs2/sys.c
fs/ioctl.c
fs/super.c
fs/xfs/scrub/fscounters.c
fs/xfs/xfs_notify_failure.c
include/linux/fs.h

index 2b8f9239bede7ce7704a4a6b22763b0ffc67c971..3e8af62c9e15ecd0d2edd85b7f4d0cc859358bd8 100644 (file)
@@ -2271,12 +2271,12 @@ out_drop_write:
        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 @@ recover_out:
 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;
 }
index a3fe95e519cbc93453e48594cdf1aaf337c2ed10..436cf168a9f580d3b8aced84d2cd593a6f00dc68 100644 (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, enum freeze_holder who)
+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, enum freeze_holder who)
        error = gfs2_freeze_lock_shared(sdp);
        if (error)
                goto fail;
-       error = thaw_super(sb, who);
+       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, FREEZE_HOLDER_USERSPACE);
+       error = gfs2_do_thaw(sdp, FREEZE_HOLDER_USERSPACE, NULL);
        if (error)
                goto out;
 
@@ -729,10 +729,12 @@ out:
  * 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;
@@ -745,7 +747,7 @@ static int gfs2_freeze_super(struct super_block *sb, enum freeze_holder who)
        }
 
        for (;;) {
-               error = freeze_super(sb, who);
+               error = freeze_super(sb, who, freeze_owner);
                if (error) {
                        fs_info(sdp, "GFS2: couldn't freeze filesystem: %d\n",
                                error);
@@ -759,7 +761,7 @@ static int gfs2_freeze_super(struct super_block *sb, enum freeze_holder who)
                        break;
                }
 
-               error = gfs2_do_thaw(sdp, who);
+               error = gfs2_do_thaw(sdp, who, freeze_owner);
                if (error)
                        goto out;
 
@@ -798,10 +800,12 @@ 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;
@@ -816,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, who);
+       error = gfs2_do_thaw(sdp, who, freeze_owner);
 
        if (!error) {
                clear_bit(SDF_FREEZE_INITIATOR, &sdp->sd_flags);
index ecc699f8d9fcaa01f7514a465d7d9f42ac8d659c..748125653d6c2df64a23dcaec18b916243d71e41 100644 (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;
index c91fd2b46a77f6f1ea99aab56b4d92b6d9a8c534..bedc83fc2f20deafb26a08a7beba029724e22dcd 100644 (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,
index 796e7f402a413b72b5bcc8088ef7ff6ce0677ec2..281790625b1895a1d0608a3a99422b727300a771 100644 (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);
@@ -1148,7 +1149,7 @@ static void do_thaw_all_callback(struct super_block *sb, void *unused)
        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;
 }
 
@@ -1175,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);
 
 /**
@@ -1468,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);
@@ -1517,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;
 }
@@ -1892,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)
 {
@@ -1918,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;
@@ -1932,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
@@ -1987,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;
 
@@ -1999,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;
@@ -2021,6 +2156,7 @@ retry:
        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);
@@ -2068,6 +2204,7 @@ retry:
         * 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);
@@ -2082,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.
@@ -2098,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;
        }
@@ -2115,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:
@@ -2130,6 +2273,7 @@ out_unlock:
  * 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.
@@ -2143,13 +2287,14 @@ out_unlock:
  * 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);
 
index e629663e460aec89de401a9558210d0e490cd6d1..9b598c5790ad5e5db179e38c1243dd77e91d6569 100644 (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;
 }
index ed8d8ed42f0a2c68e3fc7350f7fc095c696a9a88..3545dc1d953c40d1f7cb9fb30d56a261c76484e0 100644 (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
index 96f8c8a794eabce9e5701b7c5c72a69cbb19d469..7a3f821d2723bf98e95d0d4bd231c5dcbe70962b 100644 (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];
 };
 
@@ -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);
@@ -3518,6 +3523,8 @@ extern void drop_super_exclusive(struct super_block *sb);
 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 *);