1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * KUnit test of ext4 multiblocks allocation. 4 */ 5 6 #include <kunit/test.h> 7 #include <kunit/static_stub.h> 8 9 #include "ext4.h" 10 11 struct mbt_grp_ctx { 12 struct buffer_head bitmap_bh; 13 /* desc and gd_bh are just the place holders for now */ 14 struct ext4_group_desc desc; 15 struct buffer_head gd_bh; 16 }; 17 18 struct mbt_ctx { 19 struct mbt_grp_ctx *grp_ctx; 20 }; 21 22 struct mbt_ext4_super_block { 23 struct super_block sb; 24 struct mbt_ctx mbt_ctx; 25 }; 26 27 #define MBT_CTX(_sb) (&(container_of((_sb), struct mbt_ext4_super_block, sb)->mbt_ctx)) 28 #define MBT_GRP_CTX(_sb, _group) (&MBT_CTX(_sb)->grp_ctx[_group]) 29 30 static struct super_block *mbt_ext4_alloc_super_block(void) 31 { 32 struct ext4_super_block *es = kzalloc(sizeof(*es), GFP_KERNEL); 33 struct ext4_sb_info *sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); 34 struct mbt_ext4_super_block *fsb = kzalloc(sizeof(*fsb), GFP_KERNEL); 35 36 if (fsb == NULL || sbi == NULL || es == NULL) 37 goto out; 38 39 sbi->s_es = es; 40 fsb->sb.s_fs_info = sbi; 41 return &fsb->sb; 42 43 out: 44 kfree(fsb); 45 kfree(sbi); 46 kfree(es); 47 return NULL; 48 } 49 50 static void mbt_ext4_free_super_block(struct super_block *sb) 51 { 52 struct mbt_ext4_super_block *fsb = 53 container_of(sb, struct mbt_ext4_super_block, sb); 54 struct ext4_sb_info *sbi = EXT4_SB(sb); 55 56 kfree(sbi->s_es); 57 kfree(sbi); 58 kfree(fsb); 59 } 60 61 struct mbt_ext4_block_layout { 62 unsigned char blocksize_bits; 63 unsigned int cluster_bits; 64 uint32_t blocks_per_group; 65 ext4_group_t group_count; 66 uint16_t desc_size; 67 }; 68 69 static void mbt_init_sb_layout(struct super_block *sb, 70 struct mbt_ext4_block_layout *layout) 71 { 72 struct ext4_sb_info *sbi = EXT4_SB(sb); 73 struct ext4_super_block *es = sbi->s_es; 74 75 sb->s_blocksize = 1UL << layout->blocksize_bits; 76 sb->s_blocksize_bits = layout->blocksize_bits; 77 78 sbi->s_groups_count = layout->group_count; 79 sbi->s_blocks_per_group = layout->blocks_per_group; 80 sbi->s_cluster_bits = layout->cluster_bits; 81 sbi->s_cluster_ratio = 1U << layout->cluster_bits; 82 sbi->s_clusters_per_group = layout->blocks_per_group >> 83 layout->cluster_bits; 84 sbi->s_desc_size = layout->desc_size; 85 86 es->s_first_data_block = cpu_to_le32(0); 87 es->s_blocks_count_lo = cpu_to_le32(layout->blocks_per_group * 88 layout->group_count); 89 } 90 91 static int mbt_grp_ctx_init(struct super_block *sb, 92 struct mbt_grp_ctx *grp_ctx) 93 { 94 grp_ctx->bitmap_bh.b_data = kzalloc(EXT4_BLOCK_SIZE(sb), GFP_KERNEL); 95 if (grp_ctx->bitmap_bh.b_data == NULL) 96 return -ENOMEM; 97 98 return 0; 99 } 100 101 static void mbt_grp_ctx_release(struct mbt_grp_ctx *grp_ctx) 102 { 103 kfree(grp_ctx->bitmap_bh.b_data); 104 grp_ctx->bitmap_bh.b_data = NULL; 105 } 106 107 static void mbt_ctx_mark_used(struct super_block *sb, ext4_group_t group, 108 unsigned int start, unsigned int len) 109 { 110 struct mbt_grp_ctx *grp_ctx = MBT_GRP_CTX(sb, group); 111 112 mb_set_bits(grp_ctx->bitmap_bh.b_data, start, len); 113 } 114 115 /* called after mbt_init_sb_layout */ 116 static int mbt_ctx_init(struct super_block *sb) 117 { 118 struct mbt_ctx *ctx = MBT_CTX(sb); 119 ext4_group_t i, ngroups = ext4_get_groups_count(sb); 120 121 ctx->grp_ctx = kcalloc(ngroups, sizeof(struct mbt_grp_ctx), 122 GFP_KERNEL); 123 if (ctx->grp_ctx == NULL) 124 return -ENOMEM; 125 126 for (i = 0; i < ngroups; i++) 127 if (mbt_grp_ctx_init(sb, &ctx->grp_ctx[i])) 128 goto out; 129 130 /* 131 * first data block(first cluster in first group) is used by 132 * metadata, mark it used to avoid to alloc data block at first 133 * block which will fail ext4_sb_block_valid check. 134 */ 135 mb_set_bits(ctx->grp_ctx[0].bitmap_bh.b_data, 0, 1); 136 137 return 0; 138 out: 139 while (i-- > 0) 140 mbt_grp_ctx_release(&ctx->grp_ctx[i]); 141 kfree(ctx->grp_ctx); 142 return -ENOMEM; 143 } 144 145 static void mbt_ctx_release(struct super_block *sb) 146 { 147 struct mbt_ctx *ctx = MBT_CTX(sb); 148 ext4_group_t i, ngroups = ext4_get_groups_count(sb); 149 150 for (i = 0; i < ngroups; i++) 151 mbt_grp_ctx_release(&ctx->grp_ctx[i]); 152 kfree(ctx->grp_ctx); 153 } 154 155 static struct buffer_head * 156 ext4_read_block_bitmap_nowait_stub(struct super_block *sb, ext4_group_t block_group, 157 bool ignore_locked) 158 { 159 struct mbt_grp_ctx *grp_ctx = MBT_GRP_CTX(sb, block_group); 160 161 /* paired with brelse from caller of ext4_read_block_bitmap_nowait */ 162 get_bh(&grp_ctx->bitmap_bh); 163 return &grp_ctx->bitmap_bh; 164 } 165 166 static int ext4_wait_block_bitmap_stub(struct super_block *sb, 167 ext4_group_t block_group, 168 struct buffer_head *bh) 169 { 170 return 0; 171 } 172 173 static struct ext4_group_desc * 174 ext4_get_group_desc_stub(struct super_block *sb, ext4_group_t block_group, 175 struct buffer_head **bh) 176 { 177 struct mbt_grp_ctx *grp_ctx = MBT_GRP_CTX(sb, block_group); 178 179 if (bh != NULL) 180 *bh = &grp_ctx->gd_bh; 181 182 return &grp_ctx->desc; 183 } 184 185 static int 186 ext4_mb_mark_context_stub(handle_t *handle, struct super_block *sb, bool state, 187 ext4_group_t group, ext4_grpblk_t blkoff, 188 ext4_grpblk_t len, int flags, 189 ext4_grpblk_t *ret_changed) 190 { 191 struct mbt_grp_ctx *grp_ctx = MBT_GRP_CTX(sb, group); 192 struct buffer_head *bitmap_bh = &grp_ctx->bitmap_bh; 193 194 if (state) 195 mb_set_bits(bitmap_bh->b_data, blkoff, len); 196 else 197 mb_clear_bits(bitmap_bh->b_data, blkoff, len); 198 199 return 0; 200 } 201 202 #define TEST_GOAL_GROUP 1 203 static int mbt_kunit_init(struct kunit *test) 204 { 205 struct mbt_ext4_block_layout *layout = 206 (struct mbt_ext4_block_layout *)(test->param_value); 207 struct super_block *sb; 208 int ret; 209 210 sb = mbt_ext4_alloc_super_block(); 211 if (sb == NULL) 212 return -ENOMEM; 213 214 mbt_init_sb_layout(sb, layout); 215 216 ret = mbt_ctx_init(sb); 217 if (ret != 0) { 218 mbt_ext4_free_super_block(sb); 219 return ret; 220 } 221 222 test->priv = sb; 223 kunit_activate_static_stub(test, 224 ext4_read_block_bitmap_nowait, 225 ext4_read_block_bitmap_nowait_stub); 226 kunit_activate_static_stub(test, 227 ext4_wait_block_bitmap, 228 ext4_wait_block_bitmap_stub); 229 kunit_activate_static_stub(test, 230 ext4_get_group_desc, 231 ext4_get_group_desc_stub); 232 kunit_activate_static_stub(test, 233 ext4_mb_mark_context, 234 ext4_mb_mark_context_stub); 235 return 0; 236 } 237 238 static void mbt_kunit_exit(struct kunit *test) 239 { 240 struct super_block *sb = (struct super_block *)test->priv; 241 242 mbt_ctx_release(sb); 243 mbt_ext4_free_super_block(sb); 244 } 245 246 static void test_new_blocks_simple(struct kunit *test) 247 { 248 struct super_block *sb = (struct super_block *)test->priv; 249 struct inode inode = { .i_sb = sb, }; 250 struct ext4_allocation_request ar; 251 ext4_group_t i, goal_group = TEST_GOAL_GROUP; 252 int err = 0; 253 ext4_fsblk_t found; 254 struct ext4_sb_info *sbi = EXT4_SB(sb); 255 256 ar.inode = &inode; 257 258 /* get block at goal */ 259 ar.goal = ext4_group_first_block_no(sb, goal_group); 260 found = ext4_mb_new_blocks_simple(&ar, &err); 261 KUNIT_ASSERT_EQ_MSG(test, ar.goal, found, 262 "failed to alloc block at goal, expected %llu found %llu", 263 ar.goal, found); 264 265 /* get block after goal in goal group */ 266 ar.goal = ext4_group_first_block_no(sb, goal_group); 267 found = ext4_mb_new_blocks_simple(&ar, &err); 268 KUNIT_ASSERT_EQ_MSG(test, ar.goal + EXT4_C2B(sbi, 1), found, 269 "failed to alloc block after goal in goal group, expected %llu found %llu", 270 ar.goal + 1, found); 271 272 /* get block after goal group */ 273 mbt_ctx_mark_used(sb, goal_group, 0, EXT4_CLUSTERS_PER_GROUP(sb)); 274 ar.goal = ext4_group_first_block_no(sb, goal_group); 275 found = ext4_mb_new_blocks_simple(&ar, &err); 276 KUNIT_ASSERT_EQ_MSG(test, 277 ext4_group_first_block_no(sb, goal_group + 1), found, 278 "failed to alloc block after goal group, expected %llu found %llu", 279 ext4_group_first_block_no(sb, goal_group + 1), found); 280 281 /* get block before goal group */ 282 for (i = goal_group; i < ext4_get_groups_count(sb); i++) 283 mbt_ctx_mark_used(sb, i, 0, EXT4_CLUSTERS_PER_GROUP(sb)); 284 ar.goal = ext4_group_first_block_no(sb, goal_group); 285 found = ext4_mb_new_blocks_simple(&ar, &err); 286 KUNIT_ASSERT_EQ_MSG(test, 287 ext4_group_first_block_no(sb, 0) + EXT4_C2B(sbi, 1), found, 288 "failed to alloc block before goal group, expected %llu found %llu", 289 ext4_group_first_block_no(sb, 0 + EXT4_C2B(sbi, 1)), found); 290 291 /* no block available, fail to allocate block */ 292 for (i = 0; i < ext4_get_groups_count(sb); i++) 293 mbt_ctx_mark_used(sb, i, 0, EXT4_CLUSTERS_PER_GROUP(sb)); 294 ar.goal = ext4_group_first_block_no(sb, goal_group); 295 found = ext4_mb_new_blocks_simple(&ar, &err); 296 KUNIT_ASSERT_NE_MSG(test, err, 0, 297 "unexpectedly get block when no block is available"); 298 } 299 300 static const struct mbt_ext4_block_layout mbt_test_layouts[] = { 301 { 302 .blocksize_bits = 10, 303 .cluster_bits = 3, 304 .blocks_per_group = 8192, 305 .group_count = 4, 306 .desc_size = 64, 307 }, 308 { 309 .blocksize_bits = 12, 310 .cluster_bits = 3, 311 .blocks_per_group = 8192, 312 .group_count = 4, 313 .desc_size = 64, 314 }, 315 { 316 .blocksize_bits = 16, 317 .cluster_bits = 3, 318 .blocks_per_group = 8192, 319 .group_count = 4, 320 .desc_size = 64, 321 }, 322 }; 323 324 static void mbt_show_layout(const struct mbt_ext4_block_layout *layout, 325 char *desc) 326 { 327 snprintf(desc, KUNIT_PARAM_DESC_SIZE, "block_bits=%d cluster_bits=%d " 328 "blocks_per_group=%d group_count=%d desc_size=%d\n", 329 layout->blocksize_bits, layout->cluster_bits, 330 layout->blocks_per_group, layout->group_count, 331 layout->desc_size); 332 } 333 KUNIT_ARRAY_PARAM(mbt_layouts, mbt_test_layouts, mbt_show_layout); 334 335 static struct kunit_case mbt_test_cases[] = { 336 KUNIT_CASE_PARAM(test_new_blocks_simple, mbt_layouts_gen_params), 337 {} 338 }; 339 340 static struct kunit_suite mbt_test_suite = { 341 .name = "ext4_mballoc_test", 342 .init = mbt_kunit_init, 343 .exit = mbt_kunit_exit, 344 .test_cases = mbt_test_cases, 345 }; 346 347 kunit_test_suites(&mbt_test_suite); 348 349 MODULE_LICENSE("GPL"); 350