Skip to content

Commit e1d227a

Browse files
Mark Fashehmasoncl
authored andcommitted
btrfs: Handle unaligned length in extent_same
The extent-same code rejects requests with an unaligned length. This poses a problem when we want to dedupe the tail extent of files as we skip cloning the portion between i_size and the extent boundary. If we don't clone the entire extent, it won't be deleted. So the combination of these behaviors winds up giving us worst-case dedupe on many files. We can fix this by allowing a length that extents to i_size and internally aligining those to the end of the block. This is what btrfs_ioctl_clone() so we can just copy that check over. Signed-off-by: Mark Fasheh <[email protected]> Signed-off-by: Chris Mason <[email protected]>
1 parent 070034b commit e1d227a

File tree

1 file changed

+14
-6
lines changed

1 file changed

+14
-6
lines changed

fs/btrfs/ioctl.c

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2889,23 +2889,31 @@ static int btrfs_cmp_data(struct inode *src, u64 loff, struct inode *dst,
28892889
return ret;
28902890
}
28912891

2892-
static int extent_same_check_offsets(struct inode *inode, u64 off, u64 len)
2892+
static int extent_same_check_offsets(struct inode *inode, u64 off, u64 *plen,
2893+
u64 olen)
28932894
{
2895+
u64 len = *plen;
28942896
u64 bs = BTRFS_I(inode)->root->fs_info->sb->s_blocksize;
28952897

2896-
if (off + len > inode->i_size || off + len < off)
2898+
if (off + olen > inode->i_size || off + olen < off)
28972899
return -EINVAL;
2900+
2901+
/* if we extend to eof, continue to block boundary */
2902+
if (off + len == inode->i_size)
2903+
*plen = len = ALIGN(inode->i_size, bs) - off;
2904+
28982905
/* Check that we are block aligned - btrfs_clone() requires this */
28992906
if (!IS_ALIGNED(off, bs) || !IS_ALIGNED(off + len, bs))
29002907
return -EINVAL;
29012908

29022909
return 0;
29032910
}
29042911

2905-
static int btrfs_extent_same(struct inode *src, u64 loff, u64 len,
2912+
static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen,
29062913
struct inode *dst, u64 dst_loff)
29072914
{
29082915
int ret;
2916+
u64 len = olen;
29092917

29102918
/*
29112919
* btrfs_clone() can't handle extents in the same file
@@ -2920,11 +2928,11 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 len,
29202928

29212929
btrfs_double_lock(src, loff, dst, dst_loff, len);
29222930

2923-
ret = extent_same_check_offsets(src, loff, len);
2931+
ret = extent_same_check_offsets(src, loff, &len, olen);
29242932
if (ret)
29252933
goto out_unlock;
29262934

2927-
ret = extent_same_check_offsets(dst, dst_loff, len);
2935+
ret = extent_same_check_offsets(dst, dst_loff, &len, olen);
29282936
if (ret)
29292937
goto out_unlock;
29302938

@@ -2937,7 +2945,7 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 len,
29372945

29382946
ret = btrfs_cmp_data(src, loff, dst, dst_loff, len);
29392947
if (ret == 0)
2940-
ret = btrfs_clone(src, dst, loff, len, len, dst_loff);
2948+
ret = btrfs_clone(src, dst, loff, olen, len, dst_loff);
29412949

29422950
out_unlock:
29432951
btrfs_double_unlock(src, loff, dst, dst_loff, len);

0 commit comments

Comments
 (0)