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