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