ext4: unify the ext4 super block loading operation
authorJason Yan <yanaijie@huawei.com>
Fri, 16 Sep 2022 14:15:25 +0000 (22:15 +0800)
committerTheodore Ts'o <tytso@mit.edu>
Sat, 1 Oct 2022 03:46:53 +0000 (23:46 -0400)
Now we load the super block from the disk in two steps. First we load
the super block with the default block size(EXT4_MIN_BLOCK_SIZE). Second
we load the super block with the real block size. The second step is a
little far from the first step. This patch move these two steps together
in a new function.

Signed-off-by: Jason Yan <yanaijie@huawei.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Link: https://lore.kernel.org/r/20220916141527.1012715-15-yanaijie@huawei.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
fs/ext4/super.c

index bec7fbff9acf5a5962976d2342d65a52ca38011e..11debba7c7d1aea5df475ceb2f7f96ccfb6d2d4b 100644 (file)
@@ -4920,39 +4920,21 @@ static int ext4_journal_data_mode_check(struct super_block *sb)
        return 0;
 }
 
-static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)
+static int ext4_load_super(struct super_block *sb, ext4_fsblk_t *lsb,
+                          int silent)
 {
-       struct buffer_head *bh;
-       struct ext4_super_block *es = NULL;
        struct ext4_sb_info *sbi = EXT4_SB(sb);
-       struct flex_groups **flex_groups;
-       ext4_fsblk_t block;
+       struct ext4_super_block *es;
        ext4_fsblk_t logical_sb_block;
        unsigned long offset = 0;
-       struct inode *root;
-       int ret = -ENOMEM;
+       struct buffer_head *bh;
+       int ret = -EINVAL;
        int blocksize;
-       unsigned int i;
-       int needs_recovery, has_huge_files;
-       int err = 0;
-       ext4_group_t first_not_zeroed;
-       struct ext4_fs_context *ctx = fc->fs_private;
-       int silent = fc->sb_flags & SB_SILENT;
-
-       /* Set defaults for the variables that will be set during parsing */
-       if (!(ctx->spec & EXT4_SPEC_JOURNAL_IOPRIO))
-               ctx->journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
 
-       sbi->s_inode_readahead_blks = EXT4_DEF_INODE_READAHEAD_BLKS;
-       sbi->s_sectors_written_start =
-               part_stat_read(sb->s_bdev, sectors[STAT_WRITE]);
-
-       /* -EINVAL is default */
-       ret = -EINVAL;
        blocksize = sb_min_blocksize(sb, EXT4_MIN_BLOCK_SIZE);
        if (!blocksize) {
                ext4_msg(sb, KERN_ERR, "unable to set blocksize");
-               goto out_fail;
+               return -EINVAL;
        }
 
        /*
@@ -4969,8 +4951,7 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)
        bh = ext4_sb_bread_unmovable(sb, logical_sb_block);
        if (IS_ERR(bh)) {
                ext4_msg(sb, KERN_ERR, "unable to read superblock");
-               ret = PTR_ERR(bh);
-               goto out_fail;
+               return PTR_ERR(bh);
        }
        /*
         * Note: s_es must be initialized as soon as possible because
@@ -4982,9 +4963,106 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)
        if (sb->s_magic != EXT4_SUPER_MAGIC) {
                if (!silent)
                        ext4_msg(sb, KERN_ERR, "VFS: Can't find ext4 filesystem");
-               goto failed_mount;
+               goto out;
        }
 
+       if (le32_to_cpu(es->s_log_block_size) >
+           (EXT4_MAX_BLOCK_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {
+               ext4_msg(sb, KERN_ERR,
+                        "Invalid log block size: %u",
+                        le32_to_cpu(es->s_log_block_size));
+               goto out;
+       }
+       if (le32_to_cpu(es->s_log_cluster_size) >
+           (EXT4_MAX_CLUSTER_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {
+               ext4_msg(sb, KERN_ERR,
+                        "Invalid log cluster size: %u",
+                        le32_to_cpu(es->s_log_cluster_size));
+               goto out;
+       }
+
+       blocksize = EXT4_MIN_BLOCK_SIZE << le32_to_cpu(es->s_log_block_size);
+
+       /*
+        * If the default block size is not the same as the real block size,
+        * we need to reload it.
+        */
+       if (sb->s_blocksize == blocksize) {
+               *lsb = logical_sb_block;
+               sbi->s_sbh = bh;
+               return 0;
+       }
+
+       /*
+        * bh must be released before kill_bdev(), otherwise
+        * it won't be freed and its page also. kill_bdev()
+        * is called by sb_set_blocksize().
+        */
+       brelse(bh);
+       /* Validate the filesystem blocksize */
+       if (!sb_set_blocksize(sb, blocksize)) {
+               ext4_msg(sb, KERN_ERR, "bad block size %d",
+                               blocksize);
+               bh = NULL;
+               goto out;
+       }
+
+       logical_sb_block = sbi->s_sb_block * EXT4_MIN_BLOCK_SIZE;
+       offset = do_div(logical_sb_block, blocksize);
+       bh = ext4_sb_bread_unmovable(sb, logical_sb_block);
+       if (IS_ERR(bh)) {
+               ext4_msg(sb, KERN_ERR, "Can't read superblock on 2nd try");
+               ret = PTR_ERR(bh);
+               bh = NULL;
+               goto out;
+       }
+       es = (struct ext4_super_block *)(bh->b_data + offset);
+       sbi->s_es = es;
+       if (es->s_magic != cpu_to_le16(EXT4_SUPER_MAGIC)) {
+               ext4_msg(sb, KERN_ERR, "Magic mismatch, very weird!");
+               goto out;
+       }
+       *lsb = logical_sb_block;
+       sbi->s_sbh = bh;
+       return 0;
+out:
+       brelse(bh);
+       return ret;
+}
+
+static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)
+{
+       struct ext4_super_block *es = NULL;
+       struct ext4_sb_info *sbi = EXT4_SB(sb);
+       struct flex_groups **flex_groups;
+       ext4_fsblk_t block;
+       ext4_fsblk_t logical_sb_block;
+       struct inode *root;
+       int ret = -ENOMEM;
+       int blocksize;
+       unsigned int i;
+       int needs_recovery, has_huge_files;
+       int err = 0;
+       ext4_group_t first_not_zeroed;
+       struct ext4_fs_context *ctx = fc->fs_private;
+       int silent = fc->sb_flags & SB_SILENT;
+
+       /* Set defaults for the variables that will be set during parsing */
+       if (!(ctx->spec & EXT4_SPEC_JOURNAL_IOPRIO))
+               ctx->journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
+
+       sbi->s_inode_readahead_blks = EXT4_DEF_INODE_READAHEAD_BLKS;
+       sbi->s_sectors_written_start =
+               part_stat_read(sb->s_bdev, sectors[STAT_WRITE]);
+
+       /* -EINVAL is default */
+       ret = -EINVAL;
+       err = ext4_load_super(sb, &logical_sb_block, silent);
+       if (err)
+               goto out_fail;
+
+       es = sbi->s_es;
+       blocksize = sb->s_blocksize;
        sbi->s_kbytes_written = le64_to_cpu(es->s_kbytes_written);
 
        err = ext4_init_metadata_csum(sb, es);
@@ -5005,23 +5083,6 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)
         */
        sbi->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT;
 
-       if (le32_to_cpu(es->s_log_block_size) >
-           (EXT4_MAX_BLOCK_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {
-               ext4_msg(sb, KERN_ERR,
-                        "Invalid log block size: %u",
-                        le32_to_cpu(es->s_log_block_size));
-               goto failed_mount;
-       }
-       if (le32_to_cpu(es->s_log_cluster_size) >
-           (EXT4_MAX_CLUSTER_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {
-               ext4_msg(sb, KERN_ERR,
-                        "Invalid log cluster size: %u",
-                        le32_to_cpu(es->s_log_cluster_size));
-               goto failed_mount;
-       }
-
-       blocksize = EXT4_MIN_BLOCK_SIZE << le32_to_cpu(es->s_log_block_size);
-
        if (blocksize == PAGE_SIZE)
                set_opt(sb, DIOREAD_NOLOCK);
 
@@ -5088,40 +5149,6 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)
                goto failed_mount;
        }
 
-       if (sb->s_blocksize != blocksize) {
-               /*
-                * bh must be released before kill_bdev(), otherwise
-                * it won't be freed and its page also. kill_bdev()
-                * is called by sb_set_blocksize().
-                */
-               brelse(bh);
-               /* Validate the filesystem blocksize */
-               if (!sb_set_blocksize(sb, blocksize)) {
-                       ext4_msg(sb, KERN_ERR, "bad block size %d",
-                                       blocksize);
-                       bh = NULL;
-                       goto failed_mount;
-               }
-
-               logical_sb_block = sbi->s_sb_block * EXT4_MIN_BLOCK_SIZE;
-               offset = do_div(logical_sb_block, blocksize);
-               bh = ext4_sb_bread_unmovable(sb, logical_sb_block);
-               if (IS_ERR(bh)) {
-                       ext4_msg(sb, KERN_ERR,
-                              "Can't read superblock on 2nd try");
-                       ret = PTR_ERR(bh);
-                       bh = NULL;
-                       goto failed_mount;
-               }
-               es = (struct ext4_super_block *)(bh->b_data + offset);
-               sbi->s_es = es;
-               if (es->s_magic != cpu_to_le16(EXT4_SUPER_MAGIC)) {
-                       ext4_msg(sb, KERN_ERR,
-                              "Magic mismatch, very weird!");
-                       goto failed_mount;
-               }
-       }
-
        has_huge_files = ext4_has_feature_huge_file(sb);
        sbi->s_bitmap_maxbytes = ext4_max_bitmap_size(sb->s_blocksize_bits,
                                                      has_huge_files);
@@ -5158,7 +5185,6 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)
        sbi->s_itb_per_group = sbi->s_inodes_per_group /
                                        sbi->s_inodes_per_block;
        sbi->s_desc_per_block = blocksize / EXT4_DESC_SIZE(sb);
-       sbi->s_sbh = bh;
        sbi->s_mount_state = le16_to_cpu(es->s_state) & ~EXT4_FC_REPLAY;
        sbi->s_addr_per_block_bits = ilog2(EXT4_ADDR_PER_BLOCK(sb));
        sbi->s_desc_per_block_bits = ilog2(EXT4_DESC_PER_BLOCK(sb));
@@ -5595,7 +5621,7 @@ failed_mount:
 #endif
        fscrypt_free_dummy_policy(&sbi->s_dummy_enc_policy);
        /* ext4_blkdev_remove() calls kill_bdev(), release bh before it. */
-       brelse(bh);
+       brelse(sbi->s_sbh);
        ext4_blkdev_remove(sbi);
 out_fail:
        sb->s_fs_info = NULL;