btrfs: defrag: don't waste CPU time on non-target extent
v3 cover btrfs: defrag: don't waste CPU time on non-target extent
In the rework of btrfs_defrag_file() one core idea is to defrag cluster by cluster, thus we can have a better layered code structure, just like what we have now:
btrfs_defrag_file()
|- defrag_one_cluster()
|- defrag_one_range()
|- defrag_one_locked_range()
But there is a catch, btrfs_defrag_file() just moves the cluster to the next cluster, never considering cases like the current extent is already too large, we can skip to its end directly.
This increases CPU usage on very large but not fragmented files.
Fix the behavior in defrag_one_cluster() that, defrag_collect_targets() will reports where next search should start from.
If the current extent is not a target at all, then we can jump to the end of that non-target extent to save time.
To get the missing optimization, also introduce a new structure, btrfs_defrag_ctrl, so we don't need to pass things like @newer_than and @max_to_defrag around.
This also remove weird behaviors like reusing range::start for next search location.
And since we need to convert old btrfs_ioctl_defrag_range_args to newer btrfs_defrag_ctrl, also do extra sanity check in the converting function.
Such cleanup will also bring us closer to expose these extra policy parameters in future enhanced defrag ioctl interface. (Unfortunately, the reserved space of the existing defrag ioctl is not large enough to contain them all)
Changelog:
v2:
- Rebased to lastest misc-next
Just one small conflict with static_assert() update.
And this time only those patches are rebased to misc-next, thus it may cause conflicts with fixes for defrag_check_next_extent() in the future.
- Several grammar fixes
- Report accurate btrfs_defrag_ctrl::sectors_defragged This is inspired by a comment from Filipe that the skip check should be done in the defrag_collect_targets() call inside defrag_one_range().
This results a new patch in v2.
- Change the timing of btrfs_defrag_ctrl::last_scanned update Now it's updated inside defrag_one_range(), which will give us an accurate view, unlike the previous call site in defrag_one_cluster().
- Don't change the timing of extent threshold.
- Rename @last_target to @last_is_target in defrag_collect_targets()
v3:
- Add Reviewed-by tags
- Fix a wrong value in commit message of the 1st patch
- Make @orig_start const for the 3rd patch
- Fix a missing word "skip" in the 5th patch
- Remove one unnecessary assignment in the 5th patch As we don't return the defragged sectors to user space.
Qu Wenruo (5):
btrfs: uapi: introduce BTRFS_DEFRAG_RANGE_MASK for later sanity check
btrfs: defrag: introduce btrfs_defrag_ctrl structure for later usage
btrfs: defrag: use btrfs_defrag_ctrl to replace
btrfs_ioctl_defrag_range_args for btrfs_defrag_file()
btrfs: defrag: make btrfs_defrag_file() to report accurate number of
defragged sectors
btrfs: defrag: allow defrag_one_cluster() to skip large extent which
is not a target
fs/btrfs/ctree.h | 22 +++- fs/btrfs/file.c | 17 ++- fs/btrfs/ioctl.c | 219 ++++++++++++++++++++++--------------- include/uapi/linux/btrfs.h | 6 +- 4 files changed, 163 insertions(+), 101 deletions(-)
Signature · 1 line
2.35.0
PATCH v3 1/5 btrfs: uapi: introduce BTRFS_DEFRAG_RANGE_MASK for later sanity check
And since we're here, replace the hardcoded bit flags (1, 2) with (1UL << 0) and (1UL << 1), respectively.
--- include/uapi/linux/btrfs.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
include/uapi/linux/btrfs.h +4 -2
@@ -576,8 +576,10 @@ struct btrfs_ioctl_clone_range_args {* Used by: * struct btrfs_ioctl_defrag_range_args.flags */ -#define BTRFS_DEFRAG_RANGE_COMPRESS 1 -#define BTRFS_DEFRAG_RANGE_START_IO 2 +#define BTRFS_DEFRAG_RANGE_COMPRESS (1UL << 0) +#define BTRFS_DEFRAG_RANGE_START_IO (1UL << 1) +#define BTRFS_DEFRAG_RANGE_FLAGS_MASK (BTRFS_DEFRAG_RANGE_COMPRESS |\ + BTRFS_DEFRAG_RANGE_START_IO)struct btrfs_ioctl_defrag_range_args {/* start of the defrag operation */ __u64 start;
Signature · 1 line
2.35.0
PATCH v3 2/5 btrfs: defrag: introduce btrfs_defrag_ctrl structure for later usage
In reply to Qu WenruoCurrently btrfs_defrag_file() accepts not only btrfs_ioctl_defrag_range_args but also other parameters like @newer_than and @max_sectors_to_defrag for extra policies.
Those extra values are hidden from defrag ioctl and even caused bugs in the past due to different behaviors based on those extra values.
Here we introduce a new structure, btrfs_defrag_ctrl, to include:
- all members in btrfs_ioctl_defrag_range_args
- @max_sectors_to_defrag and @newer_than
- Extra values which callers of btrfs_defrag_file() may care Like @sectors_defragged and @last_scanned.
With the new structure, also introduce a new helper, btrfs_defrag_ioctl_args_to_ctrl() to:
- Do extra sanity check on @compress and @flags
- Do range alignment when possible
- Set default values.
--- fs/btrfs/ctree.h | 19 +++++++++++++++++++ fs/btrfs/ioctl.c | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+)
fs/btrfs/ctree.h +19 -0
@@ -3318,6 +3318,25 @@ int btrfs_fileattr_set(struct user_namespace *mnt_userns,int btrfs_ioctl_get_supported_features (void __user *arg);void btrfs_sync_inode_flags_to_i_flags (struct inode *inode);int __pure btrfs_is_empty_uuid (u8 *uuid); + +struct btrfs_defrag_ctrl { +/* Input, read-only fields */ +u64 start; +u64 len; +u32 extent_thresh; +u64 newer_than; +u64 max_sectors_to_defrag; +u8 compress; +u8 flags; + +/* Output fields */ +u64 sectors_defragged; +u64 last_scanned;/* Exclusive bytenr */ +}; +int btrfs_defrag_ioctl_args_to_ctrl (struct btrfs_fs_info *fs_info, +struct btrfs_ioctl_defrag_range_args *args, +struct btrfs_defrag_ctrl *ctrl, +u64 max_sectors_to_defrag,u64 newer_than);int btrfs_defrag_file (struct inode *inode,struct file_ra_state *ra,struct btrfs_ioctl_defrag_range_args *range,u64 newer_than,unsigned long max_to_defrag);
fs/btrfs/ioctl.c +41 -0
47 lines not on this page · raw message
Signature · 1 line
2.35.0
PATCH v3 3/5 btrfs: defrag: use btrfs_defrag_ctrl to replace btrfs_ioctl_defrag_range_args for btrfs_defrag_file()
In reply to Qu WenruoThis brings the following benefits:
- No more strange range->start update to indicate last scanned bytenr We have btrfs_defrag_ctrl::last_scanned (exclusive) for it directly.
- No more return value to indicate defragged sectors Now btrfs_defrag_file() will just return 0 if no error happened. And btrfs_defrag_ctrl::sectors_defragged will show that value.
- Less parameters to carry around
Now most defrag_* functions only need to fetch its policy parameters from btrfs_defrag_ctrl directly.
--- fs/btrfs/ctree.h | 3 +- fs/btrfs/file.c | 17 +++--- fs/btrfs/ioctl.c | 144 ++++++++++++++++++++--------------------------- 3 files changed, 71 insertions(+), 93 deletions(-)
fs/btrfs/ctree.h +1 -2
@@ -3338,8 +3338,7 @@ int btrfs_defrag_ioctl_args_to_ctrl(struct btrfs_fs_info *fs_info,struct btrfs_defrag_ctrl *ctrl, u64 max_sectors_to_defrag, u64 newer_than);int btrfs_defrag_file (struct inode *inode,struct file_ra_state *ra, -struct btrfs_ioctl_defrag_range_args *range, -u64 newer_than,unsigned long max_to_defrag); +struct btrfs_defrag_ctrl *ctrl);void btrfs_get_block_group_info (struct list_head *groups_list,struct btrfs_ioctl_space_info *space); void btrfs_update_ioctl_balance_args(struct btrfs_fs_info *fs_info,
fs/btrfs/file.c +8 -9
@@ -277,8 +277,7 @@ static int __btrfs_run_defrag_inode(struct btrfs_fs_info *fs_info, {struct btrfs_root *inode_root;struct inode *inode; -struct btrfs_ioctl_defrag_range_args range; -int num_defrag; +struct btrfs_defrag_ctrl ctrl = {};int ret;/* get the inode */
@@ -297,21 +296,21 @@ static int __btrfs_run_defrag_inode(struct btrfs_fs_info *fs_info,/* do a chunk of defrag */ clear_bit (BTRFS_INODE_IN_DEFRAG , &BTRFS_I (inode)->runtime_flags); -memset (&range,0 ,sizeof (range)); - range.len = (u64)-1 ; - range.start = defrag->last_offset; + ctrl.len = (u64)-1 ; + ctrl.start = defrag->last_offset; + ctrl.newer_than = defrag->transid; + ctrl.max_sectors_to_defrag =BTRFS_DEFRAG_BATCH ;sb_start_write (fs_info->sb); - num_defrag =btrfs_defrag_file (inode,NULL , &range, defrag->transid, -BTRFS_DEFRAG_BATCH ); + ret =btrfs_defrag_file (inode,NULL , &ctrl);sb_end_write (fs_info->sb);/* * if we filled the whole defrag batch, there * must be more work to do. Queue this defrag * again */ -if (num_defrag ==BTRFS_DEFRAG_BATCH ) { - defrag->last_offset = range.start; +if (ctrl.sectors_defragged ==BTRFS_DEFRAG_BATCH ) { + defrag->last_offset = ctrl.last_scanned;btrfs_requeue_inode_defrag (BTRFS_I (inode), defrag); }else if (defrag->last_offset && !defrag->cycled) {/*
fs/btrfs/ioctl.c +62 -82
301 lines not on this page · raw message
Signature · 1 line
2.35.0
PATCH v3 4/5 btrfs: defrag: make btrfs_defrag_file() to report accurate number of defragged sectors
In reply to Qu WenruoPreviously rework btrfs_defrag_file() can only report the number of sectors from the first run of defrag_collect_targets().
This number is not accurate as if holes are punched after the first defrag_collect_targets() call, we will not choose to defrag the holes.
Originally this is to avoid passing @sectors_defragged to every involved functions.
But now since we have btrfs_defrag_ctrl, there is no need to do such inaccurate accounting, just update btrfs_defrag_ctrl::sectors_defragged after a successful defrag_one_locked_target() call.
--- fs/btrfs/ioctl.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-)
fs/btrfs/ioctl.c +3 -9
@@ -1371,7 +1371,7 @@ static int defrag_one_locked_target(struct btrfs_inode *inode, }static int defrag_one_range (struct btrfs_inode *inode, -const struct btrfs_defrag_ctrl *ctrl, +struct btrfs_defrag_ctrl *ctrl,u64 start,u32 len) {struct extent_state *cached_state =NULL ;
@@ -1426,6 +1426,8 @@ static int defrag_one_range(struct btrfs_inode *inode, &cached_state);if (ret <0 )break ; + ctrl->sectors_defragged += entry->len >> + inode->root->fs_info->sectorsize_bits; }list_for_each_entry_safe (entry, tmp, &target_list, list) {
@@ -1489,17 +1491,9 @@ static int defrag_one_cluster(struct btrfs_inode *inode, ra,NULL , entry->start >>PAGE_SHIFT , ((entry->start + range_len -1 ) >>PAGE_SHIFT ) - (entry->start >> PAGE_SHIFT) + 1); -/* -* Here we may not defrag any range if holes are punched before -* we locked the pages. -* But that's fine, it only affects the @sectors_defragged -* accounting. -*/ ret =defrag_one_range (inode, ctrl, entry->start, range_len);if (ret <0 )break ; - ctrl->sectors_defragged += range_len >> - inode->root->fs_info->sectorsize_bits; } out:list_for_each_entry_safe (entry, tmp, &target_list, list) {
Signature · 1 line
2.35.0
PATCH v3 5/5 btrfs: defrag: allow defrag_one_cluster() to skip large extent which is not a target
In reply to Qu WenruoIn the rework of btrfs_defrag_file(), we always call defrag_one_cluster() and increase the offset by cluster size, which is only 256K.
But there are cases where we have a large extent (e.g. 128M) which doesn't need to be defragged at all.
Before the refactor, we can directly skip the range, but now we have to scan that extent map again and again until the cluster moves after the non-target extent.
Fix the problem by allow defrag_one_cluster() to increase btrfs_defrag_ctrl::last_scanned to the end of an extent, if and only if the last extent of the cluster is not a target.
The test script looks like this:
mkfs.btrfs -f $dev > /dev/null mount $dev $mnt # As btrfs ioctl uses 32M as extent_threshold xfs_io -f -c "pwrite 0 64M" $mnt/file1 sync # Some fragemented range to defrag xfs_io -s -c "pwrite 65548k 4k" \ -c "pwrite 65544k 4k" \ -c "pwrite 65540k 4k" \ -c "pwrite 65536k 4k" \ $mnt/file1 sync echo "=== before ===" xfs_io -c "fiemap -v" $mnt/file1 echo "=== after ===" btrfs fi defrag $mnt/file1 sync xfs_io -c "fiemap -v" $mnt/file1 umount $mnt
With extra ftrace put into defrag_one_cluster(), before the patch it would result tons of loops:
(As defrag_one_cluster() is inlined, the function name is its caller)
btrfs-126062 [005] ..... 4682.816026: btrfs_defrag_file: r/i=5/257 start=0 len=262144 btrfs-126062 [005] ..... 4682.816027: btrfs_defrag_file: r/i=5/257 start=262144 len=262144 btrfs-126062 [005] ..... 4682.816028: btrfs_defrag_file: r/i=5/257 start=524288 len=262144 btrfs-126062 [005] ..... 4682.816028: btrfs_defrag_file: r/i=5/257 start=786432 len=262144 btrfs-126062 [005] ..... 4682.816028: btrfs_defrag_file: r/i=5/257 start=1048576 len=262144
...
btrfs-126062 [005] ..... 4682.816043: btrfs_defrag_file: r/i=5/257 start=67108864 len=262144
But with this patch there will be just one loop, then directly to the end of the extent:
btrfs-130471 [014] ..... 5434.029558: defrag_one_cluster: r/i=5/257 start=0 len=262144 btrfs-130471 [014] ..... 5434.029559: defrag_one_cluster: r/i=5/257 start=67108864 len=16384
--- fs/btrfs/ioctl.c | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-)
fs/btrfs/ioctl.c +34 -6
@@ -1185,10 +1185,11 @@ struct defrag_target_range {*/ static int defrag_collect_targets (struct btrfs_inode *inode,const struct btrfs_defrag_ctrl *ctrl, -u64 start,u32 len,bool locked, -struct list_head *target_list) +u64 start,u32 len,u64 *last_scanned_ret, +bool locked,struct list_head *target_list) {bool do_compress = ctrl->flags &BTRFS_DEFRAG_RANGE_COMPRESS ; +bool last_is_target = false;u64 cur = start;int ret =0 ;
@@ -1198,6 +1199,7 @@ static int defrag_collect_targets(struct btrfs_inode *inode,bool next_mergeable = true;u64 range_len; + last_is_target = false; em =defrag_lookup_extent (&inode->vfs_inode, cur, locked);if (!em)break ;
@@ -1269,6 +1271,7 @@ static int defrag_collect_targets(struct btrfs_inode *inode, } add: + last_is_target = true; range_len =min (extent_map_end (em), start + len) - cur;/* * This one is a good target, check if it can be merged into
@@ -1312,6 +1315,17 @@ static int defrag_collect_targets(struct btrfs_inode *inode,kfree (entry); } } +if (!ret && last_scanned_ret) { +/* +* If the last extent is not a target, the caller can skip to +* the end of that extent. +* Otherwise, we can only go the end of the spcified range. +*/ +if (!last_is_target) + *last_scanned_ret = cur; +else + *last_scanned_ret = start + len; + }return ret; }
@@ -1382,6 +1396,7 @@ static int defrag_one_range(struct btrfs_inode *inode,const u32 sectorsize = inode->root->fs_info->sectorsize;u64 last_index = (start + len -1 ) >>PAGE_SHIFT ;u64 start_index = start >>PAGE_SHIFT ; +u64 last_scanned;unsigned int nr_pages = last_index - start_index +1 ;int ret =0 ;int i;
@@ -1416,8 +1431,8 @@ static int defrag_one_range(struct btrfs_inode *inode,* And this time we have extent locked already, pass @locked = true * so that we won't relock the extent range and cause deadlock. */ - ret =defrag_collect_targets (inode, ctrl, start, len, true, - &target_list); + ret =defrag_collect_targets (inode, ctrl, start, len, &last_scanned, + true, &target_list);if (ret <0 ) goto unlock_extent;
@@ -1434,6 +1449,8 @@ static int defrag_one_range(struct btrfs_inode *inode,list_del_init (&entry->list);kfree (entry); } +if (!ret) + ctrl->last_scanned = last_scanned; unlock_extent: unlock_extent_cached(&inode->io_tree, start_index << PAGE_SHIFT, (last_index << PAGE_SHIFT) + PAGE_SIZE - 1,
@@ -1461,13 +1478,14 @@ static int defrag_one_cluster(struct btrfs_inode *inode, struct btrfs_defrag_ctrl *ctrl, u32 len) {const u32 sectorsize = inode->root->fs_info->sectorsize; +const u64 orig_start = ctrl->last_scanned;struct defrag_target_range *entry;struct defrag_target_range *tmp;LIST_HEAD (target_list);int ret; ret =defrag_collect_targets (inode, ctrl, ctrl->last_scanned, len, - false, &target_list); +NULL , false, &target_list);if (ret <0 ) goto out;
@@ -1486,6 +1504,15 @@ static int defrag_one_cluster(struct btrfs_inode *inode, (ctrl->max_sectors_to_defrag - ctrl->sectors_defragged) * sectorsize); +/* +* If defrag_one_range() has updated ctrl::last_scanned, +* our range may already be invalid (e.g. hole punched). +* Skip if our range is before ctrl::last_scanned, as there is +* no need to defrag the range anymore. +*/ +if (entry->start + range_len <= ctrl->last_scanned) +continue ; +if (ra) page_cache_sync_readahead(inode->vfs_inode.i_mapping, ra, NULL, entry->start >> PAGE_SHIFT,
@@ -1500,6 +1527,8 @@ static int defrag_one_cluster(struct btrfs_inode *inode,list_del_init (&entry->list);kfree (entry); } +if (ret >=0 ) + ctrl->last_scanned =max (ctrl->last_scanned, orig_start + len);return ret; }
@@ -1645,7 +1674,6 @@ int btrfs_defrag_file(struct inode *inode, struct file_ra_state *ra,btrfs_inode_unlock (inode,0 );if (ret <0 )break ; - ctrl->last_scanned = cluster_end +1 ;if (ret >0 ) { ret =0 ;break ;
Signature · 1 line
2.35.0
Quoted 18 lines · Qu Wenruo
In the rework of
btrfs_defrag_file()one core idea is to defrag cluster by cluster, thus we can have a better layered code structure, just like what we have now:
btrfs_defrag_file()
|-defrag_one_cluster()
|-defrag_one_range()
|-defrag_one_locked_range()But there is a catch,
btrfs_defrag_file()just moves the cluster to the next cluster, never considering cases like the current extent is already too large, we can skip to its end directly.This increases CPU usage on very large but not fragmented files.
Fix the behavior in
defrag_one_cluster()that,defrag_collect_targets()will reports where next search should start from.
Quoted 6 lines
If the current extent is not a target at all, then we can jump to the end of that non-target extent to save time.
To get the missing optimization, also introduce a new structure,
btrfs_defrag_ctrl, so we don't need to pass things like @newer_than and @max_to_defrag around.
Is this patchset supposed to be in 5.16 as fix for some defrag problem? If yes, the patch switching to the control structure should be avoided and done as a post cleanup as some other patches depend on it.
Quoted 22 lines · David Sterba
Quoted 21 lines · Qu Wenruo
In the rework of
btrfs_defrag_file()one core idea is to defrag cluster by cluster, thus we can have a better layered code structure, just like what we have now:
btrfs_defrag_file()
|-defrag_one_cluster()|- defrag_one_range() |- defrag_one_locked_range()But there is a catch,
btrfs_defrag_file()just moves the cluster to the next cluster, never considering cases like the current extent is already too large, we can skip to its end directly.This increases CPU usage on very large but not fragmented files.
Fix the behavior in
defrag_one_cluster()that,defrag_collect_targets()will reports where next search should start from.If the current extent is not a target at all, then we can jump to the end of that non-target extent to save time.
Quoted 7 lines
Quoted 3 lines
To get the missing optimization, also introduce a new structure,
btrfs_defrag_ctrl, so we don't need to pass things like @newer_than and @max_to_defrag around.Is this patchset supposed to be in 5.16 as fix for some defrag problem? If yes, the patch switching to the control structure should be avoided and done as a post cleanup as some other patches depend on it.
I can backport it manually to v5.16 without the ctrl refactor.
The problem of the current situation is not really ideal, thus I put the refactor first.
Thanks,
Qu
Quoted 29 lines · Qu Wenruo
Quoted 26 lines · David Sterba
Quoted 24 lines · Qu Wenruo
In the rework of
btrfs_defrag_file()one core idea is to defrag cluster by cluster, thus we can have a better layered code structure, just like what we have now:
btrfs_defrag_file()
|-defrag_one_cluster()|- defrag_one_range() |- defrag_one_locked_range()But there is a catch,
btrfs_defrag_file()just moves the cluster to the next cluster, never considering cases like the current extent is already too large, we can skip to its end directly.This increases CPU usage on very large but not fragmented files.
Fix the behavior in
defrag_one_cluster()that,defrag_collect_targets()will reports where next search should start from.If the current extent is not a target at all, then we can jump to the end of that non-target extent to save time.
To get the missing optimization, also introduce a new structure,
btrfs_defrag_ctrl, so we don't need to pass things like @newer_than and @max_to_defrag around.
Quoted 5 lines
Quoted 3 lines
Is this patchset supposed to be in 5.16 as fix for some defrag problem? If yes, the patch switching to the control structure should be avoided and done as a post cleanup as some other patches depend on it.
I can backport it manually to v5.16 without the ctrl refactor.
That's not great if we have to do two versions, and more fixes to defrag are still expected so a cleanup patch makes any backporting harder. That we can send a manually ported version to stable is there for cases when the changes are not possible. In this case the cleanup is not necessary, but I understand it makes the code cleaner. Given that we need to fix a released kernel it's the preference.
Quoted 34 lines · David Sterba
Quoted 33 lines · Qu Wenruo
Quoted 29 lines · David Sterba
Quoted 18 lines · Qu Wenruo
In the rework of
btrfs_defrag_file()one core idea is to defrag cluster by cluster, thus we can have a better layered code structure, just like what we have now:
btrfs_defrag_file()
|-defrag_one_cluster()|- defrag_one_range() |- defrag_one_locked_range()But there is a catch,
btrfs_defrag_file()just moves the cluster to the next cluster, never considering cases like the current extent is already too large, we can skip to its end directly.This increases CPU usage on very large but not fragmented files.
Fix the behavior in
defrag_one_cluster()that,defrag_collect_targets()will reports where next search should start from.Quoted 6 lines
If the current extent is not a target at all, then we can jump to the end of that non-target extent to save time.
To get the missing optimization, also introduce a new structure,
btrfs_defrag_ctrl, so we don't need to pass things like @newer_than and @max_to_defrag around.Is this patchset supposed to be in 5.16 as fix for some defrag problem? If yes, the patch switching to the control structure should be avoided and done as a post cleanup as some other patches depend on it.
Quoted 9 lines
Quoted 1 line
I can backport it manually to v5.16 without the ctrl refactor.
That's not great if we have to do two versions, and more fixes to defrag are still expected so a cleanup patch makes any backporting harder. That we can send a manually ported version to stable is there for cases when the changes are not possible. In this case the cleanup is not necessary, but I understand it makes the code cleaner. Given that we need to fix a released kernel it's the preference.
Unfortunately, it looks like without this cleanup, all later patches either needs extra parameters passing down the call chain, or have very "creative" way to pass info around, and can causing more problem in the long run.
I'm wondering if it's some policy in the stable tree preventing such cleanup patches being merged or something else?
Thanks,
Qu
Quoted 21 lines · Qu Wenruo
Quoted 11 lines · David Sterba
Quoted 10 lines · Qu Wenruo
Quoted 8 lines · David Sterba
Quoted 3 lines · Qu Wenruo
To get the missing optimization, also introduce a new structure,
btrfs_defrag_ctrl, so we don't need to pass things like @newer_than and @max_to_defrag around.Is this patchset supposed to be in 5.16 as fix for some defrag problem? If yes, the patch switching to the control structure should be avoided and done as a post cleanup as some other patches depend on it.
Quoted 9 lines
Quoted 1 line
I can backport it manually to v5.16 without the ctrl refactor.
That's not great if we have to do two versions, and more fixes to defrag are still expected so a cleanup patch makes any backporting harder. That we can send a manually ported version to stable is there for cases when the changes are not possible. In this case the cleanup is not necessary, but I understand it makes the code cleaner. Given that we need to fix a released kernel it's the preference.
Quoted 7 lines
Unfortunately, it looks like without this cleanup, all later patches either needs extra parameters passing down the call chain, or have very "creative" way to pass info around, and can causing more problem in the long run.
I'm wondering if it's some policy in the stable tree preventing such cleanup patches being merged or something else?
It's not a hard policy, I don't remember a patch rejected from stable because it needed a prep work. The fix itself must be justified, and minimal if possible and that's what I'm always trying to go for.
This patches has diffstat
4 files changed, 163 insertions(+), 101 deletions(-)
and the preparatory patch itself
3 files changed, 71 insertions(+), 93 deletions(-)
Patch 4 probably needs to add one parameter, and 5 is adding a new one even though there's the ctrl structure. So I think it would not be that intrusive with a few extra parameters compared to the whole ctrl conversion.
Quoted 16 lines · David Sterba
Quoted 7 lines
Unfortunately, it looks like without this cleanup, all later patches either needs extra parameters passing down the call chain, or have very "creative" way to pass info around, and can causing more problem in the long run.
I'm wondering if it's some policy in the stable tree preventing such cleanup patches being merged or something else?
It's not a hard policy, I don't remember a patch rejected from stable because it needed a prep work. The fix itself must be justified, and minimal if possible and that's what I'm always trying to go for.
This patches has diffstat
4 files changed, 163 insertions(+), 101 deletions(-)
Quoted 8 lines
and the preparatory patch itself
3 files changed, 71 insertions(+), 93 deletions(-)Patch 4 probably needs to add one parameter, and 5 is adding a new one even though there's the ctrl structure. So I think it would not be that intrusive with a few extra parameters compared to the whole ctrl conversion.
I've tried to minimize that, basically it's just patch 5, passing around the last_scanned, but I'm not 100% sure, the ctrl structure makes it incomprehensible what else needs to be passed around or initialized. If we're fixing a not so great rewrite I'm very inclined to fix it by a series of minimal fixes, not another rewrite.
Quoted 26 lines · David Sterba
Quoted 16 lines · David Sterba
Quoted 7 lines
Unfortunately, it looks like without this cleanup, all later patches either needs extra parameters passing down the call chain, or have very "creative" way to pass info around, and can causing more problem in the long run.
I'm wondering if it's some policy in the stable tree preventing such cleanup patches being merged or something else?
It's not a hard policy, I don't remember a patch rejected from stable because it needed a prep work. The fix itself must be justified, and minimal if possible and that's what I'm always trying to go for.
This patches has diffstat
4 files changed, 163 insertions(+), 101 deletions(-)Quoted 8 lines
and the preparatory patch itself
3 files changed, 71 insertions(+), 93 deletions(-)Patch 4 probably needs to add one parameter, and 5 is adding a new one even though there's the ctrl structure. So I think it would not be that intrusive with a few extra parameters compared to the whole ctrl conversion.
Quoted 5 lines
I've tried to minimize that, basically it's just patch 5, passing around the
last_scanned, but I'm not 100% sure, the ctrl structure makes it incomprehensible what else needs to be passed around or initialized. If we're fixing a not so great rewrite I'm very inclined to fix it by a series of minimal fixes, not another rewrite.
OK, I'll re-order the patchset to put the fix in front of the structure rework.
Thanks,
Qu