Skip to content

Commit

Permalink
btrfs: defrag: allow defrag_one_cluster() to skip large extent which …
Browse files Browse the repository at this point in the history
…is not a target

commit 966d879 upstream.

In 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

CC: stable@vger.kernel.org # 5.16
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
adam900710 authored and gregkh committed Mar 2, 2022
1 parent e326bd0 commit 833df0a
Showing 1 changed file with 39 additions and 9 deletions.
48 changes: 39 additions & 9 deletions fs/btrfs/ioctl.c
Expand Up @@ -1174,8 +1174,10 @@ struct defrag_target_range {
static int defrag_collect_targets(struct btrfs_inode *inode,
u64 start, u64 len, u32 extent_thresh,
u64 newer_than, bool do_compress,
bool locked, struct list_head *target_list)
bool locked, struct list_head *target_list,
u64 *last_scanned_ret)
{
bool last_is_target = false;
u64 cur = start;
int ret = 0;

Expand All @@ -1185,6 +1187,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;
Expand Down Expand Up @@ -1267,6 +1270,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
Expand Down Expand Up @@ -1310,6 +1314,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 specified range.
*/
if (!last_is_target)
*last_scanned_ret = max(cur, *last_scanned_ret);
else
*last_scanned_ret = max(start + len, *last_scanned_ret);
}
return ret;
}

Expand Down Expand Up @@ -1368,7 +1383,8 @@ static int defrag_one_locked_target(struct btrfs_inode *inode,
}

static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len,
u32 extent_thresh, u64 newer_than, bool do_compress)
u32 extent_thresh, u64 newer_than, bool do_compress,
u64 *last_scanned_ret)
{
struct extent_state *cached_state = NULL;
struct defrag_target_range *entry;
Expand Down Expand Up @@ -1414,7 +1430,7 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len,
*/
ret = defrag_collect_targets(inode, start, len, extent_thresh,
newer_than, do_compress, true,
&target_list);
&target_list, last_scanned_ret);
if (ret < 0)
goto unlock_extent;

Expand Down Expand Up @@ -1449,7 +1465,8 @@ static int defrag_one_cluster(struct btrfs_inode *inode,
u64 start, u32 len, u32 extent_thresh,
u64 newer_than, bool do_compress,
unsigned long *sectors_defragged,
unsigned long max_sectors)
unsigned long max_sectors,
u64 *last_scanned_ret)
{
const u32 sectorsize = inode->root->fs_info->sectorsize;
struct defrag_target_range *entry;
Expand All @@ -1460,7 +1477,7 @@ static int defrag_one_cluster(struct btrfs_inode *inode,
BUILD_BUG_ON(!IS_ALIGNED(CLUSTER_SIZE, PAGE_SIZE));
ret = defrag_collect_targets(inode, start, len, extent_thresh,
newer_than, do_compress, false,
&target_list);
&target_list, NULL);
if (ret < 0)
goto out;

Expand All @@ -1477,6 +1494,15 @@ static int defrag_one_cluster(struct btrfs_inode *inode,
range_len = min_t(u32, range_len,
(max_sectors - *sectors_defragged) * sectorsize);

/*
* If defrag_one_range() has updated last_scanned_ret,
* our range may already be invalid (e.g. hole punched).
* Skip if our range is before last_scanned_ret, as there is
* no need to defrag the range anymore.
*/
if (entry->start + range_len <= *last_scanned_ret)
continue;

if (ra)
page_cache_sync_readahead(inode->vfs_inode.i_mapping,
ra, NULL, entry->start >> PAGE_SHIFT,
Expand All @@ -1489,7 +1515,8 @@ static int defrag_one_cluster(struct btrfs_inode *inode,
* accounting.
*/
ret = defrag_one_range(inode, entry->start, range_len,
extent_thresh, newer_than, do_compress);
extent_thresh, newer_than, do_compress,
last_scanned_ret);
if (ret < 0)
break;
*sectors_defragged += range_len >>
Expand All @@ -1500,6 +1527,8 @@ static int defrag_one_cluster(struct btrfs_inode *inode,
list_del_init(&entry->list);
kfree(entry);
}
if (ret >= 0)
*last_scanned_ret = max(*last_scanned_ret, start + len);
return ret;
}

Expand Down Expand Up @@ -1585,6 +1614,7 @@ int btrfs_defrag_file(struct inode *inode, struct file_ra_state *ra,

while (cur < last_byte) {
const unsigned long prev_sectors_defragged = sectors_defragged;
u64 last_scanned = cur;
u64 cluster_end;

/* The cluster size 256K should always be page aligned */
Expand Down Expand Up @@ -1614,16 +1644,16 @@ int btrfs_defrag_file(struct inode *inode, struct file_ra_state *ra,
BTRFS_I(inode)->defrag_compress = compress_type;
ret = defrag_one_cluster(BTRFS_I(inode), ra, cur,
cluster_end + 1 - cur, extent_thresh,
newer_than, do_compress,
&sectors_defragged, max_to_defrag);
newer_than, do_compress, &sectors_defragged,
max_to_defrag, &last_scanned);

if (sectors_defragged > prev_sectors_defragged)
balance_dirty_pages_ratelimited(inode->i_mapping);

btrfs_inode_unlock(inode, 0);
if (ret < 0)
break;
cur = cluster_end + 1;
cur = max(cluster_end + 1, last_scanned);
if (ret > 0) {
ret = 0;
break;
Expand Down

0 comments on commit 833df0a

Please sign in to comment.