1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Written by Ojaswin Mujoo <ojaswin@linux.ibm.com> (IBM) 4 * 5 * These Kunit tests are designed to test the functionality of 6 * extent split and conversion in ext4. 7 * 8 * Currently, ext4 can split extents in 2 ways: 9 * 1. By splitting the extents in the extent tree and optionally converting them 10 * to written or unwritten based on flags passed. 11 * 2. In case 1 encounters an error, ext4 instead zerooes out the unwritten 12 * areas of the extent and marks the complete extent written. 13 * 14 * The primary function that handles this is ext4_split_convert_extents(). 15 * 16 * We test both of the methods of split. The behavior we try to enforce is: 17 * 1. When passing EXT4_GET_BLOCKS_CONVERT flag to ext4_split_convert_extents(), 18 * the split extent should be converted to initialized. 19 * 2. When passing EXT4_GET_BLOCKS_CONVERT_UNWRITTEN flag to 20 * ext4_split_convert_extents(), the split extent should be converted to 21 * uninitialized. 22 * 3. In case we use the zeroout method, then we should correctly write zeroes 23 * to the unwritten areas of the extent and we should not corrupt/leak any 24 * data. 25 * 26 * Enforcing 1 and 2 is straight forward, we just setup a minimal inode with 27 * extent tree, call ext4_split_convert_extents() and check the final state of 28 * the extent tree. 29 * 30 * For zeroout testing, we maintain a separate buffer which represents the disk 31 * data corresponding to the extents. We then override ext4's zeroout functions 32 * to instead write zeroes to our buffer. Then, we override 33 * ext4_ext_insert_extent() to return -ENOSPC, which triggers the zeroout. 34 * Finally, we check the state of the extent tree and zeroout buffer to confirm 35 * everything went well. 36 */ 37 38 #include <kunit/test.h> 39 #include <kunit/static_stub.h> 40 #include <linux/gfp_types.h> 41 #include <linux/stddef.h> 42 43 #include "ext4.h" 44 #include "ext4_extents.h" 45 46 #define EXT_DATA_PBLK 100 47 #define EXT_DATA_LBLK 10 48 #define EXT_DATA_LEN 3 49 50 struct kunit_ctx { 51 /* 52 * Ext4 inode which has only 1 unwrit extent 53 */ 54 struct ext4_inode_info *k_ei; 55 /* 56 * Represents the underlying data area (used for zeroout testing) 57 */ 58 char *k_data; 59 } k_ctx; 60 61 /* 62 * describes the state of an expected extent in extent tree. 63 */ 64 struct kunit_ext_state { 65 ext4_lblk_t ex_lblk; 66 ext4_lblk_t ex_len; 67 bool is_unwrit; 68 }; 69 70 /* 71 * describes the state of the data area of a writ extent. Used for testing 72 * correctness of zeroout. 73 */ 74 struct kunit_ext_data_state { 75 char exp_char; 76 ext4_lblk_t off_blk; 77 ext4_lblk_t len_blk; 78 }; 79 80 enum kunit_test_types { 81 TEST_SPLIT_CONVERT, 82 TEST_CREATE_BLOCKS, 83 }; 84 85 struct kunit_ext_test_param { 86 /* description of test */ 87 char *desc; 88 89 /* determines which function will be tested */ 90 int type; 91 92 /* is extent unwrit at beginning of test */ 93 bool is_unwrit_at_start; 94 95 /* flags to pass while splitting */ 96 int split_flags; 97 98 /* map describing range to split */ 99 struct ext4_map_blocks split_map; 100 101 /* disable zeroout */ 102 bool disable_zeroout; 103 104 /* no of extents expected after split */ 105 int nr_exp_ext; 106 107 /* 108 * expected state of extents after split. We will never split into more 109 * than 3 extents 110 */ 111 struct kunit_ext_state exp_ext_state[3]; 112 113 /* Below fields used for zeroout tests */ 114 115 bool is_zeroout_test; 116 /* 117 * no of expected data segments (zeroout tests). Example, if we expect 118 * data to be 4kb 0s, followed by 8kb non-zero, then nr_exp_data_segs==2 119 */ 120 int nr_exp_data_segs; 121 122 /* 123 * expected state of data area after zeroout. 124 */ 125 struct kunit_ext_data_state exp_data_state[3]; 126 }; 127 128 static void ext_kill_sb(struct super_block *sb) 129 { 130 generic_shutdown_super(sb); 131 } 132 133 static int ext_set(struct super_block *sb, void *data) 134 { 135 return 0; 136 } 137 138 static struct file_system_type ext_fs_type = { 139 .name = "extents test", 140 .kill_sb = ext_kill_sb, 141 }; 142 143 static void extents_kunit_exit(struct kunit *test) 144 { 145 struct ext4_sb_info *sbi = k_ctx.k_ei->vfs_inode.i_sb->s_fs_info; 146 147 kfree(sbi); 148 kfree(k_ctx.k_ei); 149 kfree(k_ctx.k_data); 150 } 151 152 static int __ext4_ext_dirty_stub(const char *where, unsigned int line, 153 handle_t *handle, struct inode *inode, 154 struct ext4_ext_path *path) 155 { 156 return 0; 157 } 158 159 static struct ext4_ext_path * 160 ext4_ext_insert_extent_stub(handle_t *handle, struct inode *inode, 161 struct ext4_ext_path *path, 162 struct ext4_extent *newext, int gb_flags) 163 { 164 return ERR_PTR(-ENOSPC); 165 } 166 167 /* 168 * We will zeroout the equivalent range in the data area 169 */ 170 static int ext4_ext_zeroout_stub(struct inode *inode, struct ext4_extent *ex) 171 { 172 ext4_lblk_t ee_block, off_blk; 173 loff_t ee_len; 174 loff_t off_bytes; 175 struct kunit *test = kunit_get_current_test(); 176 177 ee_block = le32_to_cpu(ex->ee_block); 178 ee_len = ext4_ext_get_actual_len(ex); 179 180 KUNIT_EXPECT_EQ_MSG(test, 1, ee_block >= EXT_DATA_LBLK, "ee_block=%d", 181 ee_block); 182 KUNIT_EXPECT_EQ(test, 1, 183 ee_block + ee_len <= EXT_DATA_LBLK + EXT_DATA_LEN); 184 185 off_blk = ee_block - EXT_DATA_LBLK; 186 off_bytes = off_blk << inode->i_sb->s_blocksize_bits; 187 memset(k_ctx.k_data + off_bytes, 0, 188 ee_len << inode->i_sb->s_blocksize_bits); 189 190 return 0; 191 } 192 193 static int ext4_issue_zeroout_stub(struct inode *inode, ext4_lblk_t lblk, 194 ext4_fsblk_t pblk, ext4_lblk_t len) 195 { 196 ext4_lblk_t off_blk; 197 loff_t off_bytes; 198 struct kunit *test = kunit_get_current_test(); 199 200 kunit_log(KERN_ALERT, test, 201 "%s: lblk=%u pblk=%llu len=%u", __func__, lblk, pblk, len); 202 KUNIT_EXPECT_EQ(test, 1, lblk >= EXT_DATA_LBLK); 203 KUNIT_EXPECT_EQ(test, 1, lblk + len <= EXT_DATA_LBLK + EXT_DATA_LEN); 204 KUNIT_EXPECT_EQ(test, 1, lblk - EXT_DATA_LBLK == pblk - EXT_DATA_PBLK); 205 206 off_blk = lblk - EXT_DATA_LBLK; 207 off_bytes = off_blk << inode->i_sb->s_blocksize_bits; 208 memset(k_ctx.k_data + off_bytes, 0, 209 len << inode->i_sb->s_blocksize_bits); 210 211 return 0; 212 } 213 214 static int extents_kunit_init(struct kunit *test) 215 { 216 struct ext4_extent_header *eh = NULL; 217 struct ext4_inode_info *ei; 218 struct inode *inode; 219 struct super_block *sb; 220 struct ext4_sb_info *sbi = NULL; 221 struct kunit_ext_test_param *param = 222 (struct kunit_ext_test_param *)(test->param_value); 223 int err; 224 225 sb = sget(&ext_fs_type, NULL, ext_set, 0, NULL); 226 if (IS_ERR(sb)) 227 return PTR_ERR(sb); 228 229 sb->s_blocksize = 4096; 230 sb->s_blocksize_bits = 12; 231 232 sbi = kzalloc_obj(struct ext4_sb_info); 233 if (sbi == NULL) 234 return -ENOMEM; 235 236 sbi->s_sb = sb; 237 sb->s_fs_info = sbi; 238 239 if (!param || !param->disable_zeroout) 240 sbi->s_extent_max_zeroout_kb = 32; 241 242 /* setup the mock inode */ 243 k_ctx.k_ei = kzalloc_obj(struct ext4_inode_info); 244 if (k_ctx.k_ei == NULL) 245 return -ENOMEM; 246 ei = k_ctx.k_ei; 247 inode = &ei->vfs_inode; 248 249 err = ext4_es_register_shrinker(sbi); 250 if (err) 251 return err; 252 253 ext4_es_init_tree(&ei->i_es_tree); 254 rwlock_init(&ei->i_es_lock); 255 INIT_LIST_HEAD(&ei->i_es_list); 256 ei->i_es_all_nr = 0; 257 ei->i_es_shk_nr = 0; 258 ei->i_es_shrink_lblk = 0; 259 260 ei->i_disksize = (EXT_DATA_LBLK + EXT_DATA_LEN + 10) 261 << sb->s_blocksize_bits; 262 ei->i_flags = 0; 263 ext4_set_inode_flag(inode, EXT4_INODE_EXTENTS); 264 inode->i_sb = sb; 265 266 k_ctx.k_data = kzalloc(EXT_DATA_LEN * 4096, GFP_KERNEL); 267 if (k_ctx.k_data == NULL) 268 return -ENOMEM; 269 270 /* 271 * set the data area to a junk value 272 */ 273 memset(k_ctx.k_data, 'X', EXT_DATA_LEN * 4096); 274 275 /* create a tree with depth 0 */ 276 eh = (struct ext4_extent_header *)k_ctx.k_ei->i_data; 277 278 /* Fill extent header */ 279 eh = ext_inode_hdr(&k_ctx.k_ei->vfs_inode); 280 eh->eh_depth = 0; 281 eh->eh_entries = cpu_to_le16(1); 282 eh->eh_magic = EXT4_EXT_MAGIC; 283 eh->eh_max = 284 cpu_to_le16(ext4_ext_space_root_idx(&k_ctx.k_ei->vfs_inode, 0)); 285 eh->eh_generation = 0; 286 287 /* 288 * add 1 extent in leaf node covering: 289 * - lblks: [EXT_DATA_LBLK, EXT_DATA_LBLK * + EXT_DATA_LEN) 290 * - pblks: [EXT_DATA_PBLK, EXT_DATA_PBLK + EXT_DATA_LEN) 291 */ 292 EXT_FIRST_EXTENT(eh)->ee_block = cpu_to_le32(EXT_DATA_LBLK); 293 EXT_FIRST_EXTENT(eh)->ee_len = cpu_to_le16(EXT_DATA_LEN); 294 ext4_ext_store_pblock(EXT_FIRST_EXTENT(eh), EXT_DATA_PBLK); 295 if (!param || param->is_unwrit_at_start) 296 ext4_ext_mark_unwritten(EXT_FIRST_EXTENT(eh)); 297 298 ext4_es_insert_extent(inode, EXT_DATA_LBLK, EXT_DATA_LEN, EXT_DATA_PBLK, 299 ext4_ext_is_unwritten(EXT_FIRST_EXTENT(eh)) ? 300 EXTENT_STATUS_UNWRITTEN : 301 EXTENT_STATUS_WRITTEN, 302 0); 303 304 /* Add stubs */ 305 kunit_activate_static_stub(test, __ext4_ext_dirty, 306 __ext4_ext_dirty_stub); 307 kunit_activate_static_stub(test, ext4_ext_zeroout, ext4_ext_zeroout_stub); 308 kunit_activate_static_stub(test, ext4_issue_zeroout, 309 ext4_issue_zeroout_stub); 310 return 0; 311 } 312 313 /* 314 * Return 1 if all bytes in the buf equal to c, else return the offset of first mismatch 315 */ 316 static int check_buffer(char *buf, int c, int size) 317 { 318 void *ret = NULL; 319 320 ret = memchr_inv(buf, c, size); 321 if (ret == NULL) 322 return 0; 323 324 kunit_log(KERN_ALERT, kunit_get_current_test(), 325 "# %s: wrong char found at offset %u (expected:%d got:%d)", __func__, 326 (u32)((char *)ret - buf), c, *((char *)ret)); 327 return 1; 328 } 329 330 /* 331 * Simulate a map block call by first calling ext4_map_query_blocks() to 332 * correctly populate map flags and pblk and then call the 333 * ext4_map_create_blocks() to do actual split and conversion. This is easier 334 * than calling ext4_map_blocks() because that needs mocking a lot of unrelated 335 * functions. 336 */ 337 static void ext4_map_create_blocks_helper(struct kunit *test, 338 struct inode *inode, 339 struct ext4_map_blocks *map, 340 int flags) 341 { 342 int retval = 0; 343 344 retval = ext4_map_query_blocks(NULL, inode, map, flags); 345 if (retval < 0) { 346 KUNIT_FAIL(test, 347 "ext4_map_query_blocks() failed. Cannot proceed\n"); 348 return; 349 } 350 351 ext4_map_create_blocks(NULL, inode, map, flags); 352 } 353 354 static void test_split_convert(struct kunit *test) 355 { 356 struct ext4_ext_path *path; 357 struct inode *inode = &k_ctx.k_ei->vfs_inode; 358 struct ext4_extent *ex; 359 struct ext4_map_blocks map; 360 const struct kunit_ext_test_param *param = 361 (const struct kunit_ext_test_param *)(test->param_value); 362 int blkbits = inode->i_sb->s_blocksize_bits; 363 364 if (param->is_zeroout_test) 365 /* 366 * Force zeroout by making ext4_ext_insert_extent return ENOSPC 367 */ 368 kunit_activate_static_stub(test, ext4_ext_insert_extent, 369 ext4_ext_insert_extent_stub); 370 371 path = ext4_find_extent(inode, EXT_DATA_LBLK, NULL, EXT4_EX_NOCACHE); 372 ex = path->p_ext; 373 KUNIT_EXPECT_EQ(test, EXT_DATA_LBLK, le32_to_cpu(ex->ee_block)); 374 KUNIT_EXPECT_EQ(test, EXT_DATA_LEN, ext4_ext_get_actual_len(ex)); 375 KUNIT_EXPECT_EQ(test, param->is_unwrit_at_start, 376 ext4_ext_is_unwritten(ex)); 377 if (param->is_zeroout_test) 378 KUNIT_EXPECT_EQ(test, 0, 379 check_buffer(k_ctx.k_data, 'X', 380 EXT_DATA_LEN << blkbits)); 381 382 map.m_lblk = param->split_map.m_lblk; 383 map.m_len = param->split_map.m_len; 384 385 switch (param->type) { 386 case TEST_SPLIT_CONVERT: 387 path = ext4_split_convert_extents(NULL, inode, &map, path, 388 param->split_flags, NULL); 389 break; 390 case TEST_CREATE_BLOCKS: 391 ext4_map_create_blocks_helper(test, inode, &map, param->split_flags); 392 break; 393 default: 394 KUNIT_FAIL(test, "param->type %d not support.", param->type); 395 } 396 397 path = ext4_find_extent(inode, EXT_DATA_LBLK, NULL, EXT4_EX_NOCACHE); 398 ex = path->p_ext; 399 400 for (int i = 0; i < param->nr_exp_ext; i++) { 401 struct kunit_ext_state exp_ext = param->exp_ext_state[i]; 402 bool es_check_needed = param->type != TEST_SPLIT_CONVERT; 403 struct extent_status es; 404 int contains_ex, ex_end, es_end, es_pblk; 405 406 KUNIT_EXPECT_EQ(test, exp_ext.ex_lblk, 407 le32_to_cpu(ex->ee_block)); 408 KUNIT_EXPECT_EQ(test, exp_ext.ex_len, 409 ext4_ext_get_actual_len(ex)); 410 KUNIT_EXPECT_EQ(test, exp_ext.is_unwrit, 411 ext4_ext_is_unwritten(ex)); 412 /* 413 * Confirm extent cache is in sync. Note that es cache can be 414 * merged even when on-disk extents are not so take that into 415 * account. 416 * 417 * Also, ext4_split_convert_extents() forces EXT4_EX_NOCACHE hence 418 * es status are ignored for that case. 419 */ 420 if (es_check_needed) { 421 ext4_es_lookup_extent(inode, le32_to_cpu(ex->ee_block), 422 NULL, &es, NULL); 423 424 ex_end = exp_ext.ex_lblk + exp_ext.ex_len; 425 es_end = es.es_lblk + es.es_len; 426 contains_ex = es.es_lblk <= exp_ext.ex_lblk && 427 es_end >= ex_end; 428 es_pblk = ext4_es_pblock(&es) + 429 (exp_ext.ex_lblk - es.es_lblk); 430 431 KUNIT_EXPECT_EQ(test, contains_ex, 1); 432 KUNIT_EXPECT_EQ(test, ext4_ext_pblock(ex), es_pblk); 433 KUNIT_EXPECT_EQ(test, 1, 434 (exp_ext.is_unwrit && 435 ext4_es_is_unwritten(&es)) || 436 (!exp_ext.is_unwrit && 437 ext4_es_is_written(&es))); 438 } 439 440 /* Only printed on failure */ 441 kunit_log(KERN_INFO, test, 442 "# [extent %d] exp: lblk:%d len:%d unwrit:%d \n", i, 443 exp_ext.ex_lblk, exp_ext.ex_len, exp_ext.is_unwrit); 444 kunit_log(KERN_INFO, test, 445 "# [extent %d] got: lblk:%d len:%d unwrit:%d\n", i, 446 le32_to_cpu(ex->ee_block), 447 ext4_ext_get_actual_len(ex), 448 ext4_ext_is_unwritten(ex)); 449 if (es_check_needed) 450 kunit_log( 451 KERN_INFO, test, 452 "# [extent %d] es: lblk:%d len:%d pblk:%lld type:0x%x\n", 453 i, es.es_lblk, es.es_len, ext4_es_pblock(&es), 454 ext4_es_type(&es)); 455 kunit_log(KERN_INFO, test, "------------------\n"); 456 457 ex = ex + 1; 458 } 459 460 if (!param->is_zeroout_test) 461 return; 462 463 /* 464 * Check that then data area has been zeroed out correctly 465 */ 466 for (int i = 0; i < param->nr_exp_data_segs; i++) { 467 loff_t off, len; 468 struct kunit_ext_data_state exp_data_seg = param->exp_data_state[i]; 469 470 off = exp_data_seg.off_blk << blkbits; 471 len = exp_data_seg.len_blk << blkbits; 472 KUNIT_EXPECT_EQ_MSG(test, 0, 473 check_buffer(k_ctx.k_data + off, 474 exp_data_seg.exp_char, len), 475 "# corruption in byte range [%lld, %lld)", 476 off, len); 477 } 478 479 return; 480 } 481 482 static const struct kunit_ext_test_param test_split_convert_params[] = { 483 /* unwrit to writ splits */ 484 { .desc = "split unwrit extent to 2 extents and convert 1st half writ", 485 .type = TEST_SPLIT_CONVERT, 486 .is_unwrit_at_start = 1, 487 .split_flags = EXT4_GET_BLOCKS_CONVERT, 488 .split_map = { .m_lblk = EXT_DATA_LBLK, .m_len = 1 }, 489 .nr_exp_ext = 2, 490 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 491 .ex_len = 1, 492 .is_unwrit = 0 }, 493 { .ex_lblk = EXT_DATA_LBLK + 1, 494 .ex_len = EXT_DATA_LEN - 1, 495 .is_unwrit = 1 } }, 496 .is_zeroout_test = 0 }, 497 { .desc = "split unwrit extent to 2 extents and convert 2nd half writ", 498 .type = TEST_SPLIT_CONVERT, 499 .is_unwrit_at_start = 1, 500 .split_flags = EXT4_GET_BLOCKS_CONVERT, 501 .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 1 }, 502 .nr_exp_ext = 2, 503 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 504 .ex_len = 1, 505 .is_unwrit = 1 }, 506 { .ex_lblk = EXT_DATA_LBLK + 1, 507 .ex_len = EXT_DATA_LEN - 1, 508 .is_unwrit = 0 } }, 509 .is_zeroout_test = 0 }, 510 { .desc = "split unwrit extent to 3 extents and convert 2nd half to writ", 511 .type = TEST_SPLIT_CONVERT, 512 .is_unwrit_at_start = 1, 513 .split_flags = EXT4_GET_BLOCKS_CONVERT, 514 .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 2 }, 515 .nr_exp_ext = 3, 516 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 517 .ex_len = 1, 518 .is_unwrit = 1 }, 519 { .ex_lblk = EXT_DATA_LBLK + 1, 520 .ex_len = EXT_DATA_LEN - 2, 521 .is_unwrit = 0 }, 522 { .ex_lblk = EXT_DATA_LBLK + 1 + (EXT_DATA_LEN - 2), 523 .ex_len = 1, 524 .is_unwrit = 1 } }, 525 .is_zeroout_test = 0 }, 526 527 /* writ to unwrit splits */ 528 { .desc = "split writ extent to 2 extents and convert 1st half unwrit", 529 .type = TEST_SPLIT_CONVERT, 530 .is_unwrit_at_start = 0, 531 .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, 532 .split_map = { .m_lblk = EXT_DATA_LBLK, .m_len = 1 }, 533 .nr_exp_ext = 2, 534 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 535 .ex_len = 1, 536 .is_unwrit = 1 }, 537 { .ex_lblk = EXT_DATA_LBLK + 1, 538 .ex_len = EXT_DATA_LEN - 1, 539 .is_unwrit = 0 } }, 540 .is_zeroout_test = 0 }, 541 { .desc = "split writ extent to 2 extents and convert 2nd half unwrit", 542 .type = TEST_SPLIT_CONVERT, 543 .is_unwrit_at_start = 0, 544 .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, 545 .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 1 }, 546 .nr_exp_ext = 2, 547 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 548 .ex_len = 1, 549 .is_unwrit = 0 }, 550 { .ex_lblk = EXT_DATA_LBLK + 1, 551 .ex_len = EXT_DATA_LEN - 1, 552 .is_unwrit = 1 } }, 553 .is_zeroout_test = 0 }, 554 { .desc = "split writ extent to 3 extents and convert 2nd half to unwrit", 555 .type = TEST_SPLIT_CONVERT, 556 .is_unwrit_at_start = 0, 557 .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, 558 .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 2 }, 559 .nr_exp_ext = 3, 560 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 561 .ex_len = 1, 562 .is_unwrit = 0 }, 563 { .ex_lblk = EXT_DATA_LBLK + 1, 564 .ex_len = EXT_DATA_LEN - 2, 565 .is_unwrit = 1 }, 566 { .ex_lblk = EXT_DATA_LBLK + 1 + (EXT_DATA_LEN - 2), 567 .ex_len = 1, 568 .is_unwrit = 0 } }, 569 .is_zeroout_test = 0 }, 570 571 /* 572 * ***** zeroout tests ***** 573 */ 574 /* unwrit to writ splits */ 575 { .desc = "split unwrit extent to 2 extents and convert 1st half writ (zeroout)", 576 .type = TEST_SPLIT_CONVERT, 577 .is_unwrit_at_start = 1, 578 .split_flags = EXT4_GET_BLOCKS_CONVERT, 579 .split_map = { .m_lblk = EXT_DATA_LBLK, .m_len = 1 }, 580 .nr_exp_ext = 1, 581 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 582 .ex_len = EXT_DATA_LEN, 583 .is_unwrit = 0 } }, 584 .is_zeroout_test = 1, 585 .nr_exp_data_segs = 2, 586 .exp_data_state = { { .exp_char = 'X', .off_blk = 0, .len_blk = 1 }, 587 { .exp_char = 0, 588 .off_blk = 1, 589 .len_blk = EXT_DATA_LEN - 1 } } }, 590 { .desc = "split unwrit extent to 2 extents and convert 2nd half writ (zeroout)", 591 .type = TEST_SPLIT_CONVERT, 592 .is_unwrit_at_start = 1, 593 .split_flags = EXT4_GET_BLOCKS_CONVERT, 594 .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 1 }, 595 .nr_exp_ext = 1, 596 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 597 .ex_len = EXT_DATA_LEN, 598 .is_unwrit = 0 } }, 599 .is_zeroout_test = 1, 600 .nr_exp_data_segs = 2, 601 .exp_data_state = { { .exp_char = 0, .off_blk = 0, .len_blk = 1 }, 602 { .exp_char = 'X', 603 .off_blk = 1, 604 .len_blk = EXT_DATA_LEN - 1 } } }, 605 { .desc = "split unwrit extent to 3 extents and convert 2nd half writ (zeroout)", 606 .type = TEST_SPLIT_CONVERT, 607 .is_unwrit_at_start = 1, 608 .split_flags = EXT4_GET_BLOCKS_CONVERT, 609 .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 2 }, 610 .nr_exp_ext = 1, 611 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 612 .ex_len = EXT_DATA_LEN, 613 .is_unwrit = 0 } }, 614 .is_zeroout_test = 1, 615 .nr_exp_data_segs = 3, 616 .exp_data_state = { { .exp_char = 0, .off_blk = 0, .len_blk = 1 }, 617 { .exp_char = 'X', .off_blk = 1, .len_blk = EXT_DATA_LEN - 2 }, 618 { .exp_char = 0, .off_blk = EXT_DATA_LEN - 1, .len_blk = 1 } } }, 619 620 /* writ to unwrit splits */ 621 { .desc = "split writ extent to 2 extents and convert 1st half unwrit (zeroout)", 622 .type = TEST_SPLIT_CONVERT, 623 .is_unwrit_at_start = 0, 624 .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, 625 .split_map = { .m_lblk = EXT_DATA_LBLK, .m_len = 1 }, 626 .nr_exp_ext = 1, 627 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 628 .ex_len = EXT_DATA_LEN, 629 .is_unwrit = 0 } }, 630 .is_zeroout_test = 1, 631 .nr_exp_data_segs = 2, 632 .exp_data_state = { { .exp_char = 0, .off_blk = 0, .len_blk = 1 }, 633 { .exp_char = 'X', 634 .off_blk = 1, 635 .len_blk = EXT_DATA_LEN - 1 } } }, 636 { .desc = "split writ extent to 2 extents and convert 2nd half unwrit (zeroout)", 637 .type = TEST_SPLIT_CONVERT, 638 .is_unwrit_at_start = 0, 639 .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, 640 .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 1 }, 641 .nr_exp_ext = 1, 642 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 643 .ex_len = EXT_DATA_LEN, 644 .is_unwrit = 0 } }, 645 .is_zeroout_test = 1, 646 .nr_exp_data_segs = 2, 647 .exp_data_state = { { .exp_char = 'X', .off_blk = 0, .len_blk = 1 }, 648 { .exp_char = 0, 649 .off_blk = 1, 650 .len_blk = EXT_DATA_LEN - 1 } } }, 651 { .desc = "split writ extent to 3 extents and convert 2nd half unwrit (zeroout)", 652 .type = TEST_SPLIT_CONVERT, 653 .is_unwrit_at_start = 0, 654 .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, 655 .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 2 }, 656 .nr_exp_ext = 1, 657 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 658 .ex_len = EXT_DATA_LEN, 659 .is_unwrit = 0 } }, 660 .is_zeroout_test = 1, 661 .nr_exp_data_segs = 3, 662 .exp_data_state = { { .exp_char = 'X', .off_blk = 0, .len_blk = 1 }, 663 { .exp_char = 0, 664 .off_blk = 1, 665 .len_blk = EXT_DATA_LEN - 2 }, 666 { .exp_char = 'X', 667 .off_blk = EXT_DATA_LEN - 1, 668 .len_blk = 1 } } }, 669 }; 670 671 /* Tests to trigger ext4_ext_map_blocks() -> convert_initialized_extent() */ 672 static const struct kunit_ext_test_param test_convert_initialized_params[] = { 673 /* writ to unwrit splits */ 674 { .desc = "split writ extent to 2 extents and convert 1st half unwrit", 675 .type = TEST_CREATE_BLOCKS, 676 .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, 677 .is_unwrit_at_start = 0, 678 .split_map = { .m_lblk = EXT_DATA_LBLK, .m_len = 1 }, 679 .nr_exp_ext = 2, 680 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 681 .ex_len = 1, 682 .is_unwrit = 1 }, 683 { .ex_lblk = EXT_DATA_LBLK + 1, 684 .ex_len = EXT_DATA_LEN - 1, 685 .is_unwrit = 0 } }, 686 .is_zeroout_test = 0 }, 687 { .desc = "split writ extent to 2 extents and convert 2nd half unwrit", 688 .type = TEST_CREATE_BLOCKS, 689 .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, 690 .is_unwrit_at_start = 0, 691 .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 1 }, 692 .nr_exp_ext = 2, 693 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 694 .ex_len = 1, 695 .is_unwrit = 0 }, 696 { .ex_lblk = EXT_DATA_LBLK + 1, 697 .ex_len = EXT_DATA_LEN - 1, 698 .is_unwrit = 1 } }, 699 .is_zeroout_test = 0 }, 700 { .desc = "split writ extent to 3 extents and convert 2nd half to unwrit", 701 .type = TEST_CREATE_BLOCKS, 702 .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, 703 .is_unwrit_at_start = 0, 704 .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 2 }, 705 .nr_exp_ext = 3, 706 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 707 .ex_len = 1, 708 .is_unwrit = 0 }, 709 { .ex_lblk = EXT_DATA_LBLK + 1, 710 .ex_len = EXT_DATA_LEN - 2, 711 .is_unwrit = 1 }, 712 { .ex_lblk = EXT_DATA_LBLK + 1 + (EXT_DATA_LEN - 2), 713 .ex_len = 1, 714 .is_unwrit = 0 } }, 715 .is_zeroout_test = 0 }, 716 717 /* writ to unwrit splits (zeroout) */ 718 { .desc = "split writ extent to 2 extents and convert 1st half unwrit (zeroout)", 719 .type = TEST_CREATE_BLOCKS, 720 .is_unwrit_at_start = 0, 721 .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, 722 .split_map = { .m_lblk = EXT_DATA_LBLK, .m_len = 1 }, 723 .nr_exp_ext = 1, 724 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 725 .ex_len = EXT_DATA_LEN, 726 .is_unwrit = 0 } }, 727 .is_zeroout_test = 1, 728 .nr_exp_data_segs = 2, 729 .exp_data_state = { { .exp_char = 0, .off_blk = 0, .len_blk = 1 }, 730 { .exp_char = 'X', 731 .off_blk = 1, 732 .len_blk = EXT_DATA_LEN - 1 } } }, 733 { .desc = "split writ extent to 2 extents and convert 2nd half unwrit (zeroout)", 734 .type = TEST_CREATE_BLOCKS, 735 .is_unwrit_at_start = 0, 736 .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, 737 .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 1 }, 738 .nr_exp_ext = 1, 739 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 740 .ex_len = EXT_DATA_LEN, 741 .is_unwrit = 0 } }, 742 .is_zeroout_test = 1, 743 .nr_exp_data_segs = 2, 744 .exp_data_state = { { .exp_char = 'X', .off_blk = 0, .len_blk = 1 }, 745 { .exp_char = 0, 746 .off_blk = 1, 747 .len_blk = EXT_DATA_LEN - 1 } } }, 748 { .desc = "split writ extent to 3 extents and convert 2nd half unwrit (zeroout)", 749 .type = TEST_CREATE_BLOCKS, 750 .is_unwrit_at_start = 0, 751 .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, 752 .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 2 }, 753 .nr_exp_ext = 1, 754 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 755 .ex_len = EXT_DATA_LEN, 756 .is_unwrit = 0 } }, 757 .is_zeroout_test = 1, 758 .nr_exp_data_segs = 3, 759 .exp_data_state = { { .exp_char = 'X', .off_blk = 0, .len_blk = 1 }, 760 { .exp_char = 0, 761 .off_blk = 1, 762 .len_blk = EXT_DATA_LEN - 2 }, 763 { .exp_char = 'X', 764 .off_blk = EXT_DATA_LEN - 1, 765 .len_blk = 1 } } }, 766 }; 767 768 /* Tests to trigger ext4_ext_map_blocks() -> ext4_ext_handle_unwritten_exntents() */ 769 static const struct kunit_ext_test_param test_handle_unwritten_params[] = { 770 /* unwrit to writ splits via endio path */ 771 { .desc = "split unwrit extent to 2 extents and convert 1st half writ (endio)", 772 .type = TEST_CREATE_BLOCKS, 773 .is_unwrit_at_start = 1, 774 .split_flags = EXT4_GET_BLOCKS_CONVERT, 775 .split_map = { .m_lblk = EXT_DATA_LBLK, .m_len = 1 }, 776 .nr_exp_ext = 2, 777 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 778 .ex_len = 1, 779 .is_unwrit = 0 }, 780 { .ex_lblk = EXT_DATA_LBLK + 1, 781 .ex_len = EXT_DATA_LEN - 1, 782 .is_unwrit = 1 } }, 783 .is_zeroout_test = 0 }, 784 { .desc = "split unwrit extent to 2 extents and convert 2nd half writ (endio)", 785 .type = TEST_CREATE_BLOCKS, 786 .is_unwrit_at_start = 1, 787 .split_flags = EXT4_GET_BLOCKS_CONVERT, 788 .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 1 }, 789 .nr_exp_ext = 2, 790 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 791 .ex_len = 1, 792 .is_unwrit = 1 }, 793 { .ex_lblk = EXT_DATA_LBLK + 1, 794 .ex_len = EXT_DATA_LEN - 1, 795 .is_unwrit = 0 } }, 796 .is_zeroout_test = 0 }, 797 { .desc = "split unwrit extent to 3 extents and convert 2nd half to writ (endio)", 798 .type = TEST_CREATE_BLOCKS, 799 .is_unwrit_at_start = 1, 800 .split_flags = EXT4_GET_BLOCKS_CONVERT, 801 .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 2 }, 802 .nr_exp_ext = 3, 803 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 804 .ex_len = 1, 805 .is_unwrit = 1 }, 806 { .ex_lblk = EXT_DATA_LBLK + 1, 807 .ex_len = EXT_DATA_LEN - 2, 808 .is_unwrit = 0 }, 809 { .ex_lblk = EXT_DATA_LBLK + 1 + (EXT_DATA_LEN - 2), 810 .ex_len = 1, 811 .is_unwrit = 1 } }, 812 .is_zeroout_test = 0 }, 813 814 /* unwrit to writ splits via non-endio path */ 815 { .desc = "split unwrit extent to 2 extents and convert 1st half writ (non endio)", 816 .type = TEST_CREATE_BLOCKS, 817 .is_unwrit_at_start = 1, 818 .split_flags = EXT4_GET_BLOCKS_CREATE, 819 .split_map = { .m_lblk = EXT_DATA_LBLK, .m_len = 1 }, 820 .nr_exp_ext = 2, 821 .disable_zeroout = true, 822 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 823 .ex_len = 1, 824 .is_unwrit = 0 }, 825 { .ex_lblk = EXT_DATA_LBLK + 1, 826 .ex_len = EXT_DATA_LEN - 1, 827 .is_unwrit = 1 } }, 828 .is_zeroout_test = 0 }, 829 { .desc = "split unwrit extent to 2 extents and convert 2nd half writ (non endio)", 830 .type = TEST_CREATE_BLOCKS, 831 .is_unwrit_at_start = 1, 832 .split_flags = EXT4_GET_BLOCKS_CREATE, 833 .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 1 }, 834 .nr_exp_ext = 2, 835 .disable_zeroout = true, 836 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 837 .ex_len = 1, 838 .is_unwrit = 1 }, 839 { .ex_lblk = EXT_DATA_LBLK + 1, 840 .ex_len = EXT_DATA_LEN - 1, 841 .is_unwrit = 0 } }, 842 .is_zeroout_test = 0 }, 843 { .desc = "split unwrit extent to 3 extents and convert 2nd half to writ (non endio)", 844 .type = TEST_CREATE_BLOCKS, 845 .is_unwrit_at_start = 1, 846 .split_flags = EXT4_GET_BLOCKS_CREATE, 847 .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 2 }, 848 .nr_exp_ext = 3, 849 .disable_zeroout = true, 850 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 851 .ex_len = 1, 852 .is_unwrit = 1 }, 853 { .ex_lblk = EXT_DATA_LBLK + 1, 854 .ex_len = EXT_DATA_LEN - 2, 855 .is_unwrit = 0 }, 856 { .ex_lblk = EXT_DATA_LBLK + 1 + (EXT_DATA_LEN - 2), 857 .ex_len = 1, 858 .is_unwrit = 1 } }, 859 .is_zeroout_test = 0 }, 860 861 /* 862 * ***** zeroout tests ***** 863 */ 864 /* unwrit to writ splits (endio)*/ 865 { .desc = "split unwrit extent to 2 extents and convert 1st half writ (endio, zeroout)", 866 .type = TEST_CREATE_BLOCKS, 867 .is_unwrit_at_start = 1, 868 .split_flags = EXT4_GET_BLOCKS_CONVERT, 869 .split_map = { .m_lblk = EXT_DATA_LBLK, .m_len = 1 }, 870 .nr_exp_ext = 1, 871 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 872 .ex_len = EXT_DATA_LEN, 873 .is_unwrit = 0 } }, 874 .is_zeroout_test = 1, 875 .nr_exp_data_segs = 2, 876 .exp_data_state = { { .exp_char = 'X', .off_blk = 0, .len_blk = 1 }, 877 { .exp_char = 0, 878 .off_blk = 1, 879 .len_blk = EXT_DATA_LEN - 1 } } }, 880 { .desc = "split unwrit extent to 2 extents and convert 2nd half writ (endio, zeroout)", 881 .type = TEST_CREATE_BLOCKS, 882 .is_unwrit_at_start = 1, 883 .split_flags = EXT4_GET_BLOCKS_CONVERT, 884 .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 1 }, 885 .nr_exp_ext = 1, 886 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 887 .ex_len = EXT_DATA_LEN, 888 .is_unwrit = 0 } }, 889 .is_zeroout_test = 1, 890 .nr_exp_data_segs = 2, 891 .exp_data_state = { { .exp_char = 0, .off_blk = 0, .len_blk = 1 }, 892 { .exp_char = 'X', 893 .off_blk = 1, 894 .len_blk = EXT_DATA_LEN - 1 } } }, 895 { .desc = "split unwrit extent to 3 extents and convert 2nd half writ (endio, zeroout)", 896 .type = TEST_CREATE_BLOCKS, 897 .is_unwrit_at_start = 1, 898 .split_flags = EXT4_GET_BLOCKS_CONVERT, 899 .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 2 }, 900 .nr_exp_ext = 1, 901 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 902 .ex_len = EXT_DATA_LEN, 903 .is_unwrit = 0 } }, 904 .is_zeroout_test = 1, 905 .nr_exp_data_segs = 3, 906 .exp_data_state = { { .exp_char = 0, .off_blk = 0, .len_blk = 1 }, 907 { .exp_char = 'X', 908 .off_blk = 1, 909 .len_blk = EXT_DATA_LEN - 2 }, 910 { .exp_char = 0, 911 .off_blk = EXT_DATA_LEN - 1, 912 .len_blk = 1 } } }, 913 914 /* unwrit to writ splits (non-endio)*/ 915 { .desc = "split unwrit extent to 2 extents and convert 1st half writ (non-endio, zeroout)", 916 .type = TEST_CREATE_BLOCKS, 917 .is_unwrit_at_start = 1, 918 .split_flags = EXT4_GET_BLOCKS_CREATE, 919 .split_map = { .m_lblk = EXT_DATA_LBLK, .m_len = 1 }, 920 .nr_exp_ext = 1, 921 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 922 .ex_len = EXT_DATA_LEN, 923 .is_unwrit = 0 } }, 924 .is_zeroout_test = 1, 925 .nr_exp_data_segs = 2, 926 .exp_data_state = { { .exp_char = 'X', .off_blk = 0, .len_blk = 1 }, 927 { .exp_char = 0, 928 .off_blk = 1, 929 .len_blk = EXT_DATA_LEN - 1 } } }, 930 { .desc = "split unwrit extent to 2 extents and convert 2nd half writ (non-endio, zeroout)", 931 .type = TEST_CREATE_BLOCKS, 932 .is_unwrit_at_start = 1, 933 .split_flags = EXT4_GET_BLOCKS_CREATE, 934 .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 1 }, 935 .nr_exp_ext = 1, 936 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 937 .ex_len = EXT_DATA_LEN, 938 .is_unwrit = 0 } }, 939 .is_zeroout_test = 1, 940 .nr_exp_data_segs = 2, 941 .exp_data_state = { { .exp_char = 0, .off_blk = 0, .len_blk = 1 }, 942 { .exp_char = 'X', 943 .off_blk = 1, 944 .len_blk = EXT_DATA_LEN - 1 } } }, 945 { .desc = "split unwrit extent to 3 extents and convert 2nd half writ (non-endio, zeroout)", 946 .type = TEST_CREATE_BLOCKS, 947 .is_unwrit_at_start = 1, 948 .split_flags = EXT4_GET_BLOCKS_CREATE, 949 .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 2 }, 950 .nr_exp_ext = 1, 951 .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, 952 .ex_len = EXT_DATA_LEN, 953 .is_unwrit = 0 } }, 954 .is_zeroout_test = 1, 955 .nr_exp_data_segs = 3, 956 .exp_data_state = { { .exp_char = 0, .off_blk = 0, .len_blk = 1 }, 957 { .exp_char = 'X', 958 .off_blk = 1, 959 .len_blk = EXT_DATA_LEN - 2 }, 960 { .exp_char = 0, 961 .off_blk = EXT_DATA_LEN - 1, 962 .len_blk = 1 } } }, 963 }; 964 965 static void ext_get_desc(struct kunit *test, const void *p, char *desc) 966 967 { 968 struct kunit_ext_test_param *param = (struct kunit_ext_test_param *)p; 969 970 snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s %s\n", param->desc, 971 (param->type & TEST_CREATE_BLOCKS) ? "(highlevel)" : ""); 972 } 973 974 static int test_split_convert_param_init(struct kunit *test) 975 { 976 size_t arr_size = ARRAY_SIZE(test_split_convert_params); 977 978 kunit_register_params_array(test, test_split_convert_params, arr_size, 979 ext_get_desc); 980 return 0; 981 } 982 983 static int test_convert_initialized_param_init(struct kunit *test) 984 { 985 size_t arr_size = ARRAY_SIZE(test_convert_initialized_params); 986 987 kunit_register_params_array(test, test_convert_initialized_params, 988 arr_size, ext_get_desc); 989 return 0; 990 } 991 992 static int test_handle_unwritten_init(struct kunit *test) 993 { 994 size_t arr_size = ARRAY_SIZE(test_handle_unwritten_params); 995 996 kunit_register_params_array(test, test_handle_unwritten_params, 997 arr_size, ext_get_desc); 998 return 0; 999 } 1000 1001 /* 1002 * Note that we use KUNIT_CASE_PARAM_WITH_INIT() instead of the more compact 1003 * KUNIT_ARRAY_PARAM() because the later currently has a limitation causing the 1004 * output parsing to be prone to error. For more context: 1005 * 1006 * https://lore.kernel.org/linux-kselftest/aULJpTvJDw9ctUDe@li-dc0c254c-257c-11b2-a85c-98b6c1322444.ibm.com/ 1007 */ 1008 static struct kunit_case extents_test_cases[] = { 1009 KUNIT_CASE_PARAM_WITH_INIT(test_split_convert, kunit_array_gen_params, 1010 test_split_convert_param_init, NULL), 1011 KUNIT_CASE_PARAM_WITH_INIT(test_split_convert, kunit_array_gen_params, 1012 test_convert_initialized_param_init, NULL), 1013 KUNIT_CASE_PARAM_WITH_INIT(test_split_convert, kunit_array_gen_params, 1014 test_handle_unwritten_init, NULL), 1015 {} 1016 }; 1017 1018 static struct kunit_suite extents_test_suite = { 1019 .name = "ext4_extents_test", 1020 .init = extents_kunit_init, 1021 .exit = extents_kunit_exit, 1022 .test_cases = extents_test_cases, 1023 }; 1024 1025 kunit_test_suites(&extents_test_suite); 1026 1027 MODULE_LICENSE("GPL"); 1028