From 2936a6ac8d976f3c4de6fb5fbc00c5905c0cfec7 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 16 Jun 2025 08:14:25 +0930 Subject: [PATCH] btrfs: add assertions to make super block creation more clear 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 Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/super.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index d977d2da985ee..97011e1f63777 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -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; -- 2.51.0