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