This patch re-uses the existing btrfs file cloning ioctl code to
implement the .splice_direct copy offloading file operation.

The existing extent item copying btrfs_ioctl_clone() is renamed to a
shared btrfs_clone_extents().  The ioctl specific code (mostly simple
entry-point stuff that splice() already does elsewhere) is moved to a
new much smaller btrfs_ioctl_clone().

btrfs_splice_direct() thus inherits the conservative limitations of the
btrfs clone ioctl: it only allows block-aligned copies between files on
the same snapshot.

Signed-off-by: Zach Brown <z...@redhat.com>
---
 fs/btrfs/ctree.h |  2 ++
 fs/btrfs/file.c  | 11 ++++++++++
 fs/btrfs/ioctl.c | 64 +++++++++++++++++++++++++++++++-------------------------
 3 files changed, 48 insertions(+), 29 deletions(-)

diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index e795bf1..f73830e 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -3648,6 +3648,8 @@ int btrfs_defrag_file(struct inode *inode, struct file 
*file,
                      u64 newer_than, unsigned long max_pages);
 void btrfs_get_block_group_info(struct list_head *groups_list,
                                struct btrfs_ioctl_space_info *space);
+long btrfs_clone_extents(struct file *file, struct file *src_file, u64 off,
+                        u64 olen, u64 destoff);
 
 /* file.c */
 int btrfs_auto_defrag_init(void);
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 4d2eb64..82aec93 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -2557,6 +2557,16 @@ out:
        return offset;
 }
 
+static long btrfs_splice_direct(struct file *in, loff_t in_pos,
+                               struct file *out, loff_t out_pos, size_t len,
+                               unsigned int flags)
+{
+       int ret = btrfs_clone_extents(out, in, in_pos, len, out_pos);
+       if (ret == 0)
+               ret = len;
+       return ret;
+}
+
 const struct file_operations btrfs_file_operations = {
        .llseek         = btrfs_file_llseek,
        .read           = do_sync_read,
@@ -2573,6 +2583,7 @@ const struct file_operations btrfs_file_operations = {
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = btrfs_ioctl,
 #endif
+       .splice_direct  = btrfs_splice_direct,
 };
 
 void btrfs_auto_defrag_exit(void)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 238a055..cddf6ef 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -2469,13 +2469,12 @@ out:
        return ret;
 }
 
-static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
-                                      u64 off, u64 olen, u64 destoff)
+long btrfs_clone_extents(struct file *file, struct file *src_file, u64 off,
+                        u64 olen, u64 destoff)
 {
        struct inode *inode = file_inode(file);
+       struct inode *src = file_inode(src_file);
        struct btrfs_root *root = BTRFS_I(inode)->root;
-       struct fd src_file;
-       struct inode *src;
        struct btrfs_trans_handle *trans;
        struct btrfs_path *path;
        struct extent_buffer *leaf;
@@ -2498,10 +2497,6 @@ static noinline long btrfs_ioctl_clone(struct file 
*file, unsigned long srcfd,
         *   they don't overlap)?
         */
 
-       /* the destination must be opened for writing */
-       if (!(file->f_mode & FMODE_WRITE) || (file->f_flags & O_APPEND))
-               return -EINVAL;
-
        if (btrfs_root_readonly(root))
                return -EROFS;
 
@@ -2509,48 +2504,36 @@ static noinline long btrfs_ioctl_clone(struct file 
*file, unsigned long srcfd,
        if (ret)
                return ret;
 
-       src_file = fdget(srcfd);
-       if (!src_file.file) {
-               ret = -EBADF;
-               goto out_drop_write;
-       }
-
        ret = -EXDEV;
-       if (src_file.file->f_path.mnt != file->f_path.mnt)
-               goto out_fput;
-
-       src = file_inode(src_file.file);
+       if (src_file->f_path.mnt != file->f_path.mnt)
+               goto out_drop_write;
 
        ret = -EINVAL;
        if (src == inode)
                same_inode = 1;
 
-       /* the src must be open for reading */
-       if (!(src_file.file->f_mode & FMODE_READ))
-               goto out_fput;
-
        /* don't make the dst file partly checksummed */
        if ((BTRFS_I(src)->flags & BTRFS_INODE_NODATASUM) !=
            (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM))
-               goto out_fput;
+               goto out_drop_write;
 
        ret = -EISDIR;
        if (S_ISDIR(src->i_mode) || S_ISDIR(inode->i_mode))
-               goto out_fput;
+               goto out_drop_write;
 
        ret = -EXDEV;
        if (src->i_sb != inode->i_sb)
-               goto out_fput;
+               goto out_drop_write;
 
        ret = -ENOMEM;
        buf = vmalloc(btrfs_level_size(root, 0));
        if (!buf)
-               goto out_fput;
+               goto out_drop_write;
 
        path = btrfs_alloc_path();
        if (!path) {
                vfree(buf);
-               goto out_fput;
+               goto out_drop_write;
        }
        path->reada = 2;
 
@@ -2867,13 +2850,36 @@ out_unlock:
                mutex_unlock(&inode->i_mutex);
        vfree(buf);
        btrfs_free_path(path);
-out_fput:
-       fdput(src_file);
 out_drop_write:
        mnt_drop_write_file(file);
        return ret;
 }
 
+static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
+                                      u64 off, u64 olen, u64 destoff)
+{
+       struct fd src_file;
+       int ret;
+
+       /* the destination must be opened for writing */
+       if (!(file->f_mode & FMODE_WRITE) || (file->f_flags & O_APPEND))
+               return -EINVAL;
+
+       src_file = fdget(srcfd);
+       if (!src_file.file)
+               return -EBADF;
+
+       /* the src must be open for reading */
+       if (!(src_file.file->f_mode & FMODE_READ))
+               ret = -EINVAL;
+       else
+               ret = btrfs_clone_extents(file, src_file.file, off, olen,
+                                         destoff);
+
+       fdput(src_file);
+       return ret;
+}
+
 static long btrfs_ioctl_clone_range(struct file *file, void __user *argp)
 {
        struct btrfs_ioctl_clone_range_args args;
-- 
1.7.11.7

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to