diff mbox series

[2/3] fs: Introduce { freeze, thaw }_active_super functions

Message ID 20221129230736.3462830-3-agruenba@redhat.com (mailing list archive)
State New, archived
Headers show
Series Shut down frozen filesystems on last unmount | expand

Commit Message

Andreas Gruenbacher Nov. 29, 2022, 11:07 p.m. UTC
Introduce functions freeze_active_super() and thaw_active_super(), which
are like freeze_super() and thaw_super() but don't keep an active super
block reference between freeze_super() and the following thaw_super().
This allows filesystem shutdown to occur while a filesystem is frozen.

In places in the filesystem code where a super block may or may not be
active anymore (i.e., places that may race with ->put_super()), function
activate_super() can be used for grabbing an active super block
reference.  In that case,

freeze_super(sb) turns into:

	if (activate_super(sb)) {
		ret = freeze_active_super(sb);
		deactivate_super(sb);
	}

and thaw_super(sb) turns into:

	if (activate_super(sb)) {
		ret = thaw_active_super(sb);
		deactivate_super(sb);
	}

For obvious reaons, the filesystem is responsible for making sure that
no such asynchronous code outlives put_super().

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 fs/super.c         | 70 ++++++++++++++++++++++++++++++++++++++++------
 include/linux/fs.h |  2 ++
 2 files changed, 64 insertions(+), 8 deletions(-)

Comments

Al Viro Jan. 12, 2023, 5:54 p.m. UTC | #1
On Wed, Nov 30, 2022 at 12:07:34AM +0100, Andreas Gruenbacher wrote:

> -int freeze_super(struct super_block *sb)
> +int freeze_active_super(struct super_block *sb)
>  {
>  	int ret;
>  
> -	atomic_inc(&sb->s_active);
>  	down_write(&sb->s_umount);
>  	if (sb->s_writers.frozen != SB_UNFROZEN) {
>  		deactivate_locked_super(sb);

Not fond of the calling conventions, to be honest...  "On success return 0;
on failure return -E... *and* drop an active reference passed by the caller"?

If you go that way, at least take the deactivate_locked_super() into the
caller and I would argue that ->s_umount handling also belongs in the caller,
both grabbing and dropping it.
diff mbox series

Patch

diff --git a/fs/super.c b/fs/super.c
index 051241cf408b..cba55ca89c09 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -39,6 +39,7 @@ 
 #include <uapi/linux/mount.h>
 #include "internal.h"
 
+static int thaw_active_super_locked(struct super_block *sb);
 static int thaw_super_locked(struct super_block *sb);
 
 static LIST_HEAD(super_blocks);
@@ -510,6 +511,8 @@  void generic_shutdown_super(struct super_block *sb)
 		if (sop->put_super)
 			sop->put_super(sb);
 
+		thaw_active_super_locked(sb);
+
 		if (!list_empty(&sb->s_inodes)) {
 			printk("VFS: Busy inodes after unmount of %s. "
 			   "Self-destruct in 5 seconds.  Have a nice day...\n",
@@ -1676,7 +1679,7 @@  static void sb_freeze_unlock(struct super_block *sb, int level)
 }
 
 /**
- * freeze_super - lock the filesystem and force it into a consistent state
+ * freeze_active_super - lock the filesystem and force it into a consistent state
  * @sb: the super to lock
  *
  * Syncs the super to make sure the filesystem is consistent and calls the fs's
@@ -1708,11 +1711,10 @@  static void sb_freeze_unlock(struct super_block *sb, int level)
  *
  * sb->s_writers.frozen is protected by sb->s_umount.
  */
-int freeze_super(struct super_block *sb)
+int freeze_active_super(struct super_block *sb)
 {
 	int ret;
 
-	atomic_inc(&sb->s_active);
 	down_write(&sb->s_umount);
 	if (sb->s_writers.frozen != SB_UNFROZEN) {
 		deactivate_locked_super(sb);
@@ -1776,16 +1778,38 @@  int freeze_super(struct super_block *sb)
 	up_write(&sb->s_umount);
 	return 0;
 }
+EXPORT_SYMBOL(freeze_active_super);
+
+/**
+ * freeze_super - lock the filesystem and force it into a consistent state
+ * @sb: the super to lock
+ *
+ * Like freeze_active_super(), but takes an active reference on @sb so
+ * that it cannot be shut down while frozen.
+ */
+int freeze_super(struct super_block *sb)
+{
+	atomic_inc(&sb->s_active);
+	return freeze_active_super(sb);
+}
 EXPORT_SYMBOL(freeze_super);
 
-static int thaw_super_locked(struct super_block *sb)
+/**
+ * thaw_active_super_locked -- unlock filesystem
+ * @sb: the super to thaw
+ *
+ * Unlocks the filesystem and marks it writeable again after freeze_super().
+ *
+ * Like thaw_super(), but takes a locked super block, leaves it locked, and
+ * doesn't drop an active reference.  For use in ->put_super by filesystems
+ * that use freeze_active_super() and thaw_active_super().
+ */
+static int thaw_active_super_locked(struct super_block *sb)
 {
 	int error;
 
-	if (sb->s_writers.frozen != SB_FREEZE_COMPLETE) {
-		up_write(&sb->s_umount);
+	if (sb->s_writers.frozen != SB_FREEZE_COMPLETE)
 		return -EINVAL;
-	}
 
 	if (sb_rdonly(sb)) {
 		sb->s_writers.frozen = SB_UNFROZEN;
@@ -1800,7 +1824,6 @@  static int thaw_super_locked(struct super_block *sb)
 			printk(KERN_ERR
 				"VFS:Filesystem thaw failed\n");
 			lockdep_sb_freeze_release(sb);
-			up_write(&sb->s_umount);
 			return error;
 		}
 	}
@@ -1809,6 +1832,18 @@  static int thaw_super_locked(struct super_block *sb)
 	sb_freeze_unlock(sb, SB_FREEZE_FS);
 out:
 	wake_up(&sb->s_writers.wait_unfrozen);
+	return 0;
+}
+
+static int thaw_super_locked(struct super_block *sb)
+{
+	int ret;
+
+	ret = thaw_active_super_locked(sb);
+	if (ret) {
+		up_write(&sb->s_umount);
+		return ret;
+	}
 	deactivate_locked_super(sb);
 	return 0;
 }
@@ -1825,3 +1860,22 @@  int thaw_super(struct super_block *sb)
 	return thaw_super_locked(sb);
 }
 EXPORT_SYMBOL(thaw_super);
+
+/**
+ * thaw_active_super -- unlock filesystem
+ * @sb: the super to thaw
+ *
+ * Unlocks the filesystem and marks it writeable again after freeze_super().
+ *
+ * Like thaw_super(), but doesn't drop an active reference.
+ */
+int thaw_active_super(struct super_block *sb)
+{
+	int ret;
+
+	down_write(&sb->s_umount);
+	ret = thaw_active_super_locked(sb);
+	up_write(&sb->s_umount);
+	return ret;
+}
+EXPORT_SYMBOL(thaw_active_super);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 84c609123a25..fe869102cd85 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2612,6 +2612,8 @@  extern int user_statfs(const char __user *, struct kstatfs *);
 extern int fd_statfs(int, struct kstatfs *);
 extern int freeze_super(struct super_block *super);
 extern int thaw_super(struct super_block *super);
+extern int freeze_active_super(struct super_block *super);
+extern int thaw_active_super(struct super_block *super);
 extern __printf(2, 3)
 int super_setup_bdi_name(struct super_block *sb, char *fmt, ...);
 extern int super_setup_bdi(struct super_block *sb);