aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4
diff options
context:
space:
mode:
authorDmitry Monakhov <dmonakhov@openvz.org>2013-03-10 21:01:03 -0400
committerTheodore Ts'o <tytso@mit.edu>2013-03-10 21:01:03 -0400
commit921f266bc6bfe6ebb599c559f10443af314c19ec (patch)
tree270647b58f430887b9f510693b73c88a157bdb56 /fs/ext4
parentbd384364c1185ecb01f3b8242c915ccb5921c60d (diff)
ext4: add self-testing infrastructure to do a sanity check
This commit adds a self-testing infrastructure like extent tree does to do a sanity check for extent status tree. After status tree is as a extent cache, we'd better to make sure that it caches right result. After applied this commit, we will get a lot of messages when we run xfstests as below. ... kernel: ES len assertation failed for inode: 230 retval 1 != map->m_len 3 in ext4_map_blocks (allocation) ... kernel: ES cache assertation failed for inode: 230 es_cached ex [974/2/4781/20] != found ex [974/1/4781/1000] ... kernel: ES insert assertation failed for inode: 635 ex_status [0/45/21388/w] != es_status [44/1/21432/u] ... Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org> Signed-off-by: Zheng Liu <wenqing.lz@taobao.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs/ext4')
-rw-r--r--fs/ext4/extents_status.c175
-rw-r--r--fs/ext4/extents_status.h6
-rw-r--r--fs/ext4/inode.c96
3 files changed, 277 insertions, 0 deletions
diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c
index 37f9a2d8fd04..d2a8cb74676b 100644
--- a/fs/ext4/extents_status.c
+++ b/fs/ext4/extents_status.c
@@ -399,6 +399,179 @@ ext4_es_try_to_merge_right(struct inode *inode, struct extent_status *es)
399 return es; 399 return es;
400} 400}
401 401
402#ifdef ES_AGGRESSIVE_TEST
403static void ext4_es_insert_extent_ext_check(struct inode *inode,
404 struct extent_status *es)
405{
406 struct ext4_ext_path *path = NULL;
407 struct ext4_extent *ex;
408 ext4_lblk_t ee_block;
409 ext4_fsblk_t ee_start;
410 unsigned short ee_len;
411 int depth, ee_status, es_status;
412
413 path = ext4_ext_find_extent(inode, es->es_lblk, NULL);
414 if (IS_ERR(path))
415 return;
416
417 depth = ext_depth(inode);
418 ex = path[depth].p_ext;
419
420 if (ex) {
421
422 ee_block = le32_to_cpu(ex->ee_block);
423 ee_start = ext4_ext_pblock(ex);
424 ee_len = ext4_ext_get_actual_len(ex);
425
426 ee_status = ext4_ext_is_uninitialized(ex) ? 1 : 0;
427 es_status = ext4_es_is_unwritten(es) ? 1 : 0;
428
429 /*
430 * Make sure ex and es are not overlap when we try to insert
431 * a delayed/hole extent.
432 */
433 if (!ext4_es_is_written(es) && !ext4_es_is_unwritten(es)) {
434 if (in_range(es->es_lblk, ee_block, ee_len)) {
435 pr_warn("ES insert assertation failed for "
436 "inode: %lu we can find an extent "
437 "at block [%d/%d/%llu/%c], but we "
438 "want to add an delayed/hole extent "
439 "[%d/%d/%llu/%llx]\n",
440 inode->i_ino, ee_block, ee_len,
441 ee_start, ee_status ? 'u' : 'w',
442 es->es_lblk, es->es_len,
443 ext4_es_pblock(es), ext4_es_status(es));
444 }
445 goto out;
446 }
447
448 /*
449 * We don't check ee_block == es->es_lblk, etc. because es
450 * might be a part of whole extent, vice versa.
451 */
452 if (es->es_lblk < ee_block ||
453 ext4_es_pblock(es) != ee_start + es->es_lblk - ee_block) {
454 pr_warn("ES insert assertation failed for inode: %lu "
455 "ex_status [%d/%d/%llu/%c] != "
456 "es_status [%d/%d/%llu/%c]\n", inode->i_ino,
457 ee_block, ee_len, ee_start,
458 ee_status ? 'u' : 'w', es->es_lblk, es->es_len,
459 ext4_es_pblock(es), es_status ? 'u' : 'w');
460 goto out;
461 }
462
463 if (ee_status ^ es_status) {
464 pr_warn("ES insert assertation failed for inode: %lu "
465 "ex_status [%d/%d/%llu/%c] != "
466 "es_status [%d/%d/%llu/%c]\n", inode->i_ino,
467 ee_block, ee_len, ee_start,
468 ee_status ? 'u' : 'w', es->es_lblk, es->es_len,
469 ext4_es_pblock(es), es_status ? 'u' : 'w');
470 }
471 } else {
472 /*
473 * We can't find an extent on disk. So we need to make sure
474 * that we don't want to add an written/unwritten extent.
475 */
476 if (!ext4_es_is_delayed(es) && !ext4_es_is_hole(es)) {
477 pr_warn("ES insert assertation failed for inode: %lu "
478 "can't find an extent at block %d but we want "
479 "to add an written/unwritten extent "
480 "[%d/%d/%llu/%llx]\n", inode->i_ino,
481 es->es_lblk, es->es_lblk, es->es_len,
482 ext4_es_pblock(es), ext4_es_status(es));
483 }
484 }
485out:
486 if (path) {
487 ext4_ext_drop_refs(path);
488 kfree(path);
489 }
490}
491
492static void ext4_es_insert_extent_ind_check(struct inode *inode,
493 struct extent_status *es)
494{
495 struct ext4_map_blocks map;
496 int retval;
497
498 /*
499 * Here we call ext4_ind_map_blocks to lookup a block mapping because
500 * 'Indirect' structure is defined in indirect.c. So we couldn't
501 * access direct/indirect tree from outside. It is too dirty to define
502 * this function in indirect.c file.
503 */
504
505 map.m_lblk = es->es_lblk;
506 map.m_len = es->es_len;
507
508 retval = ext4_ind_map_blocks(NULL, inode, &map, 0);
509 if (retval > 0) {
510 if (ext4_es_is_delayed(es) || ext4_es_is_hole(es)) {
511 /*
512 * We want to add a delayed/hole extent but this
513 * block has been allocated.
514 */
515 pr_warn("ES insert assertation failed for inode: %lu "
516 "We can find blocks but we want to add a "
517 "delayed/hole extent [%d/%d/%llu/%llx]\n",
518 inode->i_ino, es->es_lblk, es->es_len,
519 ext4_es_pblock(es), ext4_es_status(es));
520 return;
521 } else if (ext4_es_is_written(es)) {
522 if (retval != es->es_len) {
523 pr_warn("ES insert assertation failed for "
524 "inode: %lu retval %d != es_len %d\n",
525 inode->i_ino, retval, es->es_len);
526 return;
527 }
528 if (map.m_pblk != ext4_es_pblock(es)) {
529 pr_warn("ES insert assertation failed for "
530 "inode: %lu m_pblk %llu != "
531 "es_pblk %llu\n",
532 inode->i_ino, map.m_pblk,
533 ext4_es_pblock(es));
534 return;
535 }
536 } else {
537 /*
538 * We don't need to check unwritten extent because
539 * indirect-based file doesn't have it.
540 */
541 BUG_ON(1);
542 }
543 } else if (retval == 0) {
544 if (ext4_es_is_written(es)) {
545 pr_warn("ES insert assertation failed for inode: %lu "
546 "We can't find the block but we want to add "
547 "an written extent [%d/%d/%llu/%llx]\n",
548 inode->i_ino, es->es_lblk, es->es_len,
549 ext4_es_pblock(es), ext4_es_status(es));
550 return;
551 }
552 }
553}
554
555static inline void ext4_es_insert_extent_check(struct inode *inode,
556 struct extent_status *es)
557{
558 /*
559 * We don't need to worry about the race condition because
560 * caller takes i_data_sem locking.
561 */
562 BUG_ON(!rwsem_is_locked(&EXT4_I(inode)->i_data_sem));
563 if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
564 ext4_es_insert_extent_ext_check(inode, es);
565 else
566 ext4_es_insert_extent_ind_check(inode, es);
567}
568#else
569static inline void ext4_es_insert_extent_check(struct inode *inode,
570 struct extent_status *es)
571{
572}
573#endif
574
402static int __es_insert_extent(struct inode *inode, struct extent_status *newes) 575static int __es_insert_extent(struct inode *inode, struct extent_status *newes)
403{ 576{
404 struct ext4_es_tree *tree = &EXT4_I(inode)->i_es_tree; 577 struct ext4_es_tree *tree = &EXT4_I(inode)->i_es_tree;
@@ -481,6 +654,8 @@ int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
481 ext4_es_store_status(&newes, status); 654 ext4_es_store_status(&newes, status);
482 trace_ext4_es_insert_extent(inode, &newes); 655 trace_ext4_es_insert_extent(inode, &newes);
483 656
657 ext4_es_insert_extent_check(inode, &newes);
658
484 write_lock(&EXT4_I(inode)->i_es_lock); 659 write_lock(&EXT4_I(inode)->i_es_lock);
485 err = __es_remove_extent(inode, lblk, end); 660 err = __es_remove_extent(inode, lblk, end);
486 if (err != 0) 661 if (err != 0)
diff --git a/fs/ext4/extents_status.h b/fs/ext4/extents_status.h
index f190dfe969da..56140ad4150b 100644
--- a/fs/ext4/extents_status.h
+++ b/fs/ext4/extents_status.h
@@ -21,6 +21,12 @@
21#endif 21#endif
22 22
23/* 23/*
24 * With ES_AGGRESSIVE_TEST defined, the result of es caching will be
25 * checked with old map_block's result.
26 */
27#define ES_AGGRESSIVE_TEST__
28
29/*
24 * These flags live in the high bits of extent_status.es_pblk 30 * These flags live in the high bits of extent_status.es_pblk
25 */ 31 */
26#define EXTENT_STATUS_WRITTEN (1ULL << 63) 32#define EXTENT_STATUS_WRITTEN (1ULL << 63)
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 95a0c62c5683..3186a43fa4b0 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -482,6 +482,58 @@ static pgoff_t ext4_num_dirty_pages(struct inode *inode, pgoff_t idx,
482 return num; 482 return num;
483} 483}
484 484
485#ifdef ES_AGGRESSIVE_TEST
486static void ext4_map_blocks_es_recheck(handle_t *handle,
487 struct inode *inode,
488 struct ext4_map_blocks *es_map,
489 struct ext4_map_blocks *map,
490 int flags)
491{
492 int retval;
493
494 map->m_flags = 0;
495 /*
496 * There is a race window that the result is not the same.
497 * e.g. xfstests #223 when dioread_nolock enables. The reason
498 * is that we lookup a block mapping in extent status tree with
499 * out taking i_data_sem. So at the time the unwritten extent
500 * could be converted.
501 */
502 if (!(flags & EXT4_GET_BLOCKS_NO_LOCK))
503 down_read((&EXT4_I(inode)->i_data_sem));
504 if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
505 retval = ext4_ext_map_blocks(handle, inode, map, flags &
506 EXT4_GET_BLOCKS_KEEP_SIZE);
507 } else {
508 retval = ext4_ind_map_blocks(handle, inode, map, flags &
509 EXT4_GET_BLOCKS_KEEP_SIZE);
510 }
511 if (!(flags & EXT4_GET_BLOCKS_NO_LOCK))
512 up_read((&EXT4_I(inode)->i_data_sem));
513 /*
514 * Clear EXT4_MAP_FROM_CLUSTER and EXT4_MAP_BOUNDARY flag
515 * because it shouldn't be marked in es_map->m_flags.
516 */
517 map->m_flags &= ~(EXT4_MAP_FROM_CLUSTER | EXT4_MAP_BOUNDARY);
518
519 /*
520 * We don't check m_len because extent will be collpased in status
521 * tree. So the m_len might not equal.
522 */
523 if (es_map->m_lblk != map->m_lblk ||
524 es_map->m_flags != map->m_flags ||
525 es_map->m_pblk != map->m_pblk) {
526 printk("ES cache assertation failed for inode: %lu "
527 "es_cached ex [%d/%d/%llu/%x] != "
528 "found ex [%d/%d/%llu/%x] retval %d flags %x\n",
529 inode->i_ino, es_map->m_lblk, es_map->m_len,
530 es_map->m_pblk, es_map->m_flags, map->m_lblk,
531 map->m_len, map->m_pblk, map->m_flags,
532 retval, flags);
533 }
534}
535#endif /* ES_AGGRESSIVE_TEST */
536
485/* 537/*
486 * The ext4_map_blocks() function tries to look up the requested blocks, 538 * The ext4_map_blocks() function tries to look up the requested blocks,
487 * and returns if the blocks are already mapped. 539 * and returns if the blocks are already mapped.
@@ -509,6 +561,11 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
509{ 561{
510 struct extent_status es; 562 struct extent_status es;
511 int retval; 563 int retval;
564#ifdef ES_AGGRESSIVE_TEST
565 struct ext4_map_blocks orig_map;
566
567 memcpy(&orig_map, map, sizeof(*map));
568#endif
512 569
513 map->m_flags = 0; 570 map->m_flags = 0;
514 ext_debug("ext4_map_blocks(): inode %lu, flag %d, max_blocks %u," 571 ext_debug("ext4_map_blocks(): inode %lu, flag %d, max_blocks %u,"
@@ -531,6 +588,10 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
531 } else { 588 } else {
532 BUG_ON(1); 589 BUG_ON(1);
533 } 590 }
591#ifdef ES_AGGRESSIVE_TEST
592 ext4_map_blocks_es_recheck(handle, inode, map,
593 &orig_map, flags);
594#endif
534 goto found; 595 goto found;
535 } 596 }
536 597
@@ -551,6 +612,15 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
551 int ret; 612 int ret;
552 unsigned long long status; 613 unsigned long long status;
553 614
615#ifdef ES_AGGRESSIVE_TEST
616 if (retval != map->m_len) {
617 printk("ES len assertation failed for inode: %lu "
618 "retval %d != map->m_len %d "
619 "in %s (lookup)\n", inode->i_ino, retval,
620 map->m_len, __func__);
621 }
622#endif
623
554 status = map->m_flags & EXT4_MAP_UNWRITTEN ? 624 status = map->m_flags & EXT4_MAP_UNWRITTEN ?
555 EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN; 625 EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
556 if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) && 626 if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) &&
@@ -643,6 +713,15 @@ found:
643 int ret; 713 int ret;
644 unsigned long long status; 714 unsigned long long status;
645 715
716#ifdef ES_AGGRESSIVE_TEST
717 if (retval != map->m_len) {
718 printk("ES len assertation failed for inode: %lu "
719 "retval %d != map->m_len %d "
720 "in %s (allocation)\n", inode->i_ino, retval,
721 map->m_len, __func__);
722 }
723#endif
724
646 status = map->m_flags & EXT4_MAP_UNWRITTEN ? 725 status = map->m_flags & EXT4_MAP_UNWRITTEN ?
647 EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN; 726 EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
648 if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) && 727 if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) &&
@@ -1768,6 +1847,11 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
1768 struct extent_status es; 1847 struct extent_status es;
1769 int retval; 1848 int retval;
1770 sector_t invalid_block = ~((sector_t) 0xffff); 1849 sector_t invalid_block = ~((sector_t) 0xffff);
1850#ifdef ES_AGGRESSIVE_TEST
1851 struct ext4_map_blocks orig_map;
1852
1853 memcpy(&orig_map, map, sizeof(*map));
1854#endif
1771 1855
1772 if (invalid_block < ext4_blocks_count(EXT4_SB(inode->i_sb)->s_es)) 1856 if (invalid_block < ext4_blocks_count(EXT4_SB(inode->i_sb)->s_es))
1773 invalid_block = ~0; 1857 invalid_block = ~0;
@@ -1809,6 +1893,9 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
1809 else 1893 else
1810 BUG_ON(1); 1894 BUG_ON(1);
1811 1895
1896#ifdef ES_AGGRESSIVE_TEST
1897 ext4_map_blocks_es_recheck(NULL, inode, map, &orig_map, 0);
1898#endif
1812 return retval; 1899 return retval;
1813 } 1900 }
1814 1901
@@ -1873,6 +1960,15 @@ add_delayed:
1873 int ret; 1960 int ret;
1874 unsigned long long status; 1961 unsigned long long status;
1875 1962
1963#ifdef ES_AGGRESSIVE_TEST
1964 if (retval != map->m_len) {
1965 printk("ES len assertation failed for inode: %lu "
1966 "retval %d != map->m_len %d "
1967 "in %s (lookup)\n", inode->i_ino, retval,
1968 map->m_len, __func__);
1969 }
1970#endif
1971
1876 status = map->m_flags & EXT4_MAP_UNWRITTEN ? 1972 status = map->m_flags & EXT4_MAP_UNWRITTEN ?
1877 EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN; 1973 EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
1878 ret = ext4_es_insert_extent(inode, map->m_lblk, map->m_len, 1974 ret = ext4_es_insert_extent(inode, map->m_lblk, map->m_len,