# \[PATCH v1 0/8\] VFS: In-kernel copy system call

- Subsystems: BTRFS FILE SYSTEM, FILESYSTEMS (VFS and infrastructure), THE REST, X86 ARCHITECTURE (32-BIT AND 64-BIT), X86 ENTRY CODE
- Maintainers: David Sterba, Alexander Viro, Christian Brauner, Linus Torvalds, Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, Andy Lutomirski
- Series: 3 patches touching 6 files, +61/-42
- Status: SUPERSEDED (Superseded by v9 posted 2015-11-10)
- Superseded by v9 (posted 2015-11-10).
- Revision v1 (6 revisions archived).
- List: linux-api
- 5 messages, 3 authors
- Page: https://ratatoskr.run/linux-api/2015/09/99106/t

## \[PATCH v1 0/8\] VFS: In-kernel copy system call

- From: Anna Schumaker \<hidden\>
- Date: 2015-09-04 20:16:54 
- Message: https://ratatoskr.run/linux-api/2015/09/99106.md

```text
Copy system calls came up during Plumbers a couple of weeks ago, because
several filesystems (including NFS and XFS) are currently working on copy
acceleration implementations.  We haven't heard from Zach Brown in a while,
so I volunteered to push his patches upstream so individual filesystems
don't need to keep writing their own ioctls.

The first three patches are a simple reposting of Zach's patches from several
months ago, with one minor error code fix.  The remaining patches add in a
fallback mechanism when filesystems don't provide a copy function.  This is
especially useful when doing a server-side copy on NFS (using the new COPY
operation in NFS v4.2).  This fallback can be disabled by passing the flag
COPY_REFLINK to the system call.

The last patch is a man page patch documenting this new system call,
including an example program.

I tested the fallback option by using /dev/urandom to generate files of
varying sizes and copying them.  I compared the time to copy against that
of `cp` just to see if there is a noticable difference.  I found that
runtimes are roughly the same, but in-kernel copy tends to use less of
the cpu.  Values in the tables below are averages across multiple trials.


 /usr/bin/cp |   512 MB  |   1024 MB |   1536 MB |   2048 MB
-------------|-----------|-----------|-----------|-----------
       user  |   0.00s   |   0.00s   |   0.00s   |   0.00s
     system  |   0.32s   |   0.52s   |   1.04s   |   1.04s
        cpu  |     73%   |     69%   |     62%   |     62%
      total  |   0.446   |   0.757   |   1.197   |   1.667


   VFS copy  |   512 MB  |   1024 MB |   1536 MB |   2048 MB
-------------|-----------|-----------|-----------|-----------
       user  |   0.00s   |   0.00s   |   0.00s   |  0.00s
     system  |   0.33s   |   0.49s   |   0.76s   |  0.99s
        cpu  |     77%   |     62%   |     60%   |    59%
      total  |   0.422   |   0.777   |   1.267   |  1.655


Questions?  Comments?  Thoughts?

Anna


Anna Schumaker (5):
  btrfs: Add mountpoint checking during btrfs_copy_file_range
  vfs: Remove copy_file_range mountpoint checks
  vfs: Copy should check len after file open mode
  vfs: Copy should use file_out rather than file_in
  vfs: Fall back on splice if no copy function defined

Zach Brown (3):
  vfs: add copy_file_range syscall and vfs helper
  x86: add sys_copy_file_range to syscall tables
  btrfs: add .copy_file_range file operation

 arch/x86/entry/syscalls/syscall_32.tbl |   1 +
 arch/x86/entry/syscalls/syscall_64.tbl |   1 +
 fs/btrfs/ctree.h                       |   3 +
 fs/btrfs/file.c                        |   1 +
 fs/btrfs/ioctl.c                       |  95 ++++++++++++++----------
 fs/read_write.c                        | 132 +++++++++++++++++++++++++++++++++
 include/linux/copy.h                   |   6 ++
 include/linux/fs.h                     |   3 +
 include/uapi/asm-generic/unistd.h      |   4 +-
 include/uapi/linux/Kbuild              |   1 +
 include/uapi/linux/copy.h              |   6 ++
 kernel/sys_ni.c                        |   1 +
 12 files changed, 214 insertions(+), 40 deletions(-)
 create mode 100644 include/linux/copy.h
 create mode 100644 include/uapi/linux/copy.h

-- 
2.5.1

--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
```

## \[PATCH v1 2/8\] x86: add sys\_copy\_file\_range to syscall tables

- From: Anna Schumaker \<hidden\>
- Date: 2015-09-04 20:16:56 
- Message: https://ratatoskr.run/linux-api/2015/09/99108.md

```text
From: Zach Brown <redacted>

Add sys_copy_file_range to the x86 syscall tables.

Signed-off-by: Zach Brown <redacted>
Signed-off-by: Anna Schumaker <redacted>
---
 arch/x86/entry/syscalls/syscall_32.tbl | 1 +
 arch/x86/entry/syscalls/syscall_64.tbl | 1 +
 2 files changed, 2 insertions(+)

diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index ef8187f..2f5e1e0 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -365,3 +365,4 @@
 356	i386	memfd_create		sys_memfd_create
 357	i386	bpf			sys_bpf
 358	i386	execveat		sys_execveat			stub32_execveat
+359	i386	copy_file_range		sys_copy_file_range
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index 9ef32d5..b2101de 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -329,6 +329,7 @@
 320	common	kexec_file_load		sys_kexec_file_load
 321	common	bpf			sys_bpf
 322	64	execveat		stub_execveat
+323	common	copy_file_range		sys_copy_file_range
 
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
-- 
2.5.1

```

## \[PATCH v1 7/8\] vfs: Copy should use file\_out rather than file\_in

- From: Anna Schumaker \<hidden\>
- Date: 2015-09-04 20:17:01 
- Message: https://ratatoskr.run/linux-api/2015/09/99110.md

```text
The way to think about this is that the destination filesystem reads the
data from the source file and processes it accordingly.  This is
especially important to avoid an infinate loop when doing a "server to
server" copy on NFS.

Signed-off-by: Anna Schumaker <redacted>
---
 fs/read_write.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/fs/read_write.c b/fs/read_write.c
index 59637ed..9dafb7f 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -1355,7 +1355,7 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
 	if (!(file_in->f_mode & FMODE_READ) ||
 	    !(file_out->f_mode & FMODE_WRITE) ||
 	    (file_out->f_flags & O_APPEND) ||
-	    !file_in->f_op || !file_in->f_op->copy_file_range)
+	    !file_out->f_op || !file_out->f_op->copy_file_range)
 		return -EBADF;
 
 	inode_in = file_inode(file_in);
@@ -1377,8 +1377,8 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
 	if (ret)
 		return ret;
 
-	ret = file_in->f_op->copy_file_range(file_in, pos_in, file_out, pos_out,
-					     len, flags);
+	ret = file_out->f_op->copy_file_range(file_in, pos_in, file_out, pos_out,
+					      len, flags);
 	if (ret > 0) {
 		fsnotify_access(file_in);
 		add_rchar(current, ret);
-- 
2.5.1

```

## \[PATCH v1 3/8\] btrfs: add .copy\_file\_range file operation

- From: Anna Schumaker \<hidden\>
- Date: 2015-09-04 20:17:13 
- Message: https://ratatoskr.run/linux-api/2015/09/99109.md

```text
From: Zach Brown <redacted>

This rearranges the existing COPY_RANGE ioctl implementation so that the
.copy_file_range file operation can call the core loop that copies file
data extent items.

The extent copying loop is lifted up into its own function.  It retains
the core btrfs error checks that should be shared.

Signed-off-by: Zach Brown <redacted>
Signed-off-by: Anna Schumaker <redacted>
---
 fs/btrfs/ctree.h |  3 ++
 fs/btrfs/file.c  |  1 +
 fs/btrfs/ioctl.c | 91 ++++++++++++++++++++++++++++++++------------------------
 3 files changed, 56 insertions(+), 39 deletions(-)

diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index aac314e..e09d4e2 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -4000,6 +4000,9 @@ int btrfs_dirty_pages(struct btrfs_root *root, struct inode *inode,
 		      loff_t pos, size_t write_bytes,
 		      struct extent_state **cached);
 int btrfs_fdatawrite_range(struct inode *inode, loff_t start, loff_t end);
+ssize_t btrfs_copy_file_range(struct file *file_in, loff_t pos_in,
+			      struct file *file_out, loff_t pos_out,
+			      size_t len, int flags);
 
 /* tree-defrag.c */
 int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index b823fac..b05449c 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -2816,6 +2816,7 @@ const struct file_operations btrfs_file_operations = {
 #ifdef CONFIG_COMPAT
 	.compat_ioctl	= btrfs_ioctl,
 #endif
+	.copy_file_range = btrfs_copy_file_range,
 };
 
 void btrfs_auto_defrag_exit(void)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 0770c91..62ae286 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -3719,17 +3719,16 @@ out:
 	return ret;
 }
 
-static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
-				       u64 off, u64 olen, u64 destoff)
+static noinline int btrfs_clone_files(struct file *file, struct file *file_src,
+					u64 off, u64 olen, u64 destoff)
 {
 	struct inode *inode = file_inode(file);
+	struct inode *src = file_inode(file_src);
 	struct btrfs_root *root = BTRFS_I(inode)->root;
-	struct fd src_file;
-	struct inode *src;
 	int ret;
 	u64 len = olen;
 	u64 bs = root->fs_info->sb->s_blocksize;
-	int same_inode = 0;
+	int same_inode = src == inode;
 
 	/*
 	 * TODO:
@@ -3742,49 +3741,20 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
 	 *   be either compressed or non-compressed.
 	 */
 
-	/* 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;
 
-	ret = mnt_want_write_file(file);
-	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);
-
-	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;
+	if (file_src->f_path.mnt != file->f_path.mnt ||
+	    src->i_sb != inode->i_sb)
+		return -EXDEV;
 
 	/* 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;
+		return -EINVAL;
 
-	ret = -EISDIR;
 	if (S_ISDIR(src->i_mode) || S_ISDIR(inode->i_mode))
-		goto out_fput;
-
-	ret = -EXDEV;
-	if (src->i_sb != inode->i_sb)
-		goto out_fput;
+		return -EISDIR;
 
 	if (!same_inode) {
 		if (inode < src) {
@@ -3877,6 +3847,49 @@ out_unlock:
 	} else {
 		mutex_unlock(&src->i_mutex);
 	}
+	return ret;
+}
+
+ssize_t btrfs_copy_file_range(struct file *file_in, loff_t pos_in,
+			      struct file *file_out, loff_t pos_out,
+			      size_t len, int flags)
+{
+	ssize_t ret;
+
+	ret = btrfs_clone_files(file_out, file_in, pos_in, len, pos_out);
+	if (ret == 0)
+		ret = len;
+	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;
+
+	ret = mnt_want_write_file(file);
+	if (ret)
+		return ret;
+
+	src_file = fdget(srcfd);
+	if (!src_file.file) {
+		ret = -EBADF;
+		goto out_drop_write;
+	}
+
+	/* the src must be open for reading */
+	if (!(src_file.file->f_mode & FMODE_READ)) {
+		ret = -EINVAL;
+		goto out_fput;
+	}
+
+	ret = btrfs_clone_files(file, src_file.file, off, olen, destoff);
+
 out_fput:
 	fdput(src_file);
 out_drop_write:
-- 
2.5.1

```

## Re: \[PATCH v1 3/8\] btrfs: add .copy\_file\_range file operation

- From: David Sterba \<hidden\>
- Date: 2015-09-09 08:40:00 
- Message: https://ratatoskr.run/linux-api/2015/09/99224.md

```text
On Fri, Sep 04, 2015 at 04:16:57PM -0400, Anna Schumaker wrote:
> From: Zach Brown <redacted>
> 
> This rearranges the existing COPY_RANGE ioctl implementation so that the
> .copy_file_range file operation can call the core loop that copies file
> data extent items.
> 
> The extent copying loop is lifted up into its own function.  It retains
> the core btrfs error checks that should be shared.
> 
> Signed-off-by: Zach Brown <redacted>
> Signed-off-by: Anna Schumaker <redacted>

Reviewed-by: David Sterba <dsterba@suse.com>
```
