From 6c5e0c9c21456fb561d0997fe2c4d3cf59745ba7 Mon Sep 17 00:00:00 2001 From: Kemeng Shi Date: Wed, 3 Jan 2024 18:48:56 +0800 Subject: [PATCH 01/23] ext4: Add unit test for test_free_blocks_simple Add unit test for test_free_blocks_simple. Signed-off-by: Kemeng Shi Link: https://lore.kernel.org/r/20240103104900.464789-2-shikemeng@huaweicloud.com Signed-off-by: Theodore Ts'o --- fs/ext4/mballoc-test.c | 96 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/fs/ext4/mballoc-test.c b/fs/ext4/mballoc-test.c index f94901fd3835..3aac42ea6a36 100644 --- a/fs/ext4/mballoc-test.c +++ b/fs/ext4/mballoc-test.c @@ -5,6 +5,7 @@ #include #include +#include #include "ext4.h" @@ -112,6 +113,13 @@ static void mbt_ctx_mark_used(struct super_block *sb, ext4_group_t group, mb_set_bits(grp_ctx->bitmap_bh.b_data, start, len); } +static void *mbt_ctx_bitmap(struct super_block *sb, ext4_group_t group) +{ + struct mbt_grp_ctx *grp_ctx = MBT_GRP_CTX(sb, group); + + return grp_ctx->bitmap_bh.b_data; +} + /* called after mbt_init_sb_layout */ static int mbt_ctx_init(struct super_block *sb) { @@ -297,6 +305,93 @@ static void test_new_blocks_simple(struct kunit *test) "unexpectedly get block when no block is available"); } +#define TEST_RANGE_COUNT 8 + +struct test_range { + ext4_grpblk_t start; + ext4_grpblk_t len; +}; + +static void +mbt_generate_test_ranges(struct super_block *sb, struct test_range *ranges, + int count) +{ + ext4_grpblk_t start, len, max; + int i; + + max = EXT4_CLUSTERS_PER_GROUP(sb) / count; + for (i = 0; i < count; i++) { + start = get_random_u32() % max; + len = get_random_u32() % max; + len = min(len, max - start); + + ranges[i].start = start + i * max; + ranges[i].len = len; + } +} + +static void +validate_free_blocks_simple(struct kunit *test, struct super_block *sb, + ext4_group_t goal_group, ext4_grpblk_t start, + ext4_grpblk_t len) +{ + void *bitmap; + ext4_grpblk_t bit, max = EXT4_CLUSTERS_PER_GROUP(sb); + ext4_group_t i; + + for (i = 0; i < ext4_get_groups_count(sb); i++) { + if (i == goal_group) + continue; + + bitmap = mbt_ctx_bitmap(sb, i); + bit = mb_find_next_zero_bit(bitmap, max, 0); + KUNIT_ASSERT_EQ_MSG(test, bit, max, + "free block on unexpected group %d", i); + } + + bitmap = mbt_ctx_bitmap(sb, goal_group); + bit = mb_find_next_zero_bit(bitmap, max, 0); + KUNIT_ASSERT_EQ(test, bit, start); + + bit = mb_find_next_bit(bitmap, max, bit + 1); + KUNIT_ASSERT_EQ(test, bit, start + len); +} + +static void +test_free_blocks_simple_range(struct kunit *test, ext4_group_t goal_group, + ext4_grpblk_t start, ext4_grpblk_t len) +{ + struct super_block *sb = (struct super_block *)test->priv; + struct ext4_sb_info *sbi = EXT4_SB(sb); + struct inode inode = { .i_sb = sb, }; + ext4_fsblk_t block; + + if (len == 0) + return; + + block = ext4_group_first_block_no(sb, goal_group) + + EXT4_C2B(sbi, start); + ext4_free_blocks_simple(&inode, block, len); + validate_free_blocks_simple(test, sb, goal_group, start, len); + mbt_ctx_mark_used(sb, goal_group, 0, EXT4_CLUSTERS_PER_GROUP(sb)); +} + +static void test_free_blocks_simple(struct kunit *test) +{ + struct super_block *sb = (struct super_block *)test->priv; + ext4_grpblk_t max = EXT4_CLUSTERS_PER_GROUP(sb); + ext4_group_t i; + struct test_range ranges[TEST_RANGE_COUNT]; + + for (i = 0; i < ext4_get_groups_count(sb); i++) + mbt_ctx_mark_used(sb, i, 0, max); + + mbt_generate_test_ranges(sb, ranges, TEST_RANGE_COUNT); + for (i = 0; i < TEST_RANGE_COUNT; i++) + test_free_blocks_simple_range(test, TEST_GOAL_GROUP, + ranges[i].start, ranges[i].len); +} + static const struct mbt_ext4_block_layout mbt_test_layouts[] = { { .blocksize_bits = 10, @@ -334,6 +429,7 @@ KUNIT_ARRAY_PARAM(mbt_layouts, mbt_test_layouts, mbt_show_layout); static struct kunit_case mbt_test_cases[] = { KUNIT_CASE_PARAM(test_new_blocks_simple, mbt_layouts_gen_params), + KUNIT_CASE_PARAM(test_free_blocks_simple, mbt_layouts_gen_params), {} }; From 67d2a11b22b4d520072db62620851615df13183e Mon Sep 17 00:00:00 2001 From: Kemeng Shi Date: Wed, 3 Jan 2024 18:48:57 +0800 Subject: [PATCH 02/23] ext4: Add unit test of ext4_mb_generate_buddy Add unit test of ext4_mb_generate_buddy Signed-off-by: Kemeng Shi Link: https://lore.kernel.org/r/20240103104900.464789-3-shikemeng@huaweicloud.com Signed-off-by: Theodore Ts'o --- fs/ext4/mballoc-test.c | 207 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) diff --git a/fs/ext4/mballoc-test.c b/fs/ext4/mballoc-test.c index 3aac42ea6a36..6964974fa536 100644 --- a/fs/ext4/mballoc-test.c +++ b/fs/ext4/mballoc-test.c @@ -28,6 +28,50 @@ struct mbt_ext4_super_block { #define MBT_CTX(_sb) (&(container_of((_sb), struct mbt_ext4_super_block, sb)->mbt_ctx)) #define MBT_GRP_CTX(_sb, _group) (&MBT_CTX(_sb)->grp_ctx[_group]) +static const struct super_operations mbt_sops = { +}; + +static int mbt_mb_init(struct super_block *sb) +{ + int ret; + + /* needed by ext4_mb_init->bdev_nonrot(sb->s_bdev) */ + sb->s_bdev = kzalloc(sizeof(*sb->s_bdev), GFP_KERNEL); + if (sb->s_bdev == NULL) + return -ENOMEM; + + sb->s_bdev->bd_queue = kzalloc(sizeof(struct request_queue), GFP_KERNEL); + if (sb->s_bdev->bd_queue == NULL) { + kfree(sb->s_bdev); + return -ENOMEM; + } + + /* + * needed by ext4_mb_init->ext4_mb_init_backend-> sbi->s_buddy_cache = + * new_inode(sb); + */ + INIT_LIST_HEAD(&sb->s_inodes); + sb->s_op = &mbt_sops; + + ret = ext4_mb_init(sb); + if (ret != 0) + goto err_out; + + return 0; + +err_out: + kfree(sb->s_bdev->bd_queue); + kfree(sb->s_bdev); + return ret; +} + +static void mbt_mb_release(struct super_block *sb) +{ + ext4_mb_release(sb); + kfree(sb->s_bdev->bd_queue); + kfree(sb->s_bdev); +} + static struct super_block *mbt_ext4_alloc_super_block(void) { struct ext4_super_block *es = kzalloc(sizeof(*es), GFP_KERNEL); @@ -37,8 +81,16 @@ static struct super_block *mbt_ext4_alloc_super_block(void) if (fsb == NULL || sbi == NULL || es == NULL) goto out; + sbi->s_blockgroup_lock = + kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL); + if (!sbi->s_blockgroup_lock) + goto out; + + bgl_lock_init(sbi->s_blockgroup_lock); + sbi->s_es = es; fsb->sb.s_fs_info = sbi; + return &fsb->sb; out: @@ -54,6 +106,7 @@ static void mbt_ext4_free_super_block(struct super_block *sb) container_of(sb, struct mbt_ext4_super_block, sb); struct ext4_sb_info *sbi = EXT4_SB(sb); + kfree(sbi->s_blockgroup_lock); kfree(sbi->s_es); kfree(sbi); kfree(fsb); @@ -83,6 +136,9 @@ static void mbt_init_sb_layout(struct super_block *sb, sbi->s_clusters_per_group = layout->blocks_per_group >> layout->cluster_bits; sbi->s_desc_size = layout->desc_size; + sbi->s_desc_per_block_bits = + sb->s_blocksize_bits - (fls(layout->desc_size) - 1); + sbi->s_desc_per_block = 1 << sbi->s_desc_per_block_bits; es->s_first_data_block = cpu_to_le32(0); es->s_blocks_count_lo = cpu_to_le32(layout->blocks_per_group * @@ -240,6 +296,14 @@ static int mbt_kunit_init(struct kunit *test) kunit_activate_static_stub(test, ext4_mb_mark_context, ext4_mb_mark_context_stub); + + /* stub function will be called in mt_mb_init->ext4_mb_init */ + if (mbt_mb_init(sb) != 0) { + mbt_ctx_release(sb); + mbt_ext4_free_super_block(sb); + return -ENOMEM; + } + return 0; } @@ -247,6 +311,7 @@ static void mbt_kunit_exit(struct kunit *test) { struct super_block *sb = (struct super_block *)test->priv; + mbt_mb_release(sb); mbt_ctx_release(sb); mbt_ext4_free_super_block(sb); } @@ -392,6 +457,147 @@ static void test_free_blocks_simple(struct kunit *test) ranges[i].start, ranges[i].len); } +static void mbt_generate_buddy(struct super_block *sb, void *buddy, + void *bitmap, struct ext4_group_info *grp) +{ + struct ext4_sb_info *sbi = EXT4_SB(sb); + uint32_t order, off; + void *bb, *bb_h; + int max; + + memset(buddy, 0xff, sb->s_blocksize); + memset(grp, 0, offsetof(struct ext4_group_info, + bb_counters[MB_NUM_ORDERS(sb)])); + + bb = bitmap; + max = EXT4_CLUSTERS_PER_GROUP(sb); + bb_h = buddy + sbi->s_mb_offsets[1]; + + off = mb_find_next_zero_bit(bb, max, 0); + grp->bb_first_free = off; + while (off < max) { + grp->bb_counters[0]++; + grp->bb_free++; + + if (!(off & 1) && !mb_test_bit(off + 1, bb)) { + grp->bb_free++; + grp->bb_counters[0]--; + mb_clear_bit(off >> 1, bb_h); + grp->bb_counters[1]++; + grp->bb_largest_free_order = 1; + off++; + } + + off = mb_find_next_zero_bit(bb, max, off + 1); + } + + for (order = 1; order < MB_NUM_ORDERS(sb) - 1; order++) { + bb = buddy + sbi->s_mb_offsets[order]; + bb_h = buddy + sbi->s_mb_offsets[order + 1]; + max = max >> 1; + off = mb_find_next_zero_bit(bb, max, 0); + + while (off < max) { + if (!(off & 1) && !mb_test_bit(off + 1, bb)) { + mb_set_bits(bb, off, 2); + grp->bb_counters[order] -= 2; + mb_clear_bit(off >> 1, bb_h); + grp->bb_counters[order + 1]++; + grp->bb_largest_free_order = order + 1; + off++; + } + + off = mb_find_next_zero_bit(bb, max, off + 1); + } + } + + max = EXT4_CLUSTERS_PER_GROUP(sb); + off = mb_find_next_zero_bit(bitmap, max, 0); + while (off < max) { + grp->bb_fragments++; + + off = mb_find_next_bit(bitmap, max, off + 1); + if (off + 1 >= max) + break; + + off = mb_find_next_zero_bit(bitmap, max, off + 1); + } +} + +static void +mbt_validate_group_info(struct kunit *test, struct ext4_group_info *grp1, + struct ext4_group_info *grp2) +{ + struct super_block *sb = (struct super_block *)test->priv; + int i; + + KUNIT_ASSERT_EQ(test, grp1->bb_first_free, + grp2->bb_first_free); + KUNIT_ASSERT_EQ(test, grp1->bb_fragments, + grp2->bb_fragments); + KUNIT_ASSERT_EQ(test, grp1->bb_free, grp2->bb_free); + KUNIT_ASSERT_EQ(test, grp1->bb_largest_free_order, + grp2->bb_largest_free_order); + + for (i = 1; i < MB_NUM_ORDERS(sb); i++) { + KUNIT_ASSERT_EQ_MSG(test, grp1->bb_counters[i], + grp2->bb_counters[i], + "bb_counters[%d] diffs, expected %d, generated %d", + i, grp1->bb_counters[i], + grp2->bb_counters[i]); + } +} + +static void +do_test_generate_buddy(struct kunit *test, struct super_block *sb, void *bitmap, + void *mbt_buddy, struct ext4_group_info *mbt_grp, + void *ext4_buddy, struct ext4_group_info *ext4_grp) +{ + int i; + + mbt_generate_buddy(sb, mbt_buddy, bitmap, mbt_grp); + + for (i = 0; i < MB_NUM_ORDERS(sb); i++) + ext4_grp->bb_counters[i] = 0; + /* needed by validation in ext4_mb_generate_buddy */ + ext4_grp->bb_free = mbt_grp->bb_free; + memset(ext4_buddy, 0xff, sb->s_blocksize); + ext4_mb_generate_buddy(sb, ext4_buddy, bitmap, TEST_GOAL_GROUP, + ext4_grp); + + KUNIT_ASSERT_EQ(test, memcmp(mbt_buddy, ext4_buddy, sb->s_blocksize), + 0); + mbt_validate_group_info(test, mbt_grp, ext4_grp); +} + +static void test_mb_generate_buddy(struct kunit *test) +{ + struct super_block *sb = (struct super_block *)test->priv; + void *bitmap, *expected_bb, *generate_bb; + struct ext4_group_info *expected_grp, *generate_grp; + struct test_range ranges[TEST_RANGE_COUNT]; + int i; + + bitmap = kunit_kzalloc(test, sb->s_blocksize, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bitmap); + expected_bb = kunit_kzalloc(test, sb->s_blocksize, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, expected_bb); + generate_bb = kunit_kzalloc(test, sb->s_blocksize, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, generate_bb); + expected_grp = kunit_kzalloc(test, offsetof(struct ext4_group_info, + bb_counters[MB_NUM_ORDERS(sb)]), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, expected_grp); + generate_grp = ext4_get_group_info(sb, TEST_GOAL_GROUP); + KUNIT_ASSERT_NOT_NULL(test, generate_grp); + + mbt_generate_test_ranges(sb, ranges, TEST_RANGE_COUNT); + for (i = 0; i < TEST_RANGE_COUNT; i++) { + mb_set_bits(bitmap, ranges[i].start, ranges[i].len); + do_test_generate_buddy(test, sb, bitmap, expected_bb, + expected_grp, generate_bb, generate_grp); + } +} + static const struct mbt_ext4_block_layout mbt_test_layouts[] = { { .blocksize_bits = 10, @@ -430,6 +636,7 @@ KUNIT_ARRAY_PARAM(mbt_layouts, mbt_test_layouts, mbt_show_layout); static struct kunit_case mbt_test_cases[] = { KUNIT_CASE_PARAM(test_new_blocks_simple, mbt_layouts_gen_params), KUNIT_CASE_PARAM(test_free_blocks_simple, mbt_layouts_gen_params), + KUNIT_CASE_PARAM(test_mb_generate_buddy, mbt_layouts_gen_params), {} }; From ac96b56a2fbd9e05b28488bdc5d3bd8006b61d35 Mon Sep 17 00:00:00 2001 From: Kemeng Shi Date: Wed, 3 Jan 2024 18:48:58 +0800 Subject: [PATCH 03/23] ext4: Add unit test for mb_mark_used Add unit test for mb_mark_used Signed-off-by: Kemeng Shi Link: https://lore.kernel.org/r/20240103104900.464789-4-shikemeng@huaweicloud.com Signed-off-by: Theodore Ts'o --- fs/ext4/mballoc-test.c | 78 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/fs/ext4/mballoc-test.c b/fs/ext4/mballoc-test.c index 6964974fa536..38e3644cb810 100644 --- a/fs/ext4/mballoc-test.c +++ b/fs/ext4/mballoc-test.c @@ -148,9 +148,13 @@ static void mbt_init_sb_layout(struct super_block *sb, static int mbt_grp_ctx_init(struct super_block *sb, struct mbt_grp_ctx *grp_ctx) { + ext4_grpblk_t max = EXT4_CLUSTERS_PER_GROUP(sb); + grp_ctx->bitmap_bh.b_data = kzalloc(EXT4_BLOCK_SIZE(sb), GFP_KERNEL); if (grp_ctx->bitmap_bh.b_data == NULL) return -ENOMEM; + mb_set_bits(grp_ctx->bitmap_bh.b_data, max, sb->s_blocksize * 8 - max); + ext4_free_group_clusters_set(sb, &grp_ctx->desc, max); return 0; } @@ -197,6 +201,8 @@ static int mbt_ctx_init(struct super_block *sb) * block which will fail ext4_sb_block_valid check. */ mb_set_bits(ctx->grp_ctx[0].bitmap_bh.b_data, 0, 1); + ext4_free_group_clusters_set(sb, &ctx->grp_ctx[0].desc, + EXT4_CLUSTERS_PER_GROUP(sb) - 1); return 0; out: @@ -231,6 +237,13 @@ static int ext4_wait_block_bitmap_stub(struct super_block *sb, ext4_group_t block_group, struct buffer_head *bh) { + /* + * real ext4_wait_block_bitmap will set these flags and + * functions like ext4_mb_init_cache will verify the flags. + */ + set_buffer_uptodate(bh); + set_bitmap_uptodate(bh); + set_buffer_verified(bh); return 0; } @@ -598,6 +611,70 @@ static void test_mb_generate_buddy(struct kunit *test) } } +static void +test_mb_mark_used_range(struct kunit *test, struct ext4_buddy *e4b, + ext4_grpblk_t start, ext4_grpblk_t len, void *bitmap, + void *buddy, struct ext4_group_info *grp) +{ + struct super_block *sb = (struct super_block *)test->priv; + struct ext4_free_extent ex; + int i; + + /* mb_mark_used only accepts non-zero len */ + if (len == 0) + return; + + ex.fe_start = start; + ex.fe_len = len; + ex.fe_group = TEST_GOAL_GROUP; + mb_mark_used(e4b, &ex); + + mb_set_bits(bitmap, start, len); + /* bypass bb_free validatoin in ext4_mb_generate_buddy */ + grp->bb_free -= len; + memset(buddy, 0xff, sb->s_blocksize); + for (i = 0; i < MB_NUM_ORDERS(sb); i++) + grp->bb_counters[i] = 0; + ext4_mb_generate_buddy(sb, buddy, bitmap, 0, grp); + + KUNIT_ASSERT_EQ(test, memcmp(buddy, e4b->bd_buddy, sb->s_blocksize), + 0); + mbt_validate_group_info(test, grp, e4b->bd_info); +} + +static void test_mb_mark_used(struct kunit *test) +{ + struct ext4_buddy e4b; + struct super_block *sb = (struct super_block *)test->priv; + void *bitmap, *buddy; + struct ext4_group_info *grp; + int ret; + struct test_range ranges[TEST_RANGE_COUNT]; + int i; + + /* buddy cache assumes that each page contains at least one block */ + if (sb->s_blocksize > PAGE_SIZE) + kunit_skip(test, "blocksize exceeds pagesize"); + + bitmap = kunit_kzalloc(test, sb->s_blocksize, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bitmap); + buddy = kunit_kzalloc(test, sb->s_blocksize, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buddy); + grp = kunit_kzalloc(test, offsetof(struct ext4_group_info, + bb_counters[MB_NUM_ORDERS(sb)]), GFP_KERNEL); + + ret = ext4_mb_load_buddy(sb, TEST_GOAL_GROUP, &e4b); + KUNIT_ASSERT_EQ(test, ret, 0); + + grp->bb_free = EXT4_CLUSTERS_PER_GROUP(sb); + mbt_generate_test_ranges(sb, ranges, TEST_RANGE_COUNT); + for (i = 0; i < TEST_RANGE_COUNT; i++) + test_mb_mark_used_range(test, &e4b, ranges[i].start, + ranges[i].len, bitmap, buddy, grp); + + ext4_mb_unload_buddy(&e4b); +} + static const struct mbt_ext4_block_layout mbt_test_layouts[] = { { .blocksize_bits = 10, @@ -637,6 +714,7 @@ static struct kunit_case mbt_test_cases[] = { KUNIT_CASE_PARAM(test_new_blocks_simple, mbt_layouts_gen_params), KUNIT_CASE_PARAM(test_free_blocks_simple, mbt_layouts_gen_params), KUNIT_CASE_PARAM(test_mb_generate_buddy, mbt_layouts_gen_params), + KUNIT_CASE_PARAM(test_mb_mark_used, mbt_layouts_gen_params), {} }; From b7098e1fa7bcf350e089af9500c4f9992a1bd6cb Mon Sep 17 00:00:00 2001 From: Kemeng Shi Date: Wed, 3 Jan 2024 18:48:59 +0800 Subject: [PATCH 04/23] ext4: Add unit test for mb_free_blocks Add unit test for mb_free_blocks. Signed-off-by: Kemeng Shi Link: https://lore.kernel.org/r/20240103104900.464789-5-shikemeng@huaweicloud.com Signed-off-by: Theodore Ts'o --- fs/ext4/mballoc-test.c | 69 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/fs/ext4/mballoc-test.c b/fs/ext4/mballoc-test.c index 38e3644cb810..b68f447407b0 100644 --- a/fs/ext4/mballoc-test.c +++ b/fs/ext4/mballoc-test.c @@ -675,6 +675,74 @@ static void test_mb_mark_used(struct kunit *test) ext4_mb_unload_buddy(&e4b); } +static void +test_mb_free_blocks_range(struct kunit *test, struct ext4_buddy *e4b, + ext4_grpblk_t start, ext4_grpblk_t len, void *bitmap, + void *buddy, struct ext4_group_info *grp) +{ + struct super_block *sb = (struct super_block *)test->priv; + int i; + + /* mb_free_blocks will WARN if len is 0 */ + if (len == 0) + return; + + mb_free_blocks(NULL, e4b, start, len); + + mb_clear_bits(bitmap, start, len); + /* bypass bb_free validatoin in ext4_mb_generate_buddy */ + grp->bb_free += len; + memset(buddy, 0xff, sb->s_blocksize); + for (i = 0; i < MB_NUM_ORDERS(sb); i++) + grp->bb_counters[i] = 0; + ext4_mb_generate_buddy(sb, buddy, bitmap, 0, grp); + + KUNIT_ASSERT_EQ(test, memcmp(buddy, e4b->bd_buddy, sb->s_blocksize), + 0); + mbt_validate_group_info(test, grp, e4b->bd_info); + +} + +static void test_mb_free_blocks(struct kunit *test) +{ + struct ext4_buddy e4b; + struct super_block *sb = (struct super_block *)test->priv; + void *bitmap, *buddy; + struct ext4_group_info *grp; + struct ext4_free_extent ex; + int ret; + int i; + struct test_range ranges[TEST_RANGE_COUNT]; + + /* buddy cache assumes that each page contains at least one block */ + if (sb->s_blocksize > PAGE_SIZE) + kunit_skip(test, "blocksize exceeds pagesize"); + + bitmap = kunit_kzalloc(test, sb->s_blocksize, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bitmap); + buddy = kunit_kzalloc(test, sb->s_blocksize, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buddy); + grp = kunit_kzalloc(test, offsetof(struct ext4_group_info, + bb_counters[MB_NUM_ORDERS(sb)]), GFP_KERNEL); + + ret = ext4_mb_load_buddy(sb, TEST_GOAL_GROUP, &e4b); + KUNIT_ASSERT_EQ(test, ret, 0); + + ex.fe_start = 0; + ex.fe_len = EXT4_CLUSTERS_PER_GROUP(sb); + ex.fe_group = TEST_GOAL_GROUP; + mb_mark_used(&e4b, &ex); + grp->bb_free = 0; + memset(bitmap, 0xff, sb->s_blocksize); + + mbt_generate_test_ranges(sb, ranges, TEST_RANGE_COUNT); + for (i = 0; i < TEST_RANGE_COUNT; i++) + test_mb_free_blocks_range(test, &e4b, ranges[i].start, + ranges[i].len, bitmap, buddy, grp); + + ext4_mb_unload_buddy(&e4b); +} + static const struct mbt_ext4_block_layout mbt_test_layouts[] = { { .blocksize_bits = 10, @@ -715,6 +783,7 @@ static struct kunit_case mbt_test_cases[] = { KUNIT_CASE_PARAM(test_free_blocks_simple, mbt_layouts_gen_params), KUNIT_CASE_PARAM(test_mb_generate_buddy, mbt_layouts_gen_params), KUNIT_CASE_PARAM(test_mb_mark_used, mbt_layouts_gen_params), + KUNIT_CASE_PARAM(test_mb_free_blocks, mbt_layouts_gen_params), {} }; From 2b81493f8eb6fc0c263dbca0064e10e4c00e0f91 Mon Sep 17 00:00:00 2001 From: Kemeng Shi Date: Wed, 3 Jan 2024 18:49:00 +0800 Subject: [PATCH 05/23] ext4: Add unit test for ext4_mb_mark_diskspace_used Add unit test for ext4_mb_mark_diskspace_used Signed-off-by: Kemeng Shi Link: https://lore.kernel.org/r/20240103104900.464789-6-shikemeng@huaweicloud.com Signed-off-by: Theodore Ts'o --- fs/ext4/mballoc-test.c | 52 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/fs/ext4/mballoc-test.c b/fs/ext4/mballoc-test.c index b68f447407b0..12d0b22cabe1 100644 --- a/fs/ext4/mballoc-test.c +++ b/fs/ext4/mballoc-test.c @@ -470,6 +470,57 @@ static void test_free_blocks_simple(struct kunit *test) ranges[i].start, ranges[i].len); } +static void +test_mark_diskspace_used_range(struct kunit *test, + struct ext4_allocation_context *ac, + ext4_grpblk_t start, + ext4_grpblk_t len) +{ + struct super_block *sb = (struct super_block *)test->priv; + int ret; + void *bitmap; + ext4_grpblk_t i, max; + + /* ext4_mb_mark_diskspace_used will BUG if len is 0 */ + if (len == 0) + return; + + ac->ac_b_ex.fe_group = TEST_GOAL_GROUP; + ac->ac_b_ex.fe_start = start; + ac->ac_b_ex.fe_len = len; + + bitmap = mbt_ctx_bitmap(sb, TEST_GOAL_GROUP); + memset(bitmap, 0, sb->s_blocksize); + ret = ext4_mb_mark_diskspace_used(ac, NULL, 0); + KUNIT_ASSERT_EQ(test, ret, 0); + + max = EXT4_CLUSTERS_PER_GROUP(sb); + i = mb_find_next_bit(bitmap, max, 0); + KUNIT_ASSERT_EQ(test, i, start); + i = mb_find_next_zero_bit(bitmap, max, i + 1); + KUNIT_ASSERT_EQ(test, i, start + len); + i = mb_find_next_bit(bitmap, max, i + 1); + KUNIT_ASSERT_EQ(test, max, i); +} + +static void test_mark_diskspace_used(struct kunit *test) +{ + struct super_block *sb = (struct super_block *)test->priv; + struct inode inode = { .i_sb = sb, }; + struct ext4_allocation_context ac; + struct test_range ranges[TEST_RANGE_COUNT]; + int i; + + mbt_generate_test_ranges(sb, ranges, TEST_RANGE_COUNT); + + ac.ac_status = AC_STATUS_FOUND; + ac.ac_sb = sb; + ac.ac_inode = &inode; + for (i = 0; i < TEST_RANGE_COUNT; i++) + test_mark_diskspace_used_range(test, &ac, ranges[i].start, + ranges[i].len); +} + static void mbt_generate_buddy(struct super_block *sb, void *buddy, void *bitmap, struct ext4_group_info *grp) { @@ -784,6 +835,7 @@ static struct kunit_case mbt_test_cases[] = { KUNIT_CASE_PARAM(test_mb_generate_buddy, mbt_layouts_gen_params), KUNIT_CASE_PARAM(test_mb_mark_used, mbt_layouts_gen_params), KUNIT_CASE_PARAM(test_mb_free_blocks, mbt_layouts_gen_params), + KUNIT_CASE_PARAM(test_mark_diskspace_used, mbt_layouts_gen_params), {} }; From 250448802cda4f4f698ebf946b3a2d2a40423972 Mon Sep 17 00:00:00 2001 From: yangerkun Date: Thu, 18 Jan 2024 12:25:56 +0800 Subject: [PATCH 06/23] ext4: remove unused buddy_loaded in ext4_mb_seq_groups_show We can just first call ext4_mb_unload_buddy, then copy information from ext4_group_info. So remove this unused value. Signed-off-by: yangerkun Reviewed-by: Jan Kara Link: https://lore.kernel.org/r/20240118042557.380058-1-yangerkun@huawei.com Signed-off-by: Theodore Ts'o --- fs/ext4/mballoc.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index e4f7cf9d89c4..abe86791590b 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -3015,8 +3015,7 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v) { struct super_block *sb = pde_data(file_inode(seq->file)); ext4_group_t group = (ext4_group_t) ((unsigned long) v); - int i; - int err, buddy_loaded = 0; + int i, err; struct ext4_buddy e4b; struct ext4_group_info *grinfo; unsigned char blocksize_bits = min_t(unsigned char, @@ -3046,14 +3045,14 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v) seq_printf(seq, "#%-5u: I/O error\n", group); return 0; } - buddy_loaded = 1; + ext4_mb_unload_buddy(&e4b); } + /* + * We care only about free space counters in the group info and + * these are safe to access even after the buddy has been unloaded + */ memcpy(&sg, grinfo, i); - - if (buddy_loaded) - ext4_mb_unload_buddy(&e4b); - seq_printf(seq, "#%-5u: %-5u %-5u %-5u [", group, sg.info.bb_free, sg.info.bb_fragments, sg.info.bb_first_free); for (i = 0; i <= 13; i++) From 4b55d3431ce5ce906480f95391b7af4fecfa2d84 Mon Sep 17 00:00:00 2001 From: yangerkun Date: Thu, 18 Jan 2024 12:25:57 +0800 Subject: [PATCH 07/23] ext4: improve error msg for ext4_mb_seq_groups_show While cat mb_groups for a mounted ext4 image which has some corrupted group, the string return to userspace was just "I/O error" which confuse me a lot. Improve it with ext4_decode_error. Signed-off-by: yangerkun Reviewed-by: Jan Kara Link: https://lore.kernel.org/r/20240118042557.380058-2-yangerkun@huawei.com Signed-off-by: Theodore Ts'o --- fs/ext4/mballoc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index abe86791590b..1a3c738ff21a 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -3016,6 +3016,7 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v) struct super_block *sb = pde_data(file_inode(seq->file)); ext4_group_t group = (ext4_group_t) ((unsigned long) v); int i, err; + char nbuf[16]; struct ext4_buddy e4b; struct ext4_group_info *grinfo; unsigned char blocksize_bits = min_t(unsigned char, @@ -3042,7 +3043,7 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v) if (unlikely(EXT4_MB_GRP_NEED_INIT(grinfo))) { err = ext4_mb_load_buddy(sb, group, &e4b); if (err) { - seq_printf(seq, "#%-5u: I/O error\n", group); + seq_printf(seq, "#%-5u: %s\n", group, ext4_decode_error(NULL, err, nbuf)); return 0; } ext4_mb_unload_buddy(&e4b); From 547e64bda9c7bd6bda2d20a329bb0f60258fe19b Mon Sep 17 00:00:00 2001 From: Cheng Nie Date: Thu, 18 Jan 2024 14:25:11 +0800 Subject: [PATCH 08/23] ext4: fix the comment of ext4_map_blocks()/ext4_ext_map_blocks() this comment of ext4_map_blocks()/ext4_ext_map_blocks() need update after commit c21770573319("ext4: Define a new set of flags for ext4_get_blocks()"). Signed-off-by: Cheng Nie Link: https://lore.kernel.org/r/20240118062511.28276-1-niecheng1@uniontech.com Signed-off-by: Theodore Ts'o --- fs/ext4/extents.c | 6 +++--- fs/ext4/inode.c | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 7669d154c05e..e57054bdc5fd 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -4111,10 +4111,10 @@ insert_hole: * * Need to be called with * down_read(&EXT4_I(inode)->i_data_sem) if not allocating file system block - * (ie, create is zero). Otherwise down_write(&EXT4_I(inode)->i_data_sem) + * (ie, flags is zero). Otherwise down_write(&EXT4_I(inode)->i_data_sem) * * return > 0, number of blocks already mapped/allocated - * if create == 0 and these are pre-allocated blocks + * if flags doesn't contain EXT4_GET_BLOCKS_CREATE and these are pre-allocated blocks * buffer head is unmapped * otherwise blocks are mapped * @@ -4218,7 +4218,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, /* * requested block isn't allocated yet; - * we couldn't try to create block if create flag is zero + * we couldn't try to create block if flags doesn't contain EXT4_GET_BLOCKS_CREATE */ if ((flags & EXT4_GET_BLOCKS_CREATE) == 0) { ext4_lblk_t len; diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 2ccf3b5e3a7c..537803250ca9 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -465,9 +465,10 @@ static void ext4_map_blocks_es_recheck(handle_t *handle, * Otherwise, call with ext4_ind_map_blocks() to handle indirect mapping * based files * - * On success, it returns the number of blocks being mapped or allocated. if - * create==0 and the blocks are pre-allocated and unwritten, the resulting @map - * is marked as unwritten. If the create == 1, it will mark @map as mapped. + * On success, it returns the number of blocks being mapped or allocated. + * If flags doesn't contain EXT4_GET_BLOCKS_CREATE the blocks are + * pre-allocated and unwritten, the resulting @map is marked as unwritten. + * If the flags contain EXT4_GET_BLOCKS_CREATE, it will mark @map as mapped. * * It returns 0 if plain look up failed (blocks have not been allocated), in * that case, @map is returned as unmapped but we still do fill map->m_len to @@ -589,8 +590,7 @@ found: * Returns if the blocks have already allocated * * Note that if blocks have been preallocated - * ext4_ext_get_block() returns the create = 0 - * with buffer head unmapped. + * ext4_ext_map_blocks() returns with buffer head unmapped */ if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) /* From 68ee261fb15457ecb17e3683cb4e6a4792ca5b71 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Fri, 19 Jan 2024 14:11:54 +0800 Subject: [PATCH 09/23] ext4: add a hint for block bitmap corrupt state in mb_groups If one group is marked as block bitmap corrupted, its free blocks cannot be used and its free count is also deducted from the global sbi->s_freeclusters_counter. User might be confused about the absent free space because we can't query the information about corrupted block groups except unreliable error messages in syslog. So add a hint to show block bitmap corrupted groups in mb_groups. Signed-off-by: Zhang Yi Reviewed-by: Jan Kara Link: https://lore.kernel.org/r/20240119061154.1525781-1-yi.zhang@huaweicloud.com Signed-off-by: Theodore Ts'o --- fs/ext4/mballoc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 1a3c738ff21a..bce82e1e792f 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -3059,7 +3059,10 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v) for (i = 0; i <= 13; i++) seq_printf(seq, " %-5u", i <= blocksize_bits + 1 ? sg.info.bb_counters[i] : 0); - seq_puts(seq, " ]\n"); + seq_puts(seq, " ]"); + if (EXT4_MB_GRP_BBITMAP_CORRUPT(&sg.info)) + seq_puts(seq, " Block bitmap corrupted!"); + seq_puts(seq, "\n"); return 0; } From d8b945fa475f13d787df00c26a6dc45a3e2e1d1d Mon Sep 17 00:00:00 2001 From: Ye Bin Date: Fri, 19 Jan 2024 14:29:08 +0800 Subject: [PATCH 10/23] ext4: forbid commit inconsistent quota data when errors=remount-ro There's issue as follows When do IO fault injection test: Quota error (device dm-3): find_block_dqentry: Quota for id 101 referenced but not present Quota error (device dm-3): qtree_read_dquot: Can't read quota structure for id 101 Quota error (device dm-3): do_check_range: Getting block 2021161007 out of range 1-186 Quota error (device dm-3): qtree_read_dquot: Can't read quota structure for id 661 Now, ext4_write_dquot()/ext4_acquire_dquot()/ext4_release_dquot() may commit inconsistent quota data even if process failed. This may lead to filesystem corruption. To ensure filesystem consistent when errors=remount-ro there is need to call ext4_handle_error() to abort journal. Signed-off-by: Ye Bin Reviewed-by: Jan Kara Link: https://lore.kernel.org/r/20240119062908.3598806-1-yebin10@huawei.com Signed-off-by: Theodore Ts'o --- fs/ext4/super.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 0f931d0c227d..3f595090eb62 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -6864,6 +6864,10 @@ static int ext4_write_dquot(struct dquot *dquot) if (IS_ERR(handle)) return PTR_ERR(handle); ret = dquot_commit(dquot); + if (ret < 0) + ext4_error_err(dquot->dq_sb, -ret, + "Failed to commit dquot type %d", + dquot->dq_id.type); err = ext4_journal_stop(handle); if (!ret) ret = err; @@ -6880,6 +6884,10 @@ static int ext4_acquire_dquot(struct dquot *dquot) if (IS_ERR(handle)) return PTR_ERR(handle); ret = dquot_acquire(dquot); + if (ret < 0) + ext4_error_err(dquot->dq_sb, -ret, + "Failed to acquire dquot type %d", + dquot->dq_id.type); err = ext4_journal_stop(handle); if (!ret) ret = err; @@ -6899,6 +6907,10 @@ static int ext4_release_dquot(struct dquot *dquot) return PTR_ERR(handle); } ret = dquot_release(dquot); + if (ret < 0) + ext4_error_err(dquot->dq_sb, -ret, + "Failed to release dquot type %d", + dquot->dq_id.type); err = ext4_journal_stop(handle); if (!ret) ret = err; From 4fbf8bc733d14bceb16dda46a3f5e19c6a9621c5 Mon Sep 17 00:00:00 2001 From: Baokun Li Date: Thu, 1 Feb 2024 22:18:45 +0800 Subject: [PATCH 11/23] ext4: correct best extent lstart adjustment logic When yangerkun review commit 93cdf49f6eca ("ext4: Fix best extent lstart adjustment logic in ext4_mb_new_inode_pa()"), it was found that the best extent did not completely cover the original request after adjusting the best extent lstart in ext4_mb_new_inode_pa() as follows: original request: 2/10(8) normalized request: 0/64(64) best extent: 0/9(9) When we check if best ex can be kept at start of goal, ac_o_ex.fe_logical is 2 less than the adjusted best extent logical end 9, so we think the adjustment is done. But obviously 0/9(9) doesn't cover 2/10(8), so we should determine here if the original request logical end is less than or equal to the adjusted best extent logical end. In addition, add a comment stating when adjusted best_ex will not cover the original request, and remove the duplicate assertion because adjusting lstart makes no change to b_ex.fe_len. Link: https://lore.kernel.org/r/3630fa7f-b432-7afd-5f79-781bc3b2c5ea@huawei.com Fixes: 93cdf49f6eca ("ext4: Fix best extent lstart adjustment logic in ext4_mb_new_inode_pa()") Cc: Signed-off-by: yangerkun Signed-off-by: Baokun Li Reviewed-by: Jan Kara Reviewed-by: Ojaswin Mujoo Link: https://lore.kernel.org/r/20240201141845.1879253-1-libaokun1@huawei.com Signed-off-by: Theodore Ts'o --- fs/ext4/mballoc.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index bce82e1e792f..bd26f2a90751 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -5172,10 +5172,16 @@ ext4_mb_new_inode_pa(struct ext4_allocation_context *ac) .fe_len = ac->ac_orig_goal_len, }; loff_t orig_goal_end = extent_logical_end(sbi, &ex); + loff_t o_ex_end = extent_logical_end(sbi, &ac->ac_o_ex); - /* we can't allocate as much as normalizer wants. - * so, found space must get proper lstart - * to cover original request */ + /* + * We can't allocate as much as normalizer wants, so we try + * to get proper lstart to cover the original request, except + * when the goal doesn't cover the original request as below: + * + * orig_ex:2045/2055(10), isize:8417280 -> normalized:0/2048 + * best_ex:0/200(200) -> adjusted: 1848/2048(200) + */ BUG_ON(ac->ac_g_ex.fe_logical > ac->ac_o_ex.fe_logical); BUG_ON(ac->ac_g_ex.fe_len < ac->ac_o_ex.fe_len); @@ -5187,7 +5193,7 @@ ext4_mb_new_inode_pa(struct ext4_allocation_context *ac) * 1. Check if best ex can be kept at end of goal (before * cr_best_avail trimmed it) and still cover original start * 2. Else, check if best ex can be kept at start of goal and - * still cover original start + * still cover original end * 3. Else, keep the best ex at start of original request. */ ex.fe_len = ac->ac_b_ex.fe_len; @@ -5197,7 +5203,7 @@ ext4_mb_new_inode_pa(struct ext4_allocation_context *ac) goto adjust_bex; ex.fe_logical = ac->ac_g_ex.fe_logical; - if (ac->ac_o_ex.fe_logical < extent_logical_end(sbi, &ex)) + if (o_ex_end <= extent_logical_end(sbi, &ex)) goto adjust_bex; ex.fe_logical = ac->ac_o_ex.fe_logical; @@ -5205,7 +5211,6 @@ adjust_bex: ac->ac_b_ex.fe_logical = ex.fe_logical; BUG_ON(ac->ac_o_ex.fe_logical < ac->ac_b_ex.fe_logical); - BUG_ON(ac->ac_o_ex.fe_len > ac->ac_b_ex.fe_len); BUG_ON(extent_logical_end(sbi, &ex) > orig_goal_end); } From 8208c41c43ad5e9b63dce6c45a73e326109ca658 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 9 Feb 2024 12:20:59 +0100 Subject: [PATCH 12/23] ext4: fold quota accounting into ext4_xattr_inode_lookup_create() When allocating EA inode, quota accounting is done just before ext4_xattr_inode_lookup_create(). Logically these two operations belong together so just fold quota accounting into ext4_xattr_inode_lookup_create(). We also make ext4_xattr_inode_lookup_create() return the looked up / created inode to convert the function to a more standard calling convention. Signed-off-by: Jan Kara Link: https://lore.kernel.org/r/20240209112107.10585-1-jack@suse.cz Signed-off-by: Theodore Ts'o --- fs/ext4/xattr.c | 50 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 82dc5e673d5c..146690c10c73 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -1565,46 +1565,49 @@ ext4_xattr_inode_cache_find(struct inode *inode, const void *value, /* * Add value of the EA in an inode. */ -static int ext4_xattr_inode_lookup_create(handle_t *handle, struct inode *inode, - const void *value, size_t value_len, - struct inode **ret_inode) +static struct inode *ext4_xattr_inode_lookup_create(handle_t *handle, + struct inode *inode, const void *value, size_t value_len) { struct inode *ea_inode; u32 hash; int err; + /* Account inode & space to quota even if sharing... */ + err = ext4_xattr_inode_alloc_quota(inode, value_len); + if (err) + return ERR_PTR(err); + hash = ext4_xattr_inode_hash(EXT4_SB(inode->i_sb), value, value_len); ea_inode = ext4_xattr_inode_cache_find(inode, value, value_len, hash); if (ea_inode) { err = ext4_xattr_inode_inc_ref(handle, ea_inode); - if (err) { - iput(ea_inode); - return err; - } - - *ret_inode = ea_inode; - return 0; + if (err) + goto out_err; + return ea_inode; } /* Create an inode for the EA value */ ea_inode = ext4_xattr_inode_create(handle, inode, hash); - if (IS_ERR(ea_inode)) - return PTR_ERR(ea_inode); + if (IS_ERR(ea_inode)) { + ext4_xattr_inode_free_quota(inode, NULL, value_len); + return ea_inode; + } err = ext4_xattr_inode_write(handle, ea_inode, value, value_len); if (err) { if (ext4_xattr_inode_dec_ref(handle, ea_inode)) ext4_warning_inode(ea_inode, "cleanup dec ref error %d", err); - iput(ea_inode); - return err; + goto out_err; } if (EA_INODE_CACHE(inode)) mb_cache_entry_create(EA_INODE_CACHE(inode), GFP_NOFS, hash, ea_inode->i_ino, true /* reusable */); - - *ret_inode = ea_inode; - return 0; + return ea_inode; +out_err: + iput(ea_inode); + ext4_xattr_inode_free_quota(inode, NULL, value_len); + return ERR_PTR(err); } /* @@ -1712,16 +1715,11 @@ static int ext4_xattr_set_entry(struct ext4_xattr_info *i, if (i->value && in_inode) { WARN_ON_ONCE(!i->value_len); - ret = ext4_xattr_inode_alloc_quota(inode, i->value_len); - if (ret) - goto out; - - ret = ext4_xattr_inode_lookup_create(handle, inode, i->value, - i->value_len, - &new_ea_inode); - if (ret) { + new_ea_inode = ext4_xattr_inode_lookup_create(handle, inode, + i->value, i->value_len); + if (IS_ERR(new_ea_inode)) { + ret = PTR_ERR(new_ea_inode); new_ea_inode = NULL; - ext4_xattr_inode_free_quota(inode, NULL, i->value_len); goto out; } } From 7f48212678e91a057259b3e281701f7feb1ee397 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 9 Feb 2024 12:21:01 +0100 Subject: [PATCH 13/23] ext4: drop duplicate ea_inode handling in ext4_xattr_block_set() ext4_xattr_block_set() drops ea_inode reference in two places. Handling it just under the 'cleanup' label is enough so drop the second occurence. Signed-off-by: Jan Kara Link: https://lore.kernel.org/r/20240209112107.10585-3-jack@suse.cz Signed-off-by: Theodore Ts'o --- fs/ext4/xattr.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 146690c10c73..b67a176bfcf9 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -2158,17 +2158,6 @@ getblk_failed: ENTRY(header(s->base)+1)); if (error) goto getblk_failed; - if (ea_inode) { - /* Drop the extra ref on ea_inode. */ - error = ext4_xattr_inode_dec_ref(handle, - ea_inode); - if (error) - ext4_warning_inode(ea_inode, - "dec ref error=%d", - error); - iput(ea_inode); - ea_inode = NULL; - } lock_buffer(new_bh); error = ext4_journal_get_create_access(handle, sb, From fa60629380bbbf0952d2eb906da187b171f54529 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 13 Feb 2024 11:16:01 +0100 Subject: [PATCH 14/23] ext4: don't report EOPNOTSUPP errors from discard When ext4 is mounted without journal, with discard mount option, and on a device not supporting trim, we print error for each and every freed extent. This is not only useless but actively harmful. Instead ignore the EOPNOTSUPP error. Trim is only advisory anyway and when the filesystem has journal we silently ignore trim error as well. Signed-off-by: Jan Kara Reviewed-by: Zhang Yi Link: https://lore.kernel.org/r/20240213101601.17463-1-jack@suse.cz Signed-off-by: Theodore Ts'o --- fs/ext4/mballoc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index bd26f2a90751..85a91a61b761 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -6496,7 +6496,13 @@ do_more: if (test_opt(sb, DISCARD)) { err = ext4_issue_discard(sb, block_group, bit, count_clusters, NULL); - if (err && err != -EOPNOTSUPP) + /* + * Ignore EOPNOTSUPP error. This is consistent with + * what happens when using journal. + */ + if (err == -EOPNOTSUPP) + err = 0; + if (err) ext4_msg(sb, KERN_WARNING, "discard request in" " group:%u block:%d count:%lu failed" " with %d", block_group, bit, count, From a6b3bfe176e8a5b05ec4447404e412c2a3fc92cc Mon Sep 17 00:00:00 2001 From: Maximilian Heyne Date: Thu, 15 Feb 2024 15:50:09 +0000 Subject: [PATCH 15/23] ext4: fix corruption during on-line resize We observed a corruption during on-line resize of a file system that is larger than 16 TiB with 4k block size. With having more then 2^32 blocks resize_inode is turned off by default by mke2fs. The issue can be reproduced on a smaller file system for convenience by explicitly turning off resize_inode. An on-line resize across an 8 GiB boundary (the size of a meta block group in this setup) then leads to a corruption: dev=/dev/ # should be >= 16 GiB mkdir -p /corruption /sbin/mke2fs -t ext4 -b 4096 -O ^resize_inode $dev $((2 * 2**21 - 2**15)) mount -t ext4 $dev /corruption dd if=/dev/zero bs=4096 of=/corruption/test count=$((2*2**21 - 4*2**15)) sha1sum /corruption/test # 79d2658b39dcfd77274e435b0934028adafaab11 /corruption/test /sbin/resize2fs $dev $((2*2**21)) # drop page cache to force reload the block from disk echo 1 > /proc/sys/vm/drop_caches sha1sum /corruption/test # 3c2abc63cbf1a94c9e6977e0fbd72cd832c4d5c3 /corruption/test 2^21 = 2^15*2^6 equals 8 GiB whereof 2^15 is the number of blocks per block group and 2^6 are the number of block groups that make a meta block group. The last checksum might be different depending on how the file is laid out across the physical blocks. The actual corruption occurs at physical block 63*2^15 = 2064384 which would be the location of the backup of the meta block group's block descriptor. During the on-line resize the file system will be converted to meta_bg starting at s_first_meta_bg which is 2 in the example - meaning all block groups after 16 GiB. However, in ext4_flex_group_add we might add block groups that are not part of the first meta block group yet. In the reproducer we achieved this by substracting the size of a whole block group from the point where the meta block group would start. This must be considered when updating the backup block group descriptors to follow the non-meta_bg layout. The fix is to add a test whether the group to add is already part of the meta block group or not. Fixes: 01f795f9e0d67 ("ext4: add online resizing support for meta_bg and 64-bit file systems") Cc: Signed-off-by: Maximilian Heyne Tested-by: Srivathsa Dara Reviewed-by: Srivathsa Dara Link: https://lore.kernel.org/r/20240215155009.94493-1-mheyne@amazon.de Signed-off-by: Theodore Ts'o --- fs/ext4/resize.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 4d4a5a32e310..3c0d12382e06 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -1602,7 +1602,8 @@ exit_journal: int gdb_num = group / EXT4_DESC_PER_BLOCK(sb); int gdb_num_end = ((group + flex_gd->count - 1) / EXT4_DESC_PER_BLOCK(sb)); - int meta_bg = ext4_has_feature_meta_bg(sb); + int meta_bg = ext4_has_feature_meta_bg(sb) && + gdb_num >= le32_to_cpu(es->s_first_meta_bg); sector_t padding_blocks = meta_bg ? 0 : sbi->s_sbh->b_blocknr - ext4_group_first_block_no(sb, 0); From 40da553f5da020931e9cddf02948847a188c5223 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 19 Feb 2024 18:10:33 +0100 Subject: [PATCH 16/23] ext4: verify s_clusters_per_group even without bigalloc Currently we ignore s_clusters_per_group field in the on-disk superblock if bigalloc feature is not enabled. However e2fsprogs don't even open the filesystem if s_clusters_per_group is invalid. This results in an odd state where kernel happily works with the filesystem while even e2fsck refuses to touch it. Verify that s_clusters_per_group is valid even if bigalloc feature is not enabled to make things consistent. Due to current e2fsprogs behavior it is unlikely there are filesystems out in the wild (except for intentionally fuzzed ones) with invalid s_clusters_per_group counts. Signed-off-by: Jan Kara Reviewed-by: Zhang Yi Link: https://lore.kernel.org/r/20240219171033.22882-1-jack@suse.cz Signed-off-by: Theodore Ts'o --- fs/ext4/super.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 3f595090eb62..91e9f961993d 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -4422,22 +4422,6 @@ static int ext4_handle_clustersize(struct super_block *sb) } sbi->s_cluster_bits = le32_to_cpu(es->s_log_cluster_size) - le32_to_cpu(es->s_log_block_size); - sbi->s_clusters_per_group = - le32_to_cpu(es->s_clusters_per_group); - if (sbi->s_clusters_per_group > sb->s_blocksize * 8) { - ext4_msg(sb, KERN_ERR, - "#clusters per group too big: %lu", - sbi->s_clusters_per_group); - return -EINVAL; - } - if (sbi->s_blocks_per_group != - (sbi->s_clusters_per_group * (clustersize / sb->s_blocksize))) { - ext4_msg(sb, KERN_ERR, "blocks per group (%lu) and " - "clusters per group (%lu) inconsistent", - sbi->s_blocks_per_group, - sbi->s_clusters_per_group); - return -EINVAL; - } } else { if (clustersize != sb->s_blocksize) { ext4_msg(sb, KERN_ERR, @@ -4451,9 +4435,21 @@ static int ext4_handle_clustersize(struct super_block *sb) sbi->s_blocks_per_group); return -EINVAL; } - sbi->s_clusters_per_group = sbi->s_blocks_per_group; sbi->s_cluster_bits = 0; } + sbi->s_clusters_per_group = le32_to_cpu(es->s_clusters_per_group); + if (sbi->s_clusters_per_group > sb->s_blocksize * 8) { + ext4_msg(sb, KERN_ERR, "#clusters per group too big: %lu", + sbi->s_clusters_per_group); + return -EINVAL; + } + if (sbi->s_blocks_per_group != + (sbi->s_clusters_per_group * (clustersize / sb->s_blocksize))) { + ext4_msg(sb, KERN_ERR, + "blocks per group (%lu) and clusters per group (%lu) inconsistent", + sbi->s_blocks_per_group, sbi->s_clusters_per_group); + return -EINVAL; + } sbi->s_cluster_ratio = clustersize / sb->s_blocksize; /* Do we have standard group size of clustersize * 8 blocks ? */ From 708623737b0a28aff33bff0237862a2e08bc5c97 Mon Sep 17 00:00:00 2001 From: Chengming Zhou Date: Sat, 24 Feb 2024 13:48:22 +0000 Subject: [PATCH 17/23] ext4: remove SLAB_MEM_SPREAD flag usage The SLAB_MEM_SPREAD flag used to be implemented in SLAB, which was removed as of v6.8-rc1, so it became a dead flag since the commit 16a1d968358a ("mm/slab: remove mm/slab.c and slab_def.h"). And the series[1] went on to mark it obsolete to avoid confusion for users. Here we can just remove all its users, which has no functional change. [1] https://lore.kernel.org/all/20240223-slab-cleanup-flags-v2-1-02f1753e8303@suse.cz/ Signed-off-by: Chengming Zhou Link: https://lore.kernel.org/r/20240224134822.829456-1-chengming.zhou@linux.dev Signed-off-by: Theodore Ts'o --- fs/ext4/super.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 91e9f961993d..3585ecabe734 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1500,8 +1500,7 @@ static int __init init_inodecache(void) { ext4_inode_cachep = kmem_cache_create_usercopy("ext4_inode_cache", sizeof(struct ext4_inode_info), 0, - (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD| - SLAB_ACCOUNT), + SLAB_RECLAIM_ACCOUNT|SLAB_ACCOUNT, offsetof(struct ext4_inode_info, i_data), sizeof_field(struct ext4_inode_info, i_data), init_once); From 0efcd739fc07cefd5c54e202f66b4ea5def4776b Mon Sep 17 00:00:00 2001 From: Wenchao Hao Date: Mon, 26 Feb 2024 16:17:31 +0800 Subject: [PATCH 18/23] ext4: remove unused parameter biop in ext4_issue_discard() all caller of ext4_issue_discard() would set biop to NULL since 'commit 55cdd0af2bc5 ("ext4: get discard out of jbd2 commit kthread contex")', it's unnecessary to keep this parameter any more. Signed-off-by: Wenchao Hao Reviewed-by: Ritesh Harjani (IBM) Reviewed-by: Zhang Yi Link: https://lore.kernel.org/r/20240226081731.3224470-1-haowenchao2@huawei.com Signed-off-by: Theodore Ts'o --- fs/ext4/mballoc.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 85a91a61b761..12b3f196010b 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -3832,8 +3832,7 @@ void ext4_mb_release(struct super_block *sb) } static inline int ext4_issue_discard(struct super_block *sb, - ext4_group_t block_group, ext4_grpblk_t cluster, int count, - struct bio **biop) + ext4_group_t block_group, ext4_grpblk_t cluster, int count) { ext4_fsblk_t discard_block; @@ -3842,13 +3841,8 @@ static inline int ext4_issue_discard(struct super_block *sb, count = EXT4_C2B(EXT4_SB(sb), count); trace_ext4_discard_blocks(sb, (unsigned long long) discard_block, count); - if (biop) { - return __blkdev_issue_discard(sb->s_bdev, - (sector_t)discard_block << (sb->s_blocksize_bits - 9), - (sector_t)count << (sb->s_blocksize_bits - 9), - GFP_NOFS, biop); - } else - return sb_issue_discard(sb, discard_block, count, GFP_NOFS, 0); + + return sb_issue_discard(sb, discard_block, count, GFP_NOFS, 0); } static void ext4_free_data_in_buddy(struct super_block *sb, @@ -6495,7 +6489,7 @@ do_more: } else { if (test_opt(sb, DISCARD)) { err = ext4_issue_discard(sb, block_group, bit, - count_clusters, NULL); + count_clusters); /* * Ignore EOPNOTSUPP error. This is consistent with * what happens when using journal. @@ -6752,7 +6746,7 @@ __acquires(bitlock) */ mb_mark_used(e4b, &ex); ext4_unlock_group(sb, group); - ret = ext4_issue_discard(sb, group, start, count, NULL); + ret = ext4_issue_discard(sb, group, start, count); ext4_lock_group(sb, group); mb_free_blocks(NULL, e4b, start, ex.fe_len); return ret; From 07be778c70149321f785611a9c50125b904b0508 Mon Sep 17 00:00:00 2001 From: Srivathsa Dara Date: Tue, 27 Feb 2024 13:13:29 +0000 Subject: [PATCH 19/23] ext4: enable meta_bg only when new desc blocks are needed This patch addresses an issue observed when resize_inode is disabled and an online extension of a filesysyem is performed. When a filesystem is expanded to a size that does not require a addition of a new descriptor block, the meta_bg feature is being enabled even though no part of the filesystem uses this layout. This patch ensures that the meta_bg feature is only enabled if any of the added block groups utilize meta_bg layout. Signed-off-by: Srivathsa Dara Link: https://lore.kernel.org/r/20240227131329.2608466-1-srivathsa.d.dara@oracle.com Signed-off-by: Theodore Ts'o --- fs/ext4/resize.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 3c0d12382e06..0ba9837d65ca 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -2085,7 +2085,7 @@ retry: } } - if ((!resize_inode && !meta_bg) || n_blocks_count == o_blocks_count) { + if ((!resize_inode && !meta_bg && n_desc_blocks > o_desc_blocks) || n_blocks_count == o_blocks_count) { err = ext4_convert_meta_bg(sb, resize_inode); if (err) goto out; From d60c53694c6ff560963590971720d632bb3d481e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 27 Feb 2024 17:15:39 +0100 Subject: [PATCH 20/23] ext4: kunit: use dynamic inode allocation Storing an inode structure on the stack pushes some functions over the warning limit for stack frame size: In file included from fs/ext4/mballoc.c:7039: fs/ext4/mballoc-test.c:506:13: error: stack frame size (1032) exceeds limit (1024) in 'test_mark_diskspace_used' [-Werror,-Wframe-larger-than] 506 | static void test_mark_diskspace_used(struct kunit *test) | ^ Use kunit_kzalloc() for all inodes. There may be a better way to do it by preallocating the inode, which would result in a larger rework. Fixes: 2b81493f8eb6 ("ext4: Add unit test for ext4_mb_mark_diskspace_used") Signed-off-by: Arnd Bergmann Reviewed-by: Kemeng Shi Link: https://lore.kernel.org/r/20240227161548.2929881-1-arnd@kernel.org Signed-off-by: Theodore Ts'o --- fs/ext4/mballoc-test.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/fs/ext4/mballoc-test.c b/fs/ext4/mballoc-test.c index 12d0b22cabe1..3b43301054b6 100644 --- a/fs/ext4/mballoc-test.c +++ b/fs/ext4/mballoc-test.c @@ -332,14 +332,19 @@ static void mbt_kunit_exit(struct kunit *test) static void test_new_blocks_simple(struct kunit *test) { struct super_block *sb = (struct super_block *)test->priv; - struct inode inode = { .i_sb = sb, }; + struct inode *inode; struct ext4_allocation_request ar; ext4_group_t i, goal_group = TEST_GOAL_GROUP; int err = 0; ext4_fsblk_t found; struct ext4_sb_info *sbi = EXT4_SB(sb); - ar.inode = &inode; + inode = kunit_kzalloc(test, sizeof(*inode), GFP_KERNEL); + if (!inode) + return; + + inode->i_sb = sb; + ar.inode = inode; /* get block at goal */ ar.goal = ext4_group_first_block_no(sb, goal_group); @@ -441,15 +446,20 @@ test_free_blocks_simple_range(struct kunit *test, ext4_group_t goal_group, { struct super_block *sb = (struct super_block *)test->priv; struct ext4_sb_info *sbi = EXT4_SB(sb); - struct inode inode = { .i_sb = sb, }; + struct inode *inode; ext4_fsblk_t block; + inode = kunit_kzalloc(test, sizeof(*inode), GFP_KERNEL); + if (!inode) + return; + inode->i_sb = sb; + if (len == 0) return; block = ext4_group_first_block_no(sb, goal_group) + EXT4_C2B(sbi, start); - ext4_free_blocks_simple(&inode, block, len); + ext4_free_blocks_simple(inode, block, len); validate_free_blocks_simple(test, sb, goal_group, start, len); mbt_ctx_mark_used(sb, goal_group, 0, EXT4_CLUSTERS_PER_GROUP(sb)); } @@ -506,16 +516,21 @@ test_mark_diskspace_used_range(struct kunit *test, static void test_mark_diskspace_used(struct kunit *test) { struct super_block *sb = (struct super_block *)test->priv; - struct inode inode = { .i_sb = sb, }; + struct inode *inode; struct ext4_allocation_context ac; struct test_range ranges[TEST_RANGE_COUNT]; int i; mbt_generate_test_ranges(sb, ranges, TEST_RANGE_COUNT); + inode = kunit_kzalloc(test, sizeof(*inode), GFP_KERNEL); + if (!inode) + return; + inode->i_sb = sb; + ac.ac_status = AC_STATUS_FOUND; ac.ac_sb = sb; - ac.ac_inode = &inode; + ac.ac_inode = inode; for (i = 0; i < TEST_RANGE_COUNT; i++) test_mark_diskspace_used_range(test, &ac, ranges[i].start, ranges[i].len); From 8ffc0cd24c2a3ea340743db18b1a1e999176fbd3 Mon Sep 17 00:00:00 2001 From: Kemeng Shi Date: Tue, 5 Mar 2024 00:35:41 +0800 Subject: [PATCH 21/23] ext4: alloc test super block from sget This fix the oops in ext4 unit test which is cuased by NULL sb.s_user_ns as following: <4>[ 14.344565] map_id_range_down (kernel/user_namespace.c:318) <4>[ 14.345378] make_kuid (kernel/user_namespace.c:415) <4>[ 14.345998] inode_init_always (include/linux/fs.h:1375 fs/inode.c:174) <4>[ 14.346696] alloc_inode (fs/inode.c:268) <4>[ 14.347353] new_inode_pseudo (fs/inode.c:1007) <4>[ 14.348016] new_inode (fs/inode.c:1033) <4>[ 14.348644] ext4_mb_init (fs/ext4/mballoc.c:3404 fs/ext4/mballoc.c:3719) <4>[ 14.349312] mbt_kunit_init (fs/ext4/mballoc-test.c:57 fs/ext4/mballoc-test.c:314) <4>[ 14.349983] kunit_try_run_case (lib/kunit/test.c:388 lib/kunit/test.c:443) <4>[ 14.350696] kunit_generic_run_threadfn_adapter (lib/kunit/try-catch.c:30) <4>[ 14.351530] kthread (kernel/kthread.c:388) <4>[ 14.352168] ret_from_fork (arch/arm64/kernel/entry.S:861) <0>[ 14.353385] Code: 52808004 b8236ae7 72be5e44 b90004c4 (38e368a1) Alloc test super block from sget to properly initialize test super block to fix the issue. Signed-off-by: Kemeng Shi Reported-by: Linux Kernel Functional Testing Reported-by: Guenter Roeck Tested-by: Guenter Roeck Link: https://lore.kernel.org/r/20240304163543.6700-2-shikemeng@huaweicloud.com Signed-off-by: Theodore Ts'o --- fs/ext4/mballoc-test.c | 55 ++++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/fs/ext4/mballoc-test.c b/fs/ext4/mballoc-test.c index 3b43301054b6..f7a511b21162 100644 --- a/fs/ext4/mballoc-test.c +++ b/fs/ext4/mballoc-test.c @@ -21,16 +21,28 @@ struct mbt_ctx { }; struct mbt_ext4_super_block { - struct super_block sb; + struct ext4_super_block es; + struct ext4_sb_info sbi; struct mbt_ctx mbt_ctx; }; -#define MBT_CTX(_sb) (&(container_of((_sb), struct mbt_ext4_super_block, sb)->mbt_ctx)) +#define MBT_SB(_sb) (container_of((_sb)->s_fs_info, struct mbt_ext4_super_block, sbi)) +#define MBT_CTX(_sb) (&MBT_SB(_sb)->mbt_ctx) #define MBT_GRP_CTX(_sb, _group) (&MBT_CTX(_sb)->grp_ctx[_group]) static const struct super_operations mbt_sops = { }; +static void mbt_kill_sb(struct super_block *sb) +{ + generic_shutdown_super(sb); +} + +static struct file_system_type mbt_fs_type = { + .name = "mballoc test", + .kill_sb = mbt_kill_sb, +}; + static int mbt_mb_init(struct super_block *sb) { int ret; @@ -72,43 +84,54 @@ static void mbt_mb_release(struct super_block *sb) kfree(sb->s_bdev); } +static int mbt_set(struct super_block *sb, void *data) +{ + return 0; +} + static struct super_block *mbt_ext4_alloc_super_block(void) { - struct ext4_super_block *es = kzalloc(sizeof(*es), GFP_KERNEL); - struct ext4_sb_info *sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); - struct mbt_ext4_super_block *fsb = kzalloc(sizeof(*fsb), GFP_KERNEL); + struct mbt_ext4_super_block *fsb; + struct super_block *sb; + struct ext4_sb_info *sbi; - if (fsb == NULL || sbi == NULL || es == NULL) + fsb = kzalloc(sizeof(*fsb), GFP_KERNEL); + if (fsb == NULL) + return NULL; + + sb = sget(&mbt_fs_type, NULL, mbt_set, 0, NULL); + if (IS_ERR(sb)) goto out; + sbi = &fsb->sbi; + sbi->s_blockgroup_lock = kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL); if (!sbi->s_blockgroup_lock) - goto out; + goto out_deactivate; bgl_lock_init(sbi->s_blockgroup_lock); - sbi->s_es = es; - fsb->sb.s_fs_info = sbi; + sbi->s_es = &fsb->es; + sb->s_fs_info = sbi; - return &fsb->sb; + up_write(&sb->s_umount); + return sb; +out_deactivate: + deactivate_locked_super(sb); out: kfree(fsb); - kfree(sbi); - kfree(es); return NULL; } static void mbt_ext4_free_super_block(struct super_block *sb) { - struct mbt_ext4_super_block *fsb = - container_of(sb, struct mbt_ext4_super_block, sb); + struct mbt_ext4_super_block *fsb = MBT_SB(sb); struct ext4_sb_info *sbi = EXT4_SB(sb); kfree(sbi->s_blockgroup_lock); - kfree(sbi->s_es); - kfree(sbi); + deactivate_super(sb); kfree(fsb); } From ad943758e0ebd881d031b657a3f389315bf3a101 Mon Sep 17 00:00:00 2001 From: Kemeng Shi Date: Tue, 5 Mar 2024 00:35:42 +0800 Subject: [PATCH 22/23] ext4: hold group lock in ext4 kunit test Although there is no concurrent block allocation/free in unit test, internal functions mb_mark_used and mb_free_blocks assert group lock is always held. Acquire group before calling mb_mark_used and mb_free_blocks in unit test to avoid the assertion. Signed-off-by: Kemeng Shi Reported-by: Guenter Roeck Tested-by: Guenter Roeck Link: https://lore.kernel.org/r/20240304163543.6700-3-shikemeng@huaweicloud.com Signed-off-by: Theodore Ts'o --- fs/ext4/mballoc-test.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/ext4/mballoc-test.c b/fs/ext4/mballoc-test.c index f7a511b21162..8188d18ca09d 100644 --- a/fs/ext4/mballoc-test.c +++ b/fs/ext4/mballoc-test.c @@ -716,7 +716,10 @@ test_mb_mark_used_range(struct kunit *test, struct ext4_buddy *e4b, ex.fe_start = start; ex.fe_len = len; ex.fe_group = TEST_GOAL_GROUP; + + ext4_lock_group(sb, TEST_GOAL_GROUP); mb_mark_used(e4b, &ex); + ext4_unlock_group(sb, TEST_GOAL_GROUP); mb_set_bits(bitmap, start, len); /* bypass bb_free validatoin in ext4_mb_generate_buddy */ @@ -776,7 +779,9 @@ test_mb_free_blocks_range(struct kunit *test, struct ext4_buddy *e4b, if (len == 0) return; + ext4_lock_group(sb, e4b->bd_group); mb_free_blocks(NULL, e4b, start, len); + ext4_unlock_group(sb, e4b->bd_group); mb_clear_bits(bitmap, start, len); /* bypass bb_free validatoin in ext4_mb_generate_buddy */ @@ -820,7 +825,11 @@ static void test_mb_free_blocks(struct kunit *test) ex.fe_start = 0; ex.fe_len = EXT4_CLUSTERS_PER_GROUP(sb); ex.fe_group = TEST_GOAL_GROUP; + + ext4_lock_group(sb, TEST_GOAL_GROUP); mb_mark_used(&e4b, &ex); + ext4_unlock_group(sb, TEST_GOAL_GROUP); + grp->bb_free = 0; memset(bitmap, 0xff, sb->s_blocksize); From 0ecae5410ab526225293d2591ca4632b22c2fd8c Mon Sep 17 00:00:00 2001 From: Kemeng Shi Date: Tue, 5 Mar 2024 00:35:43 +0800 Subject: [PATCH 23/23] ext4: initialize sbi->s_freeclusters_counter and sbi->s_dirtyclusters_counter before use in kunit test Fix that sbi->s_freeclusters_counter and sbi->s_dirtyclusters_counter are used before initialization. Signed-off-by: Kemeng Shi Reported-by: Guenter Roeck Tested-by: Guenter Roeck Link: https://lore.kernel.org/r/20240304163543.6700-4-shikemeng@huaweicloud.com Signed-off-by: Theodore Ts'o --- fs/ext4/mballoc-test.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/fs/ext4/mballoc-test.c b/fs/ext4/mballoc-test.c index 8188d18ca09d..044ca5238f41 100644 --- a/fs/ext4/mballoc-test.c +++ b/fs/ext4/mballoc-test.c @@ -45,6 +45,7 @@ static struct file_system_type mbt_fs_type = { static int mbt_mb_init(struct super_block *sb) { + ext4_fsblk_t block; int ret; /* needed by ext4_mb_init->bdev_nonrot(sb->s_bdev) */ @@ -69,8 +70,23 @@ static int mbt_mb_init(struct super_block *sb) if (ret != 0) goto err_out; + block = ext4_count_free_clusters(sb); + ret = percpu_counter_init(&EXT4_SB(sb)->s_freeclusters_counter, block, + GFP_KERNEL); + if (ret != 0) + goto err_mb_release; + + ret = percpu_counter_init(&EXT4_SB(sb)->s_dirtyclusters_counter, 0, + GFP_KERNEL); + if (ret != 0) + goto err_freeclusters; + return 0; +err_freeclusters: + percpu_counter_destroy(&EXT4_SB(sb)->s_freeclusters_counter); +err_mb_release: + ext4_mb_release(sb); err_out: kfree(sb->s_bdev->bd_queue); kfree(sb->s_bdev); @@ -79,6 +95,8 @@ err_out: static void mbt_mb_release(struct super_block *sb) { + percpu_counter_destroy(&EXT4_SB(sb)->s_dirtyclusters_counter); + percpu_counter_destroy(&EXT4_SB(sb)->s_freeclusters_counter); ext4_mb_release(sb); kfree(sb->s_bdev->bd_queue); kfree(sb->s_bdev); @@ -333,7 +351,7 @@ static int mbt_kunit_init(struct kunit *test) ext4_mb_mark_context, ext4_mb_mark_context_stub); - /* stub function will be called in mt_mb_init->ext4_mb_init */ + /* stub function will be called in mbt_mb_init->ext4_mb_init */ if (mbt_mb_init(sb) != 0) { mbt_ctx_release(sb); mbt_ext4_free_super_block(sb);