Skip to content

Commit 966d879

Browse files
adam900710kdave
authored andcommitted
btrfs: defrag: allow defrag_one_cluster() to skip large extent which is not a target
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>
1 parent 741b23a commit 966d879

1 file changed

Lines changed: 39 additions & 9 deletions

File tree

fs/btrfs/ioctl.c

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,8 +1186,10 @@ struct defrag_target_range {
11861186
static int defrag_collect_targets(struct btrfs_inode *inode,
11871187
u64 start, u64 len, u32 extent_thresh,
11881188
u64 newer_than, bool do_compress,
1189-
bool locked, struct list_head *target_list)
1189+
bool locked, struct list_head *target_list,
1190+
u64 *last_scanned_ret)
11901191
{
1192+
bool last_is_target = false;
11911193
u64 cur = start;
11921194
int ret = 0;
11931195

@@ -1197,6 +1199,7 @@ static int defrag_collect_targets(struct btrfs_inode *inode,
11971199
bool next_mergeable = true;
11981200
u64 range_len;
11991201

1202+
last_is_target = false;
12001203
em = defrag_lookup_extent(&inode->vfs_inode, cur, locked);
12011204
if (!em)
12021205
break;
@@ -1272,6 +1275,7 @@ static int defrag_collect_targets(struct btrfs_inode *inode,
12721275
}
12731276

12741277
add:
1278+
last_is_target = true;
12751279
range_len = min(extent_map_end(em), start + len) - cur;
12761280
/*
12771281
* This one is a good target, check if it can be merged into
@@ -1315,6 +1319,17 @@ static int defrag_collect_targets(struct btrfs_inode *inode,
13151319
kfree(entry);
13161320
}
13171321
}
1322+
if (!ret && last_scanned_ret) {
1323+
/*
1324+
* If the last extent is not a target, the caller can skip to
1325+
* the end of that extent.
1326+
* Otherwise, we can only go the end of the specified range.
1327+
*/
1328+
if (!last_is_target)
1329+
*last_scanned_ret = max(cur, *last_scanned_ret);
1330+
else
1331+
*last_scanned_ret = max(start + len, *last_scanned_ret);
1332+
}
13181333
return ret;
13191334
}
13201335

@@ -1373,7 +1388,8 @@ static int defrag_one_locked_target(struct btrfs_inode *inode,
13731388
}
13741389

13751390
static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len,
1376-
u32 extent_thresh, u64 newer_than, bool do_compress)
1391+
u32 extent_thresh, u64 newer_than, bool do_compress,
1392+
u64 *last_scanned_ret)
13771393
{
13781394
struct extent_state *cached_state = NULL;
13791395
struct defrag_target_range *entry;
@@ -1419,7 +1435,7 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len,
14191435
*/
14201436
ret = defrag_collect_targets(inode, start, len, extent_thresh,
14211437
newer_than, do_compress, true,
1422-
&target_list);
1438+
&target_list, last_scanned_ret);
14231439
if (ret < 0)
14241440
goto unlock_extent;
14251441

@@ -1454,7 +1470,8 @@ static int defrag_one_cluster(struct btrfs_inode *inode,
14541470
u64 start, u32 len, u32 extent_thresh,
14551471
u64 newer_than, bool do_compress,
14561472
unsigned long *sectors_defragged,
1457-
unsigned long max_sectors)
1473+
unsigned long max_sectors,
1474+
u64 *last_scanned_ret)
14581475
{
14591476
const u32 sectorsize = inode->root->fs_info->sectorsize;
14601477
struct defrag_target_range *entry;
@@ -1465,7 +1482,7 @@ static int defrag_one_cluster(struct btrfs_inode *inode,
14651482
BUILD_BUG_ON(!IS_ALIGNED(CLUSTER_SIZE, PAGE_SIZE));
14661483
ret = defrag_collect_targets(inode, start, len, extent_thresh,
14671484
newer_than, do_compress, false,
1468-
&target_list);
1485+
&target_list, NULL);
14691486
if (ret < 0)
14701487
goto out;
14711488

@@ -1482,6 +1499,15 @@ static int defrag_one_cluster(struct btrfs_inode *inode,
14821499
range_len = min_t(u32, range_len,
14831500
(max_sectors - *sectors_defragged) * sectorsize);
14841501

1502+
/*
1503+
* If defrag_one_range() has updated last_scanned_ret,
1504+
* our range may already be invalid (e.g. hole punched).
1505+
* Skip if our range is before last_scanned_ret, as there is
1506+
* no need to defrag the range anymore.
1507+
*/
1508+
if (entry->start + range_len <= *last_scanned_ret)
1509+
continue;
1510+
14851511
if (ra)
14861512
page_cache_sync_readahead(inode->vfs_inode.i_mapping,
14871513
ra, NULL, entry->start >> PAGE_SHIFT,
@@ -1494,7 +1520,8 @@ static int defrag_one_cluster(struct btrfs_inode *inode,
14941520
* accounting.
14951521
*/
14961522
ret = defrag_one_range(inode, entry->start, range_len,
1497-
extent_thresh, newer_than, do_compress);
1523+
extent_thresh, newer_than, do_compress,
1524+
last_scanned_ret);
14981525
if (ret < 0)
14991526
break;
15001527
*sectors_defragged += range_len >>
@@ -1505,6 +1532,8 @@ static int defrag_one_cluster(struct btrfs_inode *inode,
15051532
list_del_init(&entry->list);
15061533
kfree(entry);
15071534
}
1535+
if (ret >= 0)
1536+
*last_scanned_ret = max(*last_scanned_ret, start + len);
15081537
return ret;
15091538
}
15101539

@@ -1590,6 +1619,7 @@ int btrfs_defrag_file(struct inode *inode, struct file_ra_state *ra,
15901619

15911620
while (cur < last_byte) {
15921621
const unsigned long prev_sectors_defragged = sectors_defragged;
1622+
u64 last_scanned = cur;
15931623
u64 cluster_end;
15941624

15951625
/* The cluster size 256K should always be page aligned */
@@ -1619,16 +1649,16 @@ int btrfs_defrag_file(struct inode *inode, struct file_ra_state *ra,
16191649
BTRFS_I(inode)->defrag_compress = compress_type;
16201650
ret = defrag_one_cluster(BTRFS_I(inode), ra, cur,
16211651
cluster_end + 1 - cur, extent_thresh,
1622-
newer_than, do_compress,
1623-
&sectors_defragged, max_to_defrag);
1652+
newer_than, do_compress, &sectors_defragged,
1653+
max_to_defrag, &last_scanned);
16241654

16251655
if (sectors_defragged > prev_sectors_defragged)
16261656
balance_dirty_pages_ratelimited(inode->i_mapping);
16271657

16281658
btrfs_inode_unlock(inode, 0);
16291659
if (ret < 0)
16301660
break;
1631-
cur = cluster_end + 1;
1661+
cur = max(cluster_end + 1, last_scanned);
16321662
if (ret > 0) {
16331663
ret = 0;
16341664
break;

0 commit comments

Comments
 (0)