btrfs: use kvzalloc for EXTENT_SAME temporary data
[sfrench/cifs-2.6.git] / fs / btrfs / ioctl.c
index 47c1b96df0991c5c3b36d9f28a859c5398b7ba24..aeef6cd8aaeb242feba6b409582d97b11ac83c0e 100644 (file)
@@ -140,18 +140,18 @@ static unsigned int btrfs_inode_flags_to_fsflags(unsigned int flags)
  */
 void btrfs_sync_inode_flags_to_i_flags(struct inode *inode)
 {
-       struct btrfs_inode *ip = BTRFS_I(inode);
+       struct btrfs_inode *binode = BTRFS_I(inode);
        unsigned int new_fl = 0;
 
-       if (ip->flags & BTRFS_INODE_SYNC)
+       if (binode->flags & BTRFS_INODE_SYNC)
                new_fl |= S_SYNC;
-       if (ip->flags & BTRFS_INODE_IMMUTABLE)
+       if (binode->flags & BTRFS_INODE_IMMUTABLE)
                new_fl |= S_IMMUTABLE;
-       if (ip->flags & BTRFS_INODE_APPEND)
+       if (binode->flags & BTRFS_INODE_APPEND)
                new_fl |= S_APPEND;
-       if (ip->flags & BTRFS_INODE_NOATIME)
+       if (binode->flags & BTRFS_INODE_NOATIME)
                new_fl |= S_NOATIME;
-       if (ip->flags & BTRFS_INODE_DIRSYNC)
+       if (binode->flags & BTRFS_INODE_DIRSYNC)
                new_fl |= S_DIRSYNC;
 
        set_mask_bits(&inode->i_flags,
@@ -161,8 +161,8 @@ void btrfs_sync_inode_flags_to_i_flags(struct inode *inode)
 
 static int btrfs_ioctl_getflags(struct file *file, void __user *arg)
 {
-       struct btrfs_inode *ip = BTRFS_I(file_inode(file));
-       unsigned int flags = btrfs_inode_flags_to_fsflags(ip->flags);
+       struct btrfs_inode *binode = BTRFS_I(file_inode(file));
+       unsigned int flags = btrfs_inode_flags_to_fsflags(binode->flags);
 
        if (copy_to_user(arg, &flags, sizeof(flags)))
                return -EFAULT;
@@ -189,13 +189,13 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
 {
        struct inode *inode = file_inode(file);
        struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
-       struct btrfs_inode *ip = BTRFS_I(inode);
-       struct btrfs_root *root = ip->root;
+       struct btrfs_inode *binode = BTRFS_I(inode);
+       struct btrfs_root *root = binode->root;
        struct btrfs_trans_handle *trans;
-       unsigned int flags, oldflags;
+       unsigned int fsflags, old_fsflags;
        int ret;
-       u64 ip_oldflags;
-       unsigned int i_oldflags;
+       u64 old_flags;
+       unsigned int old_i_flags;
        umode_t mode;
 
        if (!inode_owner_or_capable(inode))
@@ -204,10 +204,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
        if (btrfs_root_readonly(root))
                return -EROFS;
 
-       if (copy_from_user(&flags, arg, sizeof(flags)))
+       if (copy_from_user(&fsflags, arg, sizeof(fsflags)))
                return -EFAULT;
 
-       ret = check_fsflags(flags);
+       ret = check_fsflags(fsflags);
        if (ret)
                return ret;
 
@@ -217,44 +217,44 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
 
        inode_lock(inode);
 
-       ip_oldflags = ip->flags;
-       i_oldflags = inode->i_flags;
+       old_flags = binode->flags;
+       old_i_flags = inode->i_flags;
        mode = inode->i_mode;
 
-       flags = btrfs_mask_fsflags_for_type(inode, flags);
-       oldflags = btrfs_inode_flags_to_fsflags(ip->flags);
-       if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
+       fsflags = btrfs_mask_fsflags_for_type(inode, fsflags);
+       old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
+       if ((fsflags ^ old_fsflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
                if (!capable(CAP_LINUX_IMMUTABLE)) {
                        ret = -EPERM;
                        goto out_unlock;
                }
        }
 
-       if (flags & FS_SYNC_FL)
-               ip->flags |= BTRFS_INODE_SYNC;
+       if (fsflags & FS_SYNC_FL)
+               binode->flags |= BTRFS_INODE_SYNC;
        else
-               ip->flags &= ~BTRFS_INODE_SYNC;
-       if (flags & FS_IMMUTABLE_FL)
-               ip->flags |= BTRFS_INODE_IMMUTABLE;
+               binode->flags &= ~BTRFS_INODE_SYNC;
+       if (fsflags & FS_IMMUTABLE_FL)
+               binode->flags |= BTRFS_INODE_IMMUTABLE;
        else
-               ip->flags &= ~BTRFS_INODE_IMMUTABLE;
-       if (flags & FS_APPEND_FL)
-               ip->flags |= BTRFS_INODE_APPEND;
+               binode->flags &= ~BTRFS_INODE_IMMUTABLE;
+       if (fsflags & FS_APPEND_FL)
+               binode->flags |= BTRFS_INODE_APPEND;
        else
-               ip->flags &= ~BTRFS_INODE_APPEND;
-       if (flags & FS_NODUMP_FL)
-               ip->flags |= BTRFS_INODE_NODUMP;
+               binode->flags &= ~BTRFS_INODE_APPEND;
+       if (fsflags & FS_NODUMP_FL)
+               binode->flags |= BTRFS_INODE_NODUMP;
        else
-               ip->flags &= ~BTRFS_INODE_NODUMP;
-       if (flags & FS_NOATIME_FL)
-               ip->flags |= BTRFS_INODE_NOATIME;
+               binode->flags &= ~BTRFS_INODE_NODUMP;
+       if (fsflags & FS_NOATIME_FL)
+               binode->flags |= BTRFS_INODE_NOATIME;
        else
-               ip->flags &= ~BTRFS_INODE_NOATIME;
-       if (flags & FS_DIRSYNC_FL)
-               ip->flags |= BTRFS_INODE_DIRSYNC;
+               binode->flags &= ~BTRFS_INODE_NOATIME;
+       if (fsflags & FS_DIRSYNC_FL)
+               binode->flags |= BTRFS_INODE_DIRSYNC;
        else
-               ip->flags &= ~BTRFS_INODE_DIRSYNC;
-       if (flags & FS_NOCOW_FL) {
+               binode->flags &= ~BTRFS_INODE_DIRSYNC;
+       if (fsflags & FS_NOCOW_FL) {
                if (S_ISREG(mode)) {
                        /*
                         * It's safe to turn csums off here, no extents exist.
@@ -262,10 +262,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
                         * status of the file and will not set it.
                         */
                        if (inode->i_size == 0)
-                               ip->flags |= BTRFS_INODE_NODATACOW
-                                          | BTRFS_INODE_NODATASUM;
+                               binode->flags |= BTRFS_INODE_NODATACOW
+                                             | BTRFS_INODE_NODATASUM;
                } else {
-                       ip->flags |= BTRFS_INODE_NODATACOW;
+                       binode->flags |= BTRFS_INODE_NODATACOW;
                }
        } else {
                /*
@@ -273,10 +273,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
                 */
                if (S_ISREG(mode)) {
                        if (inode->i_size == 0)
-                               ip->flags &= ~(BTRFS_INODE_NODATACOW
+                               binode->flags &= ~(BTRFS_INODE_NODATACOW
                                             | BTRFS_INODE_NODATASUM);
                } else {
-                       ip->flags &= ~BTRFS_INODE_NODATACOW;
+                       binode->flags &= ~BTRFS_INODE_NODATACOW;
                }
        }
 
@@ -285,18 +285,18 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
         * flag may be changed automatically if compression code won't make
         * things smaller.
         */
-       if (flags & FS_NOCOMP_FL) {
-               ip->flags &= ~BTRFS_INODE_COMPRESS;
-               ip->flags |= BTRFS_INODE_NOCOMPRESS;
+       if (fsflags & FS_NOCOMP_FL) {
+               binode->flags &= ~BTRFS_INODE_COMPRESS;
+               binode->flags |= BTRFS_INODE_NOCOMPRESS;
 
                ret = btrfs_set_prop(inode, "btrfs.compression", NULL, 0, 0);
                if (ret && ret != -ENODATA)
                        goto out_drop;
-       } else if (flags & FS_COMPR_FL) {
+       } else if (fsflags & FS_COMPR_FL) {
                const char *comp;
 
-               ip->flags |= BTRFS_INODE_COMPRESS;
-               ip->flags &= ~BTRFS_INODE_NOCOMPRESS;
+               binode->flags |= BTRFS_INODE_COMPRESS;
+               binode->flags &= ~BTRFS_INODE_NOCOMPRESS;
 
                comp = btrfs_compress_type2str(fs_info->compress_type);
                if (!comp || comp[0] == 0)
@@ -311,7 +311,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
                ret = btrfs_set_prop(inode, "btrfs.compression", NULL, 0, 0);
                if (ret && ret != -ENODATA)
                        goto out_drop;
-               ip->flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS);
+               binode->flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS);
        }
 
        trans = btrfs_start_transaction(root, 1);
@@ -328,8 +328,8 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
        btrfs_end_transaction(trans);
  out_drop:
        if (ret) {
-               ip->flags = ip_oldflags;
-               inode->i_flags = i_oldflags;
+               binode->flags = old_flags;
+               inode->i_flags = old_i_flags;
        }
 
  out_unlock:
@@ -338,6 +338,148 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
        return ret;
 }
 
+/*
+ * Translate btrfs internal inode flags to xflags as expected by the
+ * FS_IOC_FSGETXATT ioctl. Filter only the supported ones, unknown flags are
+ * silently dropped.
+ */
+static unsigned int btrfs_inode_flags_to_xflags(unsigned int flags)
+{
+       unsigned int xflags = 0;
+
+       if (flags & BTRFS_INODE_APPEND)
+               xflags |= FS_XFLAG_APPEND;
+       if (flags & BTRFS_INODE_IMMUTABLE)
+               xflags |= FS_XFLAG_IMMUTABLE;
+       if (flags & BTRFS_INODE_NOATIME)
+               xflags |= FS_XFLAG_NOATIME;
+       if (flags & BTRFS_INODE_NODUMP)
+               xflags |= FS_XFLAG_NODUMP;
+       if (flags & BTRFS_INODE_SYNC)
+               xflags |= FS_XFLAG_SYNC;
+
+       return xflags;
+}
+
+/* Check if @flags are a supported and valid set of FS_XFLAGS_* flags */
+static int check_xflags(unsigned int flags)
+{
+       if (flags & ~(FS_XFLAG_APPEND | FS_XFLAG_IMMUTABLE | FS_XFLAG_NOATIME |
+                     FS_XFLAG_NODUMP | FS_XFLAG_SYNC))
+               return -EOPNOTSUPP;
+       return 0;
+}
+
+/*
+ * Set the xflags from the internal inode flags. The remaining items of fsxattr
+ * are zeroed.
+ */
+static int btrfs_ioctl_fsgetxattr(struct file *file, void __user *arg)
+{
+       struct btrfs_inode *binode = BTRFS_I(file_inode(file));
+       struct fsxattr fa;
+
+       memset(&fa, 0, sizeof(fa));
+       fa.fsx_xflags = btrfs_inode_flags_to_xflags(binode->flags);
+
+       if (copy_to_user(arg, &fa, sizeof(fa)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int btrfs_ioctl_fssetxattr(struct file *file, void __user *arg)
+{
+       struct inode *inode = file_inode(file);
+       struct btrfs_inode *binode = BTRFS_I(inode);
+       struct btrfs_root *root = binode->root;
+       struct btrfs_trans_handle *trans;
+       struct fsxattr fa;
+       unsigned old_flags;
+       unsigned old_i_flags;
+       int ret = 0;
+
+       if (!inode_owner_or_capable(inode))
+               return -EPERM;
+
+       if (btrfs_root_readonly(root))
+               return -EROFS;
+
+       memset(&fa, 0, sizeof(fa));
+       if (copy_from_user(&fa, arg, sizeof(fa)))
+               return -EFAULT;
+
+       ret = check_xflags(fa.fsx_xflags);
+       if (ret)
+               return ret;
+
+       if (fa.fsx_extsize != 0 || fa.fsx_projid != 0 || fa.fsx_cowextsize != 0)
+               return -EOPNOTSUPP;
+
+       ret = mnt_want_write_file(file);
+       if (ret)
+               return ret;
+
+       inode_lock(inode);
+
+       old_flags = binode->flags;
+       old_i_flags = inode->i_flags;
+
+       /* We need the capabilities to change append-only or immutable inode */
+       if (((old_flags & (BTRFS_INODE_APPEND | BTRFS_INODE_IMMUTABLE)) ||
+            (fa.fsx_xflags & (FS_XFLAG_APPEND | FS_XFLAG_IMMUTABLE))) &&
+           !capable(CAP_LINUX_IMMUTABLE)) {
+               ret = -EPERM;
+               goto out_unlock;
+       }
+
+       if (fa.fsx_xflags & FS_XFLAG_SYNC)
+               binode->flags |= BTRFS_INODE_SYNC;
+       else
+               binode->flags &= ~BTRFS_INODE_SYNC;
+       if (fa.fsx_xflags & FS_XFLAG_IMMUTABLE)
+               binode->flags |= BTRFS_INODE_IMMUTABLE;
+       else
+               binode->flags &= ~BTRFS_INODE_IMMUTABLE;
+       if (fa.fsx_xflags & FS_XFLAG_APPEND)
+               binode->flags |= BTRFS_INODE_APPEND;
+       else
+               binode->flags &= ~BTRFS_INODE_APPEND;
+       if (fa.fsx_xflags & FS_XFLAG_NODUMP)
+               binode->flags |= BTRFS_INODE_NODUMP;
+       else
+               binode->flags &= ~BTRFS_INODE_NODUMP;
+       if (fa.fsx_xflags & FS_XFLAG_NOATIME)
+               binode->flags |= BTRFS_INODE_NOATIME;
+       else
+               binode->flags &= ~BTRFS_INODE_NOATIME;
+
+       /* 1 item for the inode */
+       trans = btrfs_start_transaction(root, 1);
+       if (IS_ERR(trans)) {
+               ret = PTR_ERR(trans);
+               goto out_unlock;
+       }
+
+       btrfs_sync_inode_flags_to_i_flags(inode);
+       inode_inc_iversion(inode);
+       inode->i_ctime = current_time(inode);
+       ret = btrfs_update_inode(trans, root, inode);
+
+       btrfs_end_transaction(trans);
+
+out_unlock:
+       if (ret) {
+               binode->flags = old_flags;
+               inode->i_flags = old_i_flags;
+       }
+
+       inode_unlock(inode);
+       mnt_drop_write_file(file);
+
+       return ret;
+}
+
 static int btrfs_ioctl_getversion(struct file *file, int __user *arg)
 {
        struct inode *inode = file_inode(file);
@@ -2772,8 +2914,6 @@ static void btrfs_cmp_data_free(struct cmp_pages *cmp)
                        put_page(pg);
                }
        }
-       kfree(cmp->src_pages);
-       kfree(cmp->dst_pages);
 }
 
 static int btrfs_cmp_data_prepare(struct inode *src, u64 loff,
@@ -2782,40 +2922,14 @@ static int btrfs_cmp_data_prepare(struct inode *src, u64 loff,
 {
        int ret;
        int num_pages = PAGE_ALIGN(len) >> PAGE_SHIFT;
-       struct page **src_pgarr, **dst_pgarr;
 
-       /*
-        * We must gather up all the pages before we initiate our
-        * extent locking. We use an array for the page pointers. Size
-        * of the array is bounded by len, which is in turn bounded by
-        * BTRFS_MAX_DEDUPE_LEN.
-        */
-       src_pgarr = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL);
-       dst_pgarr = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL);
-       if (!src_pgarr || !dst_pgarr) {
-               kfree(src_pgarr);
-               kfree(dst_pgarr);
-               return -ENOMEM;
-       }
        cmp->num_pages = num_pages;
-       cmp->src_pages = src_pgarr;
-       cmp->dst_pages = dst_pgarr;
 
-       /*
-        * If deduping ranges in the same inode, locking rules make it mandatory
-        * to always lock pages in ascending order to avoid deadlocks with
-        * concurrent tasks (such as starting writeback/delalloc).
-        */
-       if (src == dst && dst_loff < loff) {
-               swap(src_pgarr, dst_pgarr);
-               swap(loff, dst_loff);
-       }
-
-       ret = gather_extent_pages(src, src_pgarr, cmp->num_pages, loff);
+       ret = gather_extent_pages(src, cmp->src_pages, num_pages, loff);
        if (ret)
                goto out;
 
-       ret = gather_extent_pages(dst, dst_pgarr, cmp->num_pages, dst_loff);
+       ret = gather_extent_pages(dst, cmp->dst_pages, num_pages, dst_loff);
 
 out:
        if (ret)
@@ -2885,31 +2999,23 @@ static int extent_same_check_offsets(struct inode *inode, u64 off, u64 *plen,
        return 0;
 }
 
-static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen,
-                            struct inode *dst, u64 dst_loff)
+static int btrfs_extent_same_range(struct inode *src, u64 loff, u64 olen,
+                                  struct inode *dst, u64 dst_loff,
+                                  struct cmp_pages *cmp)
 {
        int ret;
        u64 len = olen;
-       struct cmp_pages cmp;
        bool same_inode = (src == dst);
        u64 same_lock_start = 0;
        u64 same_lock_len = 0;
 
-       if (len == 0)
-               return 0;
-
-       if (same_inode)
-               inode_lock(src);
-       else
-               btrfs_double_inode_lock(src, dst);
-
        ret = extent_same_check_offsets(src, loff, &len, olen);
        if (ret)
-               goto out_unlock;
+               return ret;
 
        ret = extent_same_check_offsets(dst, dst_loff, &len, olen);
        if (ret)
-               goto out_unlock;
+               return ret;
 
        if (same_inode) {
                /*
@@ -2926,32 +3032,21 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen,
                 * allow an unaligned length so long as it ends at
                 * i_size.
                 */
-               if (len != olen) {
-                       ret = -EINVAL;
-                       goto out_unlock;
-               }
+               if (len != olen)
+                       return -EINVAL;
 
                /* Check for overlapping ranges */
-               if (dst_loff + len > loff && dst_loff < loff + len) {
-                       ret = -EINVAL;
-                       goto out_unlock;
-               }
+               if (dst_loff + len > loff && dst_loff < loff + len)
+                       return -EINVAL;
 
                same_lock_start = min_t(u64, loff, dst_loff);
                same_lock_len = max_t(u64, loff, dst_loff) + len - same_lock_start;
        }
 
-       /* don't make the dst file partly checksummed */
-       if ((BTRFS_I(src)->flags & BTRFS_INODE_NODATASUM) !=
-           (BTRFS_I(dst)->flags & BTRFS_INODE_NODATASUM)) {
-               ret = -EINVAL;
-               goto out_unlock;
-       }
-
 again:
-       ret = btrfs_cmp_data_prepare(src, loff, dst, dst_loff, olen, &cmp);
+       ret = btrfs_cmp_data_prepare(src, loff, dst, dst_loff, olen, cmp);
        if (ret)
-               goto out_unlock;
+               return ret;
 
        if (same_inode)
                ret = lock_extent_range(src, same_lock_start, same_lock_len,
@@ -2972,7 +3067,7 @@ again:
                 * Ranges in the io trees already unlocked. Now unlock all
                 * pages before waiting for all IO to complete.
                 */
-               btrfs_cmp_data_free(&cmp);
+               btrfs_cmp_data_free(cmp);
                if (same_inode) {
                        btrfs_wait_ordered_range(src, same_lock_start,
                                                 same_lock_len);
@@ -2985,12 +3080,12 @@ again:
        ASSERT(ret == 0);
        if (WARN_ON(ret)) {
                /* ranges in the io trees already unlocked */
-               btrfs_cmp_data_free(&cmp);
+               btrfs_cmp_data_free(cmp);
                return ret;
        }
 
        /* pass original length for comparison so we stay within i_size */
-       ret = btrfs_cmp_data(olen, &cmp);
+       ret = btrfs_cmp_data(olen, cmp);
        if (ret == 0)
                ret = btrfs_clone(src, dst, loff, olen, len, dst_loff, 1);
 
@@ -3000,18 +3095,91 @@ again:
        else
                btrfs_double_extent_unlock(src, loff, dst, dst_loff, len);
 
-       btrfs_cmp_data_free(&cmp);
+       btrfs_cmp_data_free(cmp);
+
+       return ret;
+}
+
+#define BTRFS_MAX_DEDUPE_LEN   SZ_16M
+
+static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen,
+                            struct inode *dst, u64 dst_loff)
+{
+       int ret;
+       struct cmp_pages cmp;
+       int num_pages = PAGE_ALIGN(BTRFS_MAX_DEDUPE_LEN) >> PAGE_SHIFT;
+       bool same_inode = (src == dst);
+       u64 i, tail_len, chunk_count;
+
+       if (olen == 0)
+               return 0;
+
+       if (same_inode)
+               inode_lock(src);
+       else
+               btrfs_double_inode_lock(src, dst);
+
+       /* don't make the dst file partly checksummed */
+       if ((BTRFS_I(src)->flags & BTRFS_INODE_NODATASUM) !=
+           (BTRFS_I(dst)->flags & BTRFS_INODE_NODATASUM)) {
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+
+       tail_len = olen % BTRFS_MAX_DEDUPE_LEN;
+       chunk_count = div_u64(olen, BTRFS_MAX_DEDUPE_LEN);
+       if (chunk_count == 0)
+               num_pages = PAGE_ALIGN(tail_len) >> PAGE_SHIFT;
+
+       /*
+        * If deduping ranges in the same inode, locking rules make it
+        * mandatory to always lock pages in ascending order to avoid deadlocks
+        * with concurrent tasks (such as starting writeback/delalloc).
+        */
+       if (same_inode && dst_loff < loff)
+               swap(loff, dst_loff);
+
+       /*
+        * We must gather up all the pages before we initiate our extent
+        * locking. We use an array for the page pointers. Size of the array is
+        * bounded by len, which is in turn bounded by BTRFS_MAX_DEDUPE_LEN.
+        */
+       cmp.src_pages = kvmalloc_array(num_pages, sizeof(struct page *),
+                                      GFP_KERNEL | __GFP_ZERO);
+       cmp.dst_pages = kvmalloc_array(num_pages, sizeof(struct page *),
+                                      GFP_KERNEL | __GFP_ZERO);
+       if (!cmp.src_pages || !cmp.dst_pages) {
+               ret = -ENOMEM;
+               goto out_free;
+       }
+
+       for (i = 0; i < chunk_count; i++) {
+               ret = btrfs_extent_same_range(src, loff, BTRFS_MAX_DEDUPE_LEN,
+                                             dst, dst_loff, &cmp);
+               if (ret)
+                       goto out_unlock;
+
+               loff += BTRFS_MAX_DEDUPE_LEN;
+               dst_loff += BTRFS_MAX_DEDUPE_LEN;
+       }
+
+       if (tail_len > 0)
+               ret = btrfs_extent_same_range(src, loff, tail_len, dst,
+                                             dst_loff, &cmp);
+
 out_unlock:
        if (same_inode)
                inode_unlock(src);
        else
                btrfs_double_inode_unlock(src, dst);
 
+out_free:
+       kvfree(cmp.src_pages);
+       kvfree(cmp.dst_pages);
+
        return ret;
 }
 
-#define BTRFS_MAX_DEDUPE_LEN   SZ_16M
-
 ssize_t btrfs_dedupe_file_range(struct file *src_file, u64 loff, u64 olen,
                                struct file *dst_file, u64 dst_loff)
 {
@@ -3020,9 +3188,6 @@ ssize_t btrfs_dedupe_file_range(struct file *src_file, u64 loff, u64 olen,
        u64 bs = BTRFS_I(src)->root->fs_info->sb->s_blocksize;
        ssize_t res;
 
-       if (olen > BTRFS_MAX_DEDUPE_LEN)
-               olen = BTRFS_MAX_DEDUPE_LEN;
-
        if (WARN_ON_ONCE(bs < PAGE_SIZE)) {
                /*
                 * Btrfs does not support blocksize < page_size. As a
@@ -5377,6 +5542,10 @@ long btrfs_ioctl(struct file *file, unsigned int
                return btrfs_ioctl_get_features(file, argp);
        case BTRFS_IOC_SET_FEATURES:
                return btrfs_ioctl_set_features(file, argp);
+       case FS_IOC_FSGETXATTR:
+               return btrfs_ioctl_fsgetxattr(file, argp);
+       case FS_IOC_FSSETXATTR:
+               return btrfs_ioctl_fssetxattr(file, argp);
        }
 
        return -ENOTTY;