]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
btrfs: add assertions to make super block creation more clear
authorQu Wenruo <wqu@suse.com>
Sun, 15 Jun 2025 22:44:25 +0000 (08:14 +0930)
committerDavid Sterba <dsterba@suse.com>
Mon, 21 Jul 2025 22:06:19 +0000 (00:06 +0200)
When calling sget_fc(), there are 3 different situations:

a) Critical error
   No super block created.

b) A new super block is created
   The fc->s_fs_info is transferred to the super block, and fc->s_fs_info
   is reset to NULL.

   In this case sb->s_root should still be NULL, and needs to be properly
   initialized later by btrfs_fill_super().

c) An existing super block is returned
   The fc->s_fs_info is untouched, and anything related to that fs_info
   should be properly cleaned up.

This is not obvious even with the extra comments at sget_fc().

Enhance the situation by:

- Add comments for case b) and c)
  Especially for case c), the fs_info and fs_devices cleanup happens at
  different timing, thus needs extra explanation.

- Move the comments closer to case b) and case c)

Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/super.c

index d977d2da985ee479a1f979208372b6101d912e2c..97011e1f637776480740e628d6a12e9a1ef2b228 100644 (file)
@@ -1876,15 +1876,6 @@ static int btrfs_get_tree_super(struct fs_context *fc)
 
        bdev = fs_devices->latest_dev->bdev;
 
-       /*
-        * From now on the error handling is not straightforward.
-        *
-        * If successful, this will transfer the fs_info into the super block,
-        * and fc->s_fs_info will be NULL.  However if there's an existing
-        * super, we'll still have fc->s_fs_info populated.  If we error
-        * completely out it'll be cleaned up when we drop the fs_context,
-        * otherwise it's tied to the lifetime of the super_block.
-        */
        sb = sget_fc(fc, btrfs_fc_test_super, set_anon_super_fc);
        if (IS_ERR(sb)) {
                ret = PTR_ERR(sb);
@@ -1894,6 +1885,19 @@ static int btrfs_get_tree_super(struct fs_context *fc)
        set_device_specific_options(fs_info);
 
        if (sb->s_root) {
+               /*
+                * Not the first mount of the fs thus got an existing super block.
+                * Will reuse the returned super block, fs_info and fs_devices.
+                */
+               ASSERT(fc->s_fs_info == fs_info);
+
+               /*
+                * fc->s_fs_info is not touched and will be later freed by
+                * put_fs_context() through btrfs_free_fs_context().
+                *
+                * But we have opened fs_devices at the beginning of the
+                * function, thus still need to close them manually.
+                */
                btrfs_close_devices(fs_devices);
                /*
                 * At this stage we may have RO flag mismatch between
@@ -1902,6 +1906,13 @@ static int btrfs_get_tree_super(struct fs_context *fc)
                 * needed.
                 */
        } else {
+               /*
+                * The first mount of the fs thus a new superblock, fc->s_fs_info
+                * must be NULL, and the ownership of our fs_info and fs_devices is
+                * transferred to the super block.
+                */
+               ASSERT(fc->s_fs_info == NULL);
+
                snprintf(sb->s_id, sizeof(sb->s_id), "%pg", bdev);
                shrinker_debugfs_rename(sb->s_shrink, "sb-btrfs:%s", sb->s_id);
                btrfs_sb(sb)->bdev_holder = &btrfs_fs_type;