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