aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Fasheh <mfasheh@suse.de>2015-06-08 18:05:25 -0400
committerChris Mason <clm@fb.com>2015-06-10 10:02:50 -0400
commite1d227a42ea2b4664f94212bd1106b9a3413ffb8 (patch)
treee14df0ebdf2241ee848607f06fc39cef6d5c1db4
parent070034bdf98544b23a7fcf500618fd31dec06ab2 (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.c20
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
2892static int extent_same_check_offsets(struct inode *inode, u64 off, u64 len) 2892static 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
2905static int btrfs_extent_same(struct inode *src, u64 loff, u64 len, 2912static 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
2942out_unlock: 2950out_unlock:
2943 btrfs_double_unlock(src, loff, dst, dst_loff, len); 2951 btrfs_double_unlock(src, loff, dst, dst_loff, len);