diff mbox series

[v2] btrfs: allow defrag to convert inline extents to regular extents

Message ID 0606709d439b711a767ce1491f51f0113326d265.1652097509.git.wqu@suse.com (mailing list archive)
State New, archived
Headers show
Series [v2] btrfs: allow defrag to convert inline extents to regular extents | expand

Commit Message

Qu Wenruo May 9, 2022, noon UTC
Btrfs defaults to max_inline=2K to make small writes inlined into
metadata.

The default value is always a win, as even DUP/RAID1/RAID10 doubles the
metadata usage, it should still cause less physical space used compared
to a 4K regular extents.

But since the introduce of RAID1C3 and RAID1C4 it's no longer the case,
users may find inlined extents causing too much space wasted, and want
to convert those inlined extents back to regular extents.

Unfortunately defrag will unconditionally skip all inline extents, no
matter if the user is trying to converting them back to regular extents.

So this patch will add a small exception for defrag_collect_targets() to
allow defragging inline extents, if and only if the inlined extents are
larger than max_inline, allowing users to convert them to regular ones.

This also allows us to defrag extents like the following:

	item 6 key (257 EXTENT_DATA 0) itemoff 15794 itemsize 69
		generation 7 type 0 (inline)
		inline extent data size 48 ram_bytes 4096 compression 1 (zlib)
	item 7 key (257 EXTENT_DATA 4096) itemoff 15741 itemsize 53
		generation 7 type 1 (regular)
		extent data disk byte 13631488 nr 4096
		extent data offset 0 nr 16384 ram 16384
		extent compression 1 (zlib)

Previously we're unable to do any defrag, since the first extent is
inlined, and the second one has no extent to merge.

Now we can defrag it to just one single extent, saving 48 bytes metadata
space.

	item 6 key (257 EXTENT_DATA 0) itemoff 15810 itemsize 53
		generation 8 type 1 (regular)
		extent data disk byte 13635584 nr 4096
		extent data offset 0 nr 20480 ram 20480
		extent compression 1 (zlib)

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
Changelog:
v2:
- Re-phase why we add the inline extent without checking @next_mergeable

- Add some commit message on the new ability to handle mixed inline and
  regular extents
---
 fs/btrfs/ioctl.c | 24 ++++++++++++++++++++++--
 1 file changed, 22 insertions(+), 2 deletions(-)

Comments

Filipe Manana May 9, 2022, 12:44 p.m. UTC | #1
On Mon, May 9, 2022 at 1:28 PM Qu Wenruo <wqu@suse.com> wrote:
>
> Btrfs defaults to max_inline=2K to make small writes inlined into
> metadata.
>
> The default value is always a win, as even DUP/RAID1/RAID10 doubles the
> metadata usage, it should still cause less physical space used compared
> to a 4K regular extents.
>
> But since the introduce of RAID1C3 and RAID1C4 it's no longer the case,
> users may find inlined extents causing too much space wasted, and want
> to convert those inlined extents back to regular extents.
>
> Unfortunately defrag will unconditionally skip all inline extents, no
> matter if the user is trying to converting them back to regular extents.
>
> So this patch will add a small exception for defrag_collect_targets() to
> allow defragging inline extents, if and only if the inlined extents are
> larger than max_inline, allowing users to convert them to regular ones.
>
> This also allows us to defrag extents like the following:
>
>         item 6 key (257 EXTENT_DATA 0) itemoff 15794 itemsize 69
>                 generation 7 type 0 (inline)
>                 inline extent data size 48 ram_bytes 4096 compression 1 (zlib)
>         item 7 key (257 EXTENT_DATA 4096) itemoff 15741 itemsize 53
>                 generation 7 type 1 (regular)
>                 extent data disk byte 13631488 nr 4096
>                 extent data offset 0 nr 16384 ram 16384
>                 extent compression 1 (zlib)
>
> Previously we're unable to do any defrag, since the first extent is
> inlined, and the second one has no extent to merge.
>
> Now we can defrag it to just one single extent, saving 48 bytes metadata
> space.
>
>         item 6 key (257 EXTENT_DATA 0) itemoff 15810 itemsize 53
>                 generation 8 type 1 (regular)
>                 extent data disk byte 13635584 nr 4096
>                 extent data offset 0 nr 20480 ram 20480
>                 extent compression 1 (zlib)
>
> Signed-off-by: Qu Wenruo <wqu@suse.com>
> ---
> Changelog:
> v2:
> - Re-phase why we add the inline extent without checking @next_mergeable
>
> - Add some commit message on the new ability to handle mixed inline and
>   regular extents
> ---
>  fs/btrfs/ioctl.c | 24 ++++++++++++++++++++++--
>  1 file changed, 22 insertions(+), 2 deletions(-)
>
> diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
> index 9d8e46815ee4..d5a8f5b7d3a9 100644
> --- a/fs/btrfs/ioctl.c
> +++ b/fs/btrfs/ioctl.c
> @@ -1420,8 +1420,19 @@ static int defrag_collect_targets(struct btrfs_inode *inode,
>                 if (!em)
>                         break;
>
> -               /* Skip hole/inline/preallocated extents */
> -               if (em->block_start >= EXTENT_MAP_LAST_BYTE ||
> +               /*
> +                * If the file extent is an inlined one, we may still want to
> +                * defrag it (fallthrough) if it will cause a regular extent.
> +                * This is for users who want to convert inline extents to
> +                * regular ones through max_inline= mount option.
> +                */
> +               if (em->block_start == EXTENT_MAP_INLINE &&
> +                   em->len <= inode->root->fs_info->max_inline)
> +                       goto next;
> +
> +               /* Skip hole/delalloc/preallocated extents */
> +               if (em->block_start == EXTENT_MAP_HOLE ||
> +                   em->block_start == EXTENT_MAP_DELALLOC ||
>                     test_bit(EXTENT_FLAG_PREALLOC, &em->flags))
>                         goto next;
>
> @@ -1480,6 +1491,15 @@ static int defrag_collect_targets(struct btrfs_inode *inode,
>                 if (em->len >= get_extent_max_capacity(em))
>                         goto next;
>
> +               /*
> +                * Normally there is no more extent after an inline one, thus

s/there is no more extent/there are no more extents/

It can be fixed when cherry picked.

Looks good, thanks.

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

> +                * @next_mergeable will normally be false and not defragged.
> +                * So if an inline extent passed all above checks, just add it
> +                * for defrag, and be converted to regular extents.
> +                */
> +               if (em->block_start == EXTENT_MAP_INLINE)
> +                       goto add;
> +
>                 next_mergeable = defrag_check_next_extent(&inode->vfs_inode, em,
>                                                 extent_thresh, newer_than, locked);
>                 if (!next_mergeable) {
> --
> 2.36.0
>
David Sterba May 10, 2022, 11:55 a.m. UTC | #2
On Mon, May 09, 2022 at 08:00:53PM +0800, Qu Wenruo wrote:
> Btrfs defaults to max_inline=2K to make small writes inlined into
> metadata.
> 
> The default value is always a win, as even DUP/RAID1/RAID10 doubles the
> metadata usage, it should still cause less physical space used compared
> to a 4K regular extents.
> 
> But since the introduce of RAID1C3 and RAID1C4 it's no longer the case,
> users may find inlined extents causing too much space wasted, and want
> to convert those inlined extents back to regular extents.
> 
> Unfortunately defrag will unconditionally skip all inline extents, no
> matter if the user is trying to converting them back to regular extents.
> 
> So this patch will add a small exception for defrag_collect_targets() to
> allow defragging inline extents, if and only if the inlined extents are
> larger than max_inline, allowing users to convert them to regular ones.
> 
> This also allows us to defrag extents like the following:
> 
> 	item 6 key (257 EXTENT_DATA 0) itemoff 15794 itemsize 69
> 		generation 7 type 0 (inline)
> 		inline extent data size 48 ram_bytes 4096 compression 1 (zlib)
> 	item 7 key (257 EXTENT_DATA 4096) itemoff 15741 itemsize 53
> 		generation 7 type 1 (regular)
> 		extent data disk byte 13631488 nr 4096
> 		extent data offset 0 nr 16384 ram 16384
> 		extent compression 1 (zlib)
> 
> Previously we're unable to do any defrag, since the first extent is
> inlined, and the second one has no extent to merge.
> 
> Now we can defrag it to just one single extent, saving 48 bytes metadata
> space.
> 
> 	item 6 key (257 EXTENT_DATA 0) itemoff 15810 itemsize 53
> 		generation 8 type 1 (regular)
> 		extent data disk byte 13635584 nr 4096
> 		extent data offset 0 nr 20480 ram 20480
> 		extent compression 1 (zlib)
> 
> Signed-off-by: Qu Wenruo <wqu@suse.com>
> ---
> Changelog:
> v2:
> - Re-phase why we add the inline extent without checking @next_mergeable
> 
> - Add some commit message on the new ability to handle mixed inline and
>   regular extents

Added to misc-next, with the updated comment, thanks.
diff mbox series

Patch

diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 9d8e46815ee4..d5a8f5b7d3a9 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -1420,8 +1420,19 @@  static int defrag_collect_targets(struct btrfs_inode *inode,
 		if (!em)
 			break;
 
-		/* Skip hole/inline/preallocated extents */
-		if (em->block_start >= EXTENT_MAP_LAST_BYTE ||
+		/*
+		 * If the file extent is an inlined one, we may still want to
+		 * defrag it (fallthrough) if it will cause a regular extent.
+		 * This is for users who want to convert inline extents to
+		 * regular ones through max_inline= mount option.
+		 */
+		if (em->block_start == EXTENT_MAP_INLINE &&
+		    em->len <= inode->root->fs_info->max_inline)
+			goto next;
+
+		/* Skip hole/delalloc/preallocated extents */
+		if (em->block_start == EXTENT_MAP_HOLE ||
+		    em->block_start == EXTENT_MAP_DELALLOC ||
 		    test_bit(EXTENT_FLAG_PREALLOC, &em->flags))
 			goto next;
 
@@ -1480,6 +1491,15 @@  static int defrag_collect_targets(struct btrfs_inode *inode,
 		if (em->len >= get_extent_max_capacity(em))
 			goto next;
 
+		/*
+		 * Normally there is no more extent after an inline one, thus
+		 * @next_mergeable will normally be false and not defragged.
+		 * So if an inline extent passed all above checks, just add it
+		 * for defrag, and be converted to regular extents.
+		 */
+		if (em->block_start == EXTENT_MAP_INLINE)
+			goto add;
+
 		next_mergeable = defrag_check_next_extent(&inode->vfs_inode, em,
 						extent_thresh, newer_than, locked);
 		if (!next_mergeable) {