diff mbox series

[v3,04/11] btrfs: introduce extra sanity checks for extent maps

Message ID a9fa2b3ca93da520f65410ce9fc5ef7d626227d5.1716440169.git.wqu@suse.com (mailing list archive)
State New, archived
Headers show
Series btrfs: extent-map: unify the members with btrfs_ordered_extent | expand

Commit Message

Qu Wenruo May 23, 2024, 5:03 a.m. UTC
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 would 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 timing:

- 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.

Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
---
 fs/btrfs/extent_map.c             | 60 +++++++++++++++++++++++++++++++
 fs/btrfs/relocation.c             |  4 +++
 fs/btrfs/tests/extent-map-tests.c | 56 ++++++++++++++++++++++++++++-
 fs/btrfs/tests/inode-tests.c      |  2 +-
 4 files changed, 120 insertions(+), 2 deletions(-)

Comments

Filipe Manana May 23, 2024, 4:57 p.m. UTC | #1
On Thu, May 23, 2024 at 6:04 AM Qu Wenruo <wqu@suse.com> wrote:
>
> 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 would 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 timing:
>
> - 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.
>
> Signed-off-by: Qu Wenruo <wqu@suse.com>
> Signed-off-by: David Sterba <dsterba@suse.com>
> ---
>  fs/btrfs/extent_map.c             | 60 +++++++++++++++++++++++++++++++
>  fs/btrfs/relocation.c             |  4 +++
>  fs/btrfs/tests/extent-map-tests.c | 56 ++++++++++++++++++++++++++++-
>  fs/btrfs/tests/inode-tests.c      |  2 +-
>  4 files changed, 120 insertions(+), 2 deletions(-)
>
> diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
> index c7d2393692e6..b157f30ac241 100644
> --- a/fs/btrfs/extent_map.c
> +++ b/fs/btrfs/extent_map.c
> @@ -283,8 +283,62 @@ 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\n",
> +               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);
> +       }

I think I mentioned this before, but since these are all unexpected to
happen and we're in a critical section that can have a lot of
concurrency, adding unlikely() here would be good to have.
You can do that afterwards in a separate patch. I know some of these
checks get removed in later patches.

Reviewed-by: Filipe Manana <fdmanana@suse.com>

> +}
> +
>  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;
> @@ -319,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);
> @@ -334,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);
> @@ -445,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;
> @@ -548,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))
> diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
> index 5f1a909a1d91..151ed1ebd291 100644
> --- a/fs/btrfs/relocation.c
> +++ b/fs/btrfs/relocation.c
> @@ -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);
> diff --git a/fs/btrfs/tests/extent-map-tests.c b/fs/btrfs/tests/extent-map-tests.c
> index c511a1297956..e73ac7a0869c 100644
> --- a/fs/btrfs/tests/extent-map-tests.c
> +++ b/fs/btrfs/tests/extent-map-tests.c
> @@ -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);
> diff --git a/fs/btrfs/tests/inode-tests.c b/fs/btrfs/tests/inode-tests.c
> index 99da9d34b77a..0895c6e06812 100644
> --- a/fs/btrfs/tests/inode-tests.c
> +++ b/fs/btrfs/tests/inode-tests.c
> @@ -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;
> --
> 2.45.1
>
>
diff mbox series

Patch

diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
index c7d2393692e6..b157f30ac241 100644
--- a/fs/btrfs/extent_map.c
+++ b/fs/btrfs/extent_map.c
@@ -283,8 +283,62 @@  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\n",
+		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;
@@ -319,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);
@@ -334,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);
@@ -445,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;
@@ -548,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))
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index 5f1a909a1d91..151ed1ebd291 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -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);
diff --git a/fs/btrfs/tests/extent-map-tests.c b/fs/btrfs/tests/extent-map-tests.c
index c511a1297956..e73ac7a0869c 100644
--- a/fs/btrfs/tests/extent-map-tests.c
+++ b/fs/btrfs/tests/extent-map-tests.c
@@ -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);
diff --git a/fs/btrfs/tests/inode-tests.c b/fs/btrfs/tests/inode-tests.c
index 99da9d34b77a..0895c6e06812 100644
--- a/fs/btrfs/tests/inode-tests.c
+++ b/fs/btrfs/tests/inode-tests.c
@@ -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;