]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
btrfs: introduce extra sanity checks for extent maps
authorQu Wenruo <wqu@suse.com>
Mon, 29 Apr 2024 22:23:03 +0000 (07:53 +0930)
committerDavid Sterba <dsterba@suse.com>
Thu, 11 Jul 2024 13:33:20 +0000 (15:33 +0200)
Since extent_map structure has the all the needed members to represent a
file extent directly, we can apply all the file extent sanity checks to
an extent map.

The new sanity checks will cross check both the old members
(block_start/block_len/orig_start) and the new members
(disk_bytenr/disk_num_bytes/offset).

There is a special case for offset/orig_start/start cross check, we only
do such sanity check for compressed extent, as only compressed
read/encoded write really utilize orig_start.
This can be proved by the cleanup patch of orig_start.

The checks happens at the following times:

- add_extent_mapping()
  This is for newly added extent map

- replace_extent_mapping()
  This is for btrfs_drop_extent_map_range() and split_extent_map()

- try_merge_map()

For a lot of call sites we have to properly populate all the members to
pass the sanity check, meanwhile the following code needs extra
modification:

- setup_file_extents() from inode-tests
  The file extents layout of setup_file_extents() is already too invalid
  that tree-checker would reject most of them in real world.

  However there is just a special unaligned regular extent which has
  mismatched disk_num_bytes (4096) and ram_bytes (4096 - 1).
  So instead of dropping the whole test case, here we just unify
  disk_num_bytes and ram_bytes to 4096 - 1.

- test_case_7() from extent-map-tests
  An extent is inserted with 16K length, but on-disk extent size is
  only 4K.
  This means it must be a compressed extent, so set the compressed flag
  for it.

Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/extent_map.c
fs/btrfs/relocation.c
fs/btrfs/tests/extent-map-tests.c
fs/btrfs/tests/inode-tests.c

index 0242f354e1af0dc6f898f7855b3b5a0abc9515ea..5d5938c8557a681c7c1e9cb93dd2ac56bccf6727 100644 (file)
@@ -284,8 +284,61 @@ static void merge_ondisk_extents(struct extent_map *prev, struct extent_map *nex
        next->offset = new_offset;
 }
 
+static void dump_extent_map(struct btrfs_fs_info *fs_info, const char *prefix,
+                           struct extent_map *em)
+{
+       if (!IS_ENABLED(CONFIG_BTRFS_DEBUG))
+               return;
+       btrfs_crit(fs_info,
+"%s, start=%llu len=%llu disk_bytenr=%llu disk_num_bytes=%llu ram_bytes=%llu offset=%llu orig_start=%llu block_start=%llu block_len=%llu flags=0x%x",
+               prefix, em->start, em->len, em->disk_bytenr, em->disk_num_bytes,
+               em->ram_bytes, em->offset, em->orig_start, em->block_start,
+               em->block_len, em->flags);
+       ASSERT(0);
+}
+
+/* Internal sanity checks for btrfs debug builds. */
+static void validate_extent_map(struct btrfs_fs_info *fs_info, struct extent_map *em)
+{
+       if (!IS_ENABLED(CONFIG_BTRFS_DEBUG))
+               return;
+       if (em->disk_bytenr < EXTENT_MAP_LAST_BYTE) {
+               if (em->disk_num_bytes == 0)
+                       dump_extent_map(fs_info, "zero disk_num_bytes", em);
+               if (em->offset + em->len > em->ram_bytes)
+                       dump_extent_map(fs_info, "ram_bytes too small", em);
+               if (em->offset + em->len > em->disk_num_bytes &&
+                   !extent_map_is_compressed(em))
+                       dump_extent_map(fs_info, "disk_num_bytes too small", em);
+
+               if (extent_map_is_compressed(em)) {
+                       if (em->block_start != em->disk_bytenr)
+                               dump_extent_map(fs_info,
+                                       "mismatch block_start/disk_bytenr/offset", em);
+                       if (em->disk_num_bytes != em->block_len)
+                               dump_extent_map(fs_info,
+                                       "mismatch disk_num_bytes/block_len", em);
+                       /*
+                        * Here we only check the start/orig_start/offset for
+                        * compressed extents as that's the only case where
+                        * orig_start is utilized.
+                        */
+                       if (em->orig_start != em->start - em->offset)
+                               dump_extent_map(fs_info,
+                                       "mismatch orig_start/offset/start", em);
+
+               } else if (em->block_start != em->disk_bytenr + em->offset) {
+                       dump_extent_map(fs_info,
+                               "mismatch block_start/disk_bytenr/offset", em);
+               }
+       } else if (em->offset) {
+               dump_extent_map(fs_info, "non-zero offset for hole/inline", em);
+       }
+}
+
 static void try_merge_map(struct btrfs_inode *inode, struct extent_map *em)
 {
+       struct btrfs_fs_info *fs_info = inode->root->fs_info;
        struct extent_map_tree *tree = &inode->extent_tree;
        struct extent_map *merge = NULL;
        struct rb_node *rb;
@@ -320,6 +373,7 @@ static void try_merge_map(struct btrfs_inode *inode, struct extent_map *em)
                                merge_ondisk_extents(merge, em);
                        em->flags |= EXTENT_FLAG_MERGED;
 
+                       validate_extent_map(fs_info, em);
                        rb_erase(&merge->rb_node, &tree->root);
                        RB_CLEAR_NODE(&merge->rb_node);
                        free_extent_map(merge);
@@ -335,6 +389,7 @@ static void try_merge_map(struct btrfs_inode *inode, struct extent_map *em)
                em->block_len += merge->block_len;
                if (em->disk_bytenr < EXTENT_MAP_LAST_BYTE)
                        merge_ondisk_extents(em, merge);
+               validate_extent_map(fs_info, em);
                rb_erase(&merge->rb_node, &tree->root);
                RB_CLEAR_NODE(&merge->rb_node);
                em->generation = max(em->generation, merge->generation);
@@ -446,6 +501,7 @@ static int add_extent_mapping(struct btrfs_inode *inode,
 
        lockdep_assert_held_write(&tree->lock);
 
+       validate_extent_map(fs_info, em);
        ret = tree_insert(&tree->root, em);
        if (ret)
                return ret;
@@ -549,10 +605,13 @@ static void replace_extent_mapping(struct btrfs_inode *inode,
                                   struct extent_map *new,
                                   int modified)
 {
+       struct btrfs_fs_info *fs_info = inode->root->fs_info;
        struct extent_map_tree *tree = &inode->extent_tree;
 
        lockdep_assert_held_write(&tree->lock);
 
+       validate_extent_map(fs_info, new);
+
        WARN_ON(cur->flags & EXTENT_FLAG_PINNED);
        ASSERT(extent_map_in_tree(cur));
        if (!(cur->flags & EXTENT_FLAG_LOGGING))
index 5f1a909a1d9186e04a45571eb802462ddbdc64e2..151ed1ebd291a4df1ecf7b496235cccbb57241c4 100644 (file)
@@ -2911,9 +2911,13 @@ static noinline_for_stack int setup_relocation_extent_mapping(struct inode *inod
                return -ENOMEM;
 
        em->start = start;
+       em->orig_start = start;
        em->len = end + 1 - start;
        em->block_len = em->len;
        em->block_start = block_start;
+       em->disk_bytenr = block_start;
+       em->disk_num_bytes = em->len;
+       em->ram_bytes = em->len;
        em->flags |= EXTENT_FLAG_PINNED;
 
        lock_extent(&BTRFS_I(inode)->io_tree, start, end, &cached_state);
index c511a1297956fa80c1936a96896bcccdbf2670aa..e73ac7a0869cbdd5813f5b3f6d7cf991946e7303 100644 (file)
@@ -78,6 +78,9 @@ static int test_case_1(struct btrfs_fs_info *fs_info, struct btrfs_inode *inode)
        em->len = SZ_16K;
        em->block_start = 0;
        em->block_len = SZ_16K;
+       em->disk_bytenr = 0;
+       em->disk_num_bytes = SZ_16K;
+       em->ram_bytes = SZ_16K;
        write_lock(&em_tree->lock);
        ret = btrfs_add_extent_mapping(inode, &em, em->start, em->len);
        write_unlock(&em_tree->lock);
@@ -96,9 +99,13 @@ static int test_case_1(struct btrfs_fs_info *fs_info, struct btrfs_inode *inode)
        }
 
        em->start = SZ_16K;
+       em->orig_start = SZ_16K;
        em->len = SZ_4K;
        em->block_start = SZ_32K; /* avoid merging */
        em->block_len = SZ_4K;
+       em->disk_bytenr = SZ_32K; /* avoid merging */
+       em->disk_num_bytes = SZ_4K;
+       em->ram_bytes = SZ_4K;
        write_lock(&em_tree->lock);
        ret = btrfs_add_extent_mapping(inode, &em, em->start, em->len);
        write_unlock(&em_tree->lock);
@@ -117,9 +124,13 @@ static int test_case_1(struct btrfs_fs_info *fs_info, struct btrfs_inode *inode)
 
        /* Add [0, 8K), should return [0, 16K) instead. */
        em->start = start;
+       em->orig_start = start;
        em->len = len;
        em->block_start = start;
        em->block_len = len;
+       em->disk_bytenr = start;
+       em->disk_num_bytes = len;
+       em->ram_bytes = len;
        write_lock(&em_tree->lock);
        ret = btrfs_add_extent_mapping(inode, &em, em->start, em->len);
        write_unlock(&em_tree->lock);
@@ -174,6 +185,9 @@ static int test_case_2(struct btrfs_fs_info *fs_info, struct btrfs_inode *inode)
        em->len = SZ_1K;
        em->block_start = EXTENT_MAP_INLINE;
        em->block_len = (u64)-1;
+       em->disk_bytenr = EXTENT_MAP_INLINE;
+       em->disk_num_bytes = 0;
+       em->ram_bytes = SZ_1K;
        write_lock(&em_tree->lock);
        ret = btrfs_add_extent_mapping(inode, &em, em->start, em->len);
        write_unlock(&em_tree->lock);
@@ -192,9 +206,13 @@ static int test_case_2(struct btrfs_fs_info *fs_info, struct btrfs_inode *inode)
        }
 
        em->start = SZ_4K;
+       em->orig_start = SZ_4K;
        em->len = SZ_4K;
        em->block_start = SZ_4K;
        em->block_len = SZ_4K;
+       em->disk_bytenr = SZ_4K;
+       em->disk_num_bytes = SZ_4K;
+       em->ram_bytes = SZ_4K;
        write_lock(&em_tree->lock);
        ret = btrfs_add_extent_mapping(inode, &em, em->start, em->len);
        write_unlock(&em_tree->lock);
@@ -216,6 +234,9 @@ static int test_case_2(struct btrfs_fs_info *fs_info, struct btrfs_inode *inode)
        em->len = SZ_1K;
        em->block_start = EXTENT_MAP_INLINE;
        em->block_len = (u64)-1;
+       em->disk_bytenr = EXTENT_MAP_INLINE;
+       em->disk_num_bytes = 0;
+       em->ram_bytes = SZ_1K;
        write_lock(&em_tree->lock);
        ret = btrfs_add_extent_mapping(inode, &em, em->start, em->len);
        write_unlock(&em_tree->lock);
@@ -262,9 +283,13 @@ static int __test_case_3(struct btrfs_fs_info *fs_info,
 
        /* Add [4K, 8K) */
        em->start = SZ_4K;
+       em->orig_start = SZ_4K;
        em->len = SZ_4K;
        em->block_start = SZ_4K;
        em->block_len = SZ_4K;
+       em->disk_bytenr = SZ_4K;
+       em->disk_num_bytes = SZ_4K;
+       em->ram_bytes = SZ_4K;
        write_lock(&em_tree->lock);
        ret = btrfs_add_extent_mapping(inode, &em, em->start, em->len);
        write_unlock(&em_tree->lock);
@@ -286,6 +311,9 @@ static int __test_case_3(struct btrfs_fs_info *fs_info,
        em->len = SZ_16K;
        em->block_start = 0;
        em->block_len = SZ_16K;
+       em->disk_bytenr = 0;
+       em->disk_num_bytes = SZ_16K;
+       em->ram_bytes = SZ_16K;
        write_lock(&em_tree->lock);
        ret = btrfs_add_extent_mapping(inode, &em, start, len);
        write_unlock(&em_tree->lock);
@@ -372,6 +400,9 @@ static int __test_case_4(struct btrfs_fs_info *fs_info,
        em->len = SZ_8K;
        em->block_start = 0;
        em->block_len = SZ_8K;
+       em->disk_bytenr = 0;
+       em->disk_num_bytes = SZ_8K;
+       em->ram_bytes = SZ_8K;
        write_lock(&em_tree->lock);
        ret = btrfs_add_extent_mapping(inode, &em, em->start, em->len);
        write_unlock(&em_tree->lock);
@@ -390,9 +421,13 @@ static int __test_case_4(struct btrfs_fs_info *fs_info,
 
        /* Add [8K, 32K) */
        em->start = SZ_8K;
+       em->orig_start = SZ_8K;
        em->len = 24 * SZ_1K;
        em->block_start = SZ_16K; /* avoid merging */
        em->block_len = 24 * SZ_1K;
+       em->disk_bytenr = SZ_16K; /* avoid merging */
+       em->disk_num_bytes = 24 * SZ_1K;
+       em->ram_bytes = 24 * SZ_1K;
        write_lock(&em_tree->lock);
        ret = btrfs_add_extent_mapping(inode, &em, em->start, em->len);
        write_unlock(&em_tree->lock);
@@ -410,9 +445,13 @@ static int __test_case_4(struct btrfs_fs_info *fs_info,
        }
        /* Add [0K, 32K) */
        em->start = 0;
+       em->orig_start = 0;
        em->len = SZ_32K;
        em->block_start = 0;
        em->block_len = SZ_32K;
+       em->disk_bytenr = 0;
+       em->disk_num_bytes = SZ_32K;
+       em->ram_bytes = SZ_32K;
        write_lock(&em_tree->lock);
        ret = btrfs_add_extent_mapping(inode, &em, start, len);
        write_unlock(&em_tree->lock);
@@ -494,9 +533,13 @@ static int add_compressed_extent(struct btrfs_inode *inode,
        }
 
        em->start = start;
+       em->orig_start = start;
        em->len = len;
        em->block_start = block_start;
        em->block_len = SZ_4K;
+       em->disk_bytenr = block_start;
+       em->disk_num_bytes = SZ_4K;
+       em->ram_bytes = len;
        em->flags |= EXTENT_FLAG_COMPRESS_ZLIB;
        write_lock(&em_tree->lock);
        ret = btrfs_add_extent_mapping(inode, &em, em->start, em->len);
@@ -715,9 +758,13 @@ static int test_case_6(struct btrfs_fs_info *fs_info, struct btrfs_inode *inode)
        }
 
        em->start = SZ_4K;
+       em->orig_start = SZ_4K;
        em->len = SZ_4K;
        em->block_start = SZ_16K;
        em->block_len = SZ_16K;
+       em->disk_bytenr = SZ_16K;
+       em->disk_num_bytes = SZ_16K;
+       em->ram_bytes = SZ_16K;
        write_lock(&em_tree->lock);
        ret = btrfs_add_extent_mapping(inode, &em, 0, SZ_8K);
        write_unlock(&em_tree->lock);
@@ -771,7 +818,10 @@ static int test_case_7(struct btrfs_fs_info *fs_info, struct btrfs_inode *inode)
        em->len = SZ_16K;
        em->block_start = 0;
        em->block_len = SZ_4K;
-       em->flags |= EXTENT_FLAG_PINNED;
+       em->disk_bytenr = 0;
+       em->disk_num_bytes = SZ_4K;
+       em->ram_bytes = SZ_16K;
+       em->flags |= (EXTENT_FLAG_PINNED | EXTENT_FLAG_COMPRESS_ZLIB);
        write_lock(&em_tree->lock);
        ret = btrfs_add_extent_mapping(inode, &em, em->start, em->len);
        write_unlock(&em_tree->lock);
@@ -790,9 +840,13 @@ static int test_case_7(struct btrfs_fs_info *fs_info, struct btrfs_inode *inode)
 
        /* [32K, 48K), not pinned */
        em->start = SZ_32K;
+       em->orig_start = SZ_32K;
        em->len = SZ_16K;
        em->block_start = SZ_32K;
        em->block_len = SZ_16K;
+       em->disk_bytenr = SZ_32K;
+       em->disk_num_bytes = SZ_16K;
+       em->ram_bytes = SZ_16K;
        write_lock(&em_tree->lock);
        ret = btrfs_add_extent_mapping(inode, &em, em->start, em->len);
        write_unlock(&em_tree->lock);
index 99da9d34b77aed53255e9af745c42b0431ecd306..0895c6e0681237f587eabffefe411ab571a9c74c 100644 (file)
@@ -117,7 +117,7 @@ static void setup_file_extents(struct btrfs_root *root, u32 sectorsize)
 
        /* Now for a regular extent */
        insert_extent(root, offset, sectorsize - 1, sectorsize - 1, 0,
-                     disk_bytenr, sectorsize, BTRFS_FILE_EXTENT_REG, 0, slot);
+                     disk_bytenr, sectorsize - 1, BTRFS_FILE_EXTENT_REG, 0, slot);
        slot++;
        disk_bytenr += sectorsize;
        offset += sectorsize - 1;