diff options
author | Mark Fasheh <mfasheh@suse.de> | 2015-06-08 18:05:25 -0400 |
---|---|---|
committer | Chris Mason <clm@fb.com> | 2015-06-10 10:02:50 -0400 |
commit | e1d227a42ea2b4664f94212bd1106b9a3413ffb8 (patch) | |
tree | e14df0ebdf2241ee848607f06fc39cef6d5c1db4 | |
parent | 070034bdf98544b23a7fcf500618fd31dec06ab2 (diff) |
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 <mfasheh@suse.de>
Signed-off-by: Chris Mason <clm@fb.com>
-rw-r--r-- | fs/btrfs/ioctl.c | 20 |
1 files changed, 14 insertions, 6 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 9041f154cc32..c86b835da7a8 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -2889,12 +2889,19 @@ static int btrfs_cmp_data(struct inode *src, u64 loff, struct inode *dst, | |||
2889 | return ret; | 2889 | return ret; |
2890 | } | 2890 | } |
2891 | 2891 | ||
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) | ||
2893 | { | 2894 | { |
2895 | u64 len = *plen; | ||
2894 | u64 bs = BTRFS_I(inode)->root->fs_info->sb->s_blocksize; | 2896 | u64 bs = BTRFS_I(inode)->root->fs_info->sb->s_blocksize; |
2895 | 2897 | ||
2896 | if (off + len > inode->i_size || off + len < off) | 2898 | if (off + olen > inode->i_size || off + olen < off) |
2897 | return -EINVAL; | 2899 | 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 | |||
2898 | /* Check that we are block aligned - btrfs_clone() requires this */ | 2905 | /* Check that we are block aligned - btrfs_clone() requires this */ |
2899 | if (!IS_ALIGNED(off, bs) || !IS_ALIGNED(off + len, bs)) | 2906 | if (!IS_ALIGNED(off, bs) || !IS_ALIGNED(off + len, bs)) |
2900 | return -EINVAL; | 2907 | return -EINVAL; |
@@ -2902,10 +2909,11 @@ static int extent_same_check_offsets(struct inode *inode, u64 off, u64 len) | |||
2902 | return 0; | 2909 | return 0; |
2903 | } | 2910 | } |
2904 | 2911 | ||
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, |
2906 | struct inode *dst, u64 dst_loff) | 2913 | struct inode *dst, u64 dst_loff) |
2907 | { | 2914 | { |
2908 | int ret; | 2915 | int ret; |
2916 | u64 len = olen; | ||
2909 | 2917 | ||
2910 | /* | 2918 | /* |
2911 | * btrfs_clone() can't handle extents in the same file | 2919 | * 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, | |||
2920 | 2928 | ||
2921 | btrfs_double_lock(src, loff, dst, dst_loff, len); | 2929 | btrfs_double_lock(src, loff, dst, dst_loff, len); |
2922 | 2930 | ||
2923 | ret = extent_same_check_offsets(src, loff, len); | 2931 | ret = extent_same_check_offsets(src, loff, &len, olen); |
2924 | if (ret) | 2932 | if (ret) |
2925 | goto out_unlock; | 2933 | goto out_unlock; |
2926 | 2934 | ||
2927 | ret = extent_same_check_offsets(dst, dst_loff, len); | 2935 | ret = extent_same_check_offsets(dst, dst_loff, &len, olen); |
2928 | if (ret) | 2936 | if (ret) |
2929 | goto out_unlock; | 2937 | goto out_unlock; |
2930 | 2938 | ||
@@ -2937,7 +2945,7 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 len, | |||
2937 | 2945 | ||
2938 | ret = btrfs_cmp_data(src, loff, dst, dst_loff, len); | 2946 | ret = btrfs_cmp_data(src, loff, dst, dst_loff, len); |
2939 | if (ret == 0) | 2947 | 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); |
2941 | 2949 | ||
2942 | out_unlock: | 2950 | out_unlock: |
2943 | btrfs_double_unlock(src, loff, dst, dst_loff, len); | 2951 | btrfs_double_unlock(src, loff, dst, dst_loff, len); |