Skip to contentSkip to conversation

vfs: move btrfs clone ioctls to common code

PATCH In review Peng Tao 4 participants 12 messages Oct 24, 2015 – Nov 13, 2015 2 messages not in the archive

Subsystems (7)
lore
Share & tools
lore

Peng Tao

author
#0 lore raw

cover vfs: move btrfs clone ioctls to common code

Hi all,

This patchset moves BTRFS_IOC_CLONE/BTRFS_IOC_CLONE_RANGE to common vfs layer and adds NFS42 CLONE support to knfsd.

It is based on top of Trond's linux-next branch (a85240d254) and Anna's latest sys_copy_file_range work (v7).

With this, btrfs/cifs/nfs all handle CLONE/CLONE_RANGE ioctls through the .copy_file_range method instead of each implementing them in the private ioctl handlers.

Cheers,
Tao

Anna Schumaker (2):
nfsd: Pass filehandle to nfs4_preprocess_stateid_op()
NFSD: Implement the CLONE call

Peng Tao (7):
vfs: add COPY_FILE_CLONE_ONLY flag
cifs: add .copy_file_range file operation
nfs42: add .copy_file_range file operation
vfs: pull btrfs clone API to vfs layer
btrfs: remove btrfs_ioctl_clone(_range)
cifs: remove private handler of BTRFS_IOC_CLONE
nfs42: remove private clone ioctl handler

 fs/btrfs/ioctl.c        |  49 ------------------
 fs/cifs/cifsfs.c        |  22 ++++++++
 fs/cifs/cifsfs.h        |   4 +-
 fs/cifs/ioctl.c         | 103 ++++++++++++++++++++-----------------
 fs/ioctl.c              |  40 +++++++++++++++
 fs/nfs/nfs4file.c       | 133 +++++++++---------------------------------------
 fs/nfsd/nfs4proc.c      |  96 +++++++++++++++++++++++++++++++---
 fs/nfsd/nfs4state.c     |   6 +--
 fs/nfsd/nfs4xdr.c       |  21 ++++++++
 fs/nfsd/state.h         |   5 +-
 fs/nfsd/vfs.c           |  12 +++++
 fs/nfsd/vfs.h           |   1 +
 fs/nfsd/xdr4.h          |  10 ++++
 fs/read_write.c         |   9 ++--
 include/linux/fs.h      |   3 ++
 include/linux/nfs4.h    |   4 +-
 include/uapi/linux/fs.h |   9 ++++
 17 files changed, 303 insertions(+), 224 deletions(-)
Signature · 1 line
1.8.3.1

A message that is not in the archive (sent off-list or not yet received): 1445728636-10109-1-git-send-email-tao.peng-7I+n7zu2hftEKMMhf/[email protected]

author
#2 lore raw

PATCH 1/9 vfs: add COPY_FILE_CLONE_ONLY flag

To tell file system not to return partial success in the .copy_file_range method. This is useful to implement the clone (or reflink) functionality.

COPY_FILE_CLONE_ONLY is added only to include/linux/fs.h and thus is not exposed to users. We can replace it with something like Anna's COPY_FR_REFLINK in uapi/fs.h when implementing new user visiable APIs like sys_clone.

---
 fs/read_write.c    | 9 +++++----
 include/linux/fs.h | 3 +++
 2 files changed, 8 insertions(+), 4 deletions(-)
fs/read_write.c +5 -4
index d2da7e4..da11a7f 100644
@@ -1349,8 +1349,9 @@ static ssize_t vfs_copy_fr_copy(struct file *file_in, loff_t pos_in,
 
 /*
  * copy_file_range() differs from regular file read and write in that it
- * specifically allows return partial success.  When it does so is up to
- * the copy_file_range method.
+ * specifically allows returning partial success when COPY_FILE_CLONE_ONLY
+ * is not set in the flags argument, in which case it is up to the file system
+ * to decide whether it wants to do so in the copy_file_range method.
  */
 ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
 			    struct file *file_out, loff_t pos_out,
@@ -1360,7 +1361,7 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
 	struct inode *inode_out = file_inode(file_out);
 	ssize_t ret;
 
-	if (flags != 0)
+	if (flags && flags != COPY_FILE_CLONE_ONLY)
 		return -EINVAL;
 
 	if (!(file_in->f_mode & FMODE_READ) ||
@@ -1385,7 +1386,7 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
 	if (file_out->f_op->copy_file_range)
 		ret = file_out->f_op->copy_file_range(file_in, pos_in, file_out,
 						      pos_out, len, flags);
-	if (ret == -EOPNOTSUPP)
+	if (ret == -EOPNOTSUPP && !(flags & COPY_FILE_CLONE_ONLY))
 		ret = vfs_copy_fr_copy(file_in, pos_in, file_out, pos_out, len);
 
 	if (ret > 0) {
include/linux/fs.h +3 -0
index 6220307..1c430fd 100644
@@ -1610,6 +1610,9 @@ struct block_device_operations;
 
 struct iov_iter;
 
+/* Tell file system not to return partial success in copy_file_range method */
+#define COPY_FILE_CLONE_ONLY		(1 << 0)
+
 struct file_operations {
 	struct module *owner;
 	loff_t (*llseek) (struct file *, loff_t, int);
Signature · 1 line
1.8.3.1

A message that is not in the archive (sent off-list or not yet received): 1445728636-10109-2-git-send-email-tao.peng-7I+n7zu2hftEKMMhf/[email protected]

#4 lore raw

Re: [PATCH 1/9] vfs: add COPY_FILE_CLONE_ONLY flag

Quoted 3 lines · Peng Tao

To tell file system not to return partial success in the .copy_file_range method. This is useful to implement the clone (or reflink) functionality.

The return value is only part of it, the other part is to make it atomic. Thus I don't think overloading copy_file_range is a good idea - we should have a seaprate .clone_file_range that is called by the btrfs ioctls moved to the core, and have vfs_copy_file_range use .clone_file_range if that exists, and if none of the potentially conflicting future flags like COPY_FALLOC are present.

author
#5 lore raw

PATCH 4/9 vfs: pull btrfs clone API to vfs layer

In reply to a message not in the archive

Now that a few file systems are adding clone functionality, namingly btrfs, CIFS, NFS (in another series) and XFS (ttp://oss.sgi.com/archives/xfs/2015-06/msg00407.html), it makes sense to pull the ioctl to common code.

---
 fs/ioctl.c              | 40 ++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/fs.h |  9 +++++++++
 2 files changed, 49 insertions(+)
fs/ioctl.c +40 -0
index 5d01d26..9a78426 100644
@@ -215,6 +215,40 @@ static int ioctl_fiemap(struct file *filp, unsigned long arg)
 	return error;
 }
 
+static long ioctl_file_clone(struct file *dst_file, unsigned long srcfd,
+			     u64 off, u64 olen, u64 destoff)
+{
+	struct fd src_file = fdget(srcfd);
+	struct inode *src_inode;
+	int ret;
+
+	if (!src_file.file)
+		return -EBADF;
+	/* olen == 0 means src off to eof */
+	if (olen == 0) {
+		src_inode = file_inode(src_file.file);
+		olen = i_size_read(src_inode) - off;
+	}
+	ret = vfs_copy_file_range(src_file.file, off, dst_file,
+				  destoff, olen, COPY_FILE_CLONE_ONLY);
+	/* vfs_copy_file_range returns bytes copied */
+	if (ret > 0)
+		ret = 0;
+
+	fdput(src_file);
+	return ret;
+}
+
+static long ioctl_file_clone_range(struct file *file, void __user *argp)
+{
+	struct file_clone_range args;
+
+	if (copy_from_user(&args, argp, sizeof(args)))
+		return -EFAULT;
+	return ioctl_file_clone(file, args.src_fd, args.src_offset,
+				args.src_length, args.dest_offset);
+}
+
 #ifdef CONFIG_BLOCK
 
 static inline sector_t logical_to_blk(struct inode *inode, loff_t offset)
@@ -600,6 +634,12 @@ int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
 	case FIGETBSZ:
 		return put_user(inode->i_sb->s_blocksize, argp);
 
+	case FICLONE:
+		return ioctl_file_clone(filp, arg, 0, 0, 0);
+
+	case FICLONERANGE:
+		return ioctl_file_clone_range(filp, argp);
+
 	default:
 		if (S_ISREG(inode->i_mode))
 			error = file_ioctl(filp, cmd, arg);
include/uapi/linux/fs.h +9 -0
index 9b964a5..ac7f1c5 100644
@@ -39,6 +39,13 @@
 #define RENAME_EXCHANGE		(1 << 1)	/* Exchange source and dest */
 #define RENAME_WHITEOUT		(1 << 2)	/* Whiteout source */
 
+struct file_clone_range {
+	__s64 src_fd;
+	__u64 src_offset;
+	__u64 src_length;
+	__u64 dest_offset;
+};
+
 struct fstrim_range {
 	__u64 start;
 	__u64 len;
@@ -159,6 +166,8 @@ struct inodes_stat_t {
 #define FIFREEZE	_IOWR('X', 119, int)	/* Freeze */
 #define FITHAW		_IOWR('X', 120, int)	/* Thaw */
 #define FITRIM		_IOWR('X', 121, struct fstrim_range)	/* Trim */
+#define FICLONE		_IOW(0x94, 9, int)	/* Clone */
+#define FICLONERANGE	_IOW(0x94, 13, struct file_clone_range)	/* Clone range */
 
 #define	FS_IOC_GETFLAGS			_IOR('f', 1, long)
 #define	FS_IOC_SETFLAGS			_IOW('f', 2, long)
Signature · 1 line
1.8.3.1
#6 lore raw

PATCH 2/9 cifs: add .copy_file_range file operation

In reply to Peng Tao
---
 fs/cifs/cifsfs.c |  22 ++++++++++++
 fs/cifs/cifsfs.h |   4 ++-
 fs/cifs/ioctl.c  | 100 +++++++++++++++++++++++++++++++------------------------
 3 files changed, 82 insertions(+), 44 deletions(-)
fs/cifs/cifsfs.c +22 -0
index e739950..6ef7c3c 100644
@@ -910,6 +910,22 @@ const struct inode_operations cifs_symlink_inode_ops = {
 #endif
 };
 
+#ifdef CONFIG_CIFS_POSIX
+ssize_t cifs_file_copy_range(struct file *file_in, loff_t pos_in,
+			     struct file *file_out, loff_t pos_out,
+			     size_t len, unsigned int flags)
+{
+	unsigned int xid;
+	int rc;
+
+	xid = get_xid();
+	rc = cifs_file_clone_range(xid, file_in, file_out, pos_in,
+				   len, pos_out, true);
+	free_xid(xid);
+	return rc < 0 ? rc : len;
+}
+#endif
+
 const struct file_operations cifs_file_ops = {
 	.read_iter = cifs_loose_read_iter,
 	.write_iter = cifs_file_write_iter,
@@ -923,6 +939,7 @@ const struct file_operations cifs_file_ops = {
 	.llseek = cifs_llseek,
 #ifdef CONFIG_CIFS_POSIX
 	.unlocked_ioctl	= cifs_ioctl,
+	.copy_file_range = cifs_file_copy_range,
 #endif /* CONFIG_CIFS_POSIX */
 	.setlease = cifs_setlease,
 	.fallocate = cifs_fallocate,
@@ -941,6 +958,7 @@ const struct file_operations cifs_file_strict_ops = {
 	.llseek = cifs_llseek,
 #ifdef CONFIG_CIFS_POSIX
 	.unlocked_ioctl	= cifs_ioctl,
+	.copy_file_range = cifs_file_copy_range,
 #endif /* CONFIG_CIFS_POSIX */
 	.setlease = cifs_setlease,
 	.fallocate = cifs_fallocate,
@@ -959,6 +977,7 @@ const struct file_operations cifs_file_direct_ops = {
 	.splice_read = generic_file_splice_read,
 #ifdef CONFIG_CIFS_POSIX
 	.unlocked_ioctl  = cifs_ioctl,
+	.copy_file_range = cifs_file_copy_range,
 #endif /* CONFIG_CIFS_POSIX */
 	.llseek = cifs_llseek,
 	.setlease = cifs_setlease,
@@ -977,6 +996,7 @@ const struct file_operations cifs_file_nobrl_ops = {
 	.llseek = cifs_llseek,
 #ifdef CONFIG_CIFS_POSIX
 	.unlocked_ioctl	= cifs_ioctl,
+	.copy_file_range = cifs_file_copy_range,
 #endif /* CONFIG_CIFS_POSIX */
 	.setlease = cifs_setlease,
 	.fallocate = cifs_fallocate,
@@ -994,6 +1014,7 @@ const struct file_operations cifs_file_strict_nobrl_ops = {
 	.llseek = cifs_llseek,
 #ifdef CONFIG_CIFS_POSIX
 	.unlocked_ioctl	= cifs_ioctl,
+	.copy_file_range = cifs_file_copy_range,
 #endif /* CONFIG_CIFS_POSIX */
 	.setlease = cifs_setlease,
 	.fallocate = cifs_fallocate,
@@ -1011,6 +1032,7 @@ const struct file_operations cifs_file_direct_nobrl_ops = {
 	.splice_read = generic_file_splice_read,
 #ifdef CONFIG_CIFS_POSIX
 	.unlocked_ioctl  = cifs_ioctl,
+	.copy_file_range = cifs_file_copy_range,
 #endif /* CONFIG_CIFS_POSIX */
 	.llseek = cifs_llseek,
 	.setlease = cifs_setlease,
fs/cifs/cifsfs.h +3 -1
index c3cc160..797439b 100644
@@ -131,7 +131,9 @@ extern int	cifs_setxattr(struct dentry *, const char *, const void *,
 extern ssize_t	cifs_getxattr(struct dentry *, const char *, void *, size_t);
 extern ssize_t	cifs_listxattr(struct dentry *, char *, size_t);
 extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
-
+extern int cifs_file_clone_range(unsigned int xid, struct file *src_file,
+				 struct file *dst_file, u64 off, u64 len,
+				 u64 destoff, bool dup_extents);
 #ifdef CONFIG_CIFS_NFSD_EXPORT
 extern const struct export_operations cifs_export_ops;
 #endif /* CONFIG_CIFS_NFSD_EXPORT */
fs/cifs/ioctl.c +57 -43

131 lines not on this page · raw message

Signature · 1 line
1.8.3.1
#7 lore raw

Re: [PATCH 2/9] cifs: add .copy_file_range file operation

Patch looks fine, but it points out a Kconfig problem with cifs.ko. Need to remove the

         #ifdef CONFIG_CIFS_POSIX

around the statements like:

.unlocked_ioctl = cifs_ioctl,
.copy_file_range = cifs_file_copy_range,

Copy offload does not require support for the old CIFS POSIX extensions (or even cifs at all) - it works fine over SMB3. Alternatively you can move the copy_file_range to the end of the struct file_operations in your patch, and I can remove the CONFIG_CIFS_POSIX around cifs_ioctl in a different patch that I put in my tree - whichever you prefer.

Originally the first ioctl that cifs supported did require the CIFS POSIX extensions because it used an info level defined in the CIFS UNIX/POSIX extensions. With later additions to cifs_ioctl that restriction is now obsolete. Later ioctls have been added that work over SMB3, and do not require the CIFS POSIX extensions (such as copy
offload). The patch that originally introduced the CIFS_POSIX ifdef
around this ioctl was

commit c67593a03129967eae8939c4899767182eb6d6cd

Author: Steve French <[email protected]>
Date:   Thu Apr 28 22:41:04 2005 -0700

[PATCH] cifs: Enable ioctl support in POSIX extensions to handle lsattr

but obviously now we don't need the CONFIG_CIFS_POSIX ifdef around the ioctl functions.

Quoted 75 lines · Peng Tao
---
 fs/cifs/cifsfs.c |  22 ++++++++++++
 fs/cifs/cifsfs.h |   4 ++-
 fs/cifs/ioctl.c  | 100 +++++++++++++++++++++++++++++++------------------------
 3 files changed, 82 insertions(+), 44 deletions(-)
fs/cifs/cifsfs.c +21 -0
index e739950..6ef7c3c 100644
@@ -910,6 +910,22 @@ const struct inode_operations cifs_symlink_inode_ops = {
 #endif
 };
 
+#ifdef CONFIG_CIFS_POSIX
+ssize_t cifs_file_copy_range(struct file *file_in, loff_t pos_in,
+                            struct file *file_out, loff_t pos_out,
+                            size_t len, unsigned int flags)
+{
+       unsigned int xid;
+       int rc;
+
+       xid = get_xid();
+       rc = cifs_file_clone_range(xid, file_in, file_out, pos_in,
+                                  len, pos_out, true);
+       free_xid(xid);
+       return rc < 0 ? rc : len;
+}
+#endif
+
 const struct file_operations cifs_file_ops = {
        .read_iter = cifs_loose_read_iter,
        .write_iter = cifs_file_write_iter,
@@ -923,6 +939,7 @@ const struct file_operations cifs_file_ops = {
        .llseek = cifs_llseek,
 #ifdef CONFIG_CIFS_POSIX
        .unlocked_ioctl = cifs_ioctl,
+       .copy_file_range = cifs_file_copy_range,
 #endif /* CONFIG_CIFS_POSIX */
        .setlease = cifs_setlease,
        .fallocate = cifs_fallocate,
@@ -941,6 +958,7 @@ const struct file_operations cifs_file_strict_ops = {
        .llseek = cifs_llseek,
 #ifdef CONFIG_CIFS_POSIX
        .unlocked_ioctl = cifs_ioctl,
+       .copy_file_range = cifs_file_copy_range,
 #endif /* CONFIG_CIFS_POSIX */
        .setlease = cifs_setlease,
        .fallocate = cifs_fallocate,
@@ -959,6 +977,7 @@ const struct file_operations cifs_file_direct_ops = {
        .splice_read = generic_file_splice_read,
 #ifdef CONFIG_CIFS_POSIX
        .unlocked_ioctl  = cifs_ioctl,
+       .copy_file_range = cifs_file_copy_range,
 #endif /* CONFIG_CIFS_POSIX */
        .llseek = cifs_llseek,
        .setlease = cifs_setlease,
@@ -977,6 +996,7 @@ const struct file_operations cifs_file_nobrl_ops = {
        .llseek = cifs_llseek,
 #ifdef CONFIG_CIFS_POSIX
        .unlocked_ioctl = cifs_ioctl,
+       .copy_file_range = cifs_file_copy_range,
 #endif /* CONFIG_CIFS_POSIX */
        .setlease = cifs_setlease,
        .fallocate = cifs_fallocate,
@@ -994,6 +1014,7 @@ const struct file_operations cifs_file_strict_nobrl_ops = {
        .llseek = cifs_llseek,
 #ifdef CONFIG_CIFS_POSIX
        .unlocked_ioctl = cifs_ioctl,
+       .copy_file_range = cifs_file_copy_range,
 #endif /* CONFIG_CIFS_POSIX */
        .setlease = cifs_setlease,
        .fallocate = cifs_fallocate,

fs/cifs/cifsfs.c const struct file_operations cifs_file_direct_nobrl_ops = { · line 1038

Quoted 7 lines
fs/cifs/cifsfs.c +1 -0
@@ -1011,6 +1032,7 @@ const struct file_operations cifs_file_direct_nobrl_ops = {
        .splice_read = generic_file_splice_read,
 #ifdef CONFIG_CIFS_POSIX
        .unlocked_ioctl  = cifs_ioctl,
+       .copy_file_range = cifs_file_copy_range,
 #endif /* CONFIG_CIFS_POSIX */
        .llseek = cifs_llseek,
        .setlease = cifs_setlease,

Patch looks fine, but it points out a Kconfig problem with cifs.ko. Need to remove the

#ifdef CONFIG_CIFS_POSIX

around the

.unlocked_ioctl = cifs_ioctl,
.copy_file_range = cifs_file_copy_range,

statements. Alternatively you can move the copy_file_range to the end of the struct file_operations in your patch, and I can remove the CONFIG_CIFS_POSIX around cifs_ioctl in a different patch that I put in my tree - whichever you prefer.

Originally the first ioctl that cifs supported did require the CIFS POSIX extensions because it used an info level defined in the CIFS UNIX/POSIX extensions. With later additions to cifs_ioctl that restriction is now obsolete. Later ioctls have been added that work over SMB3, and do not require the CIFS POSIX extensions (such as copy offload). See patch

Signature · 3 lines
Thanks,

Steve
#8 lore raw

PATCH 3/9 nfs42: add .copy_file_range file operation

In reply to Peng Tao
---
 fs/nfs/nfs4file.c | 95 ++++++++++++++++++++++++++++++-------------------------
 1 file changed, 52 insertions(+), 43 deletions(-)
fs/nfs/nfs4file.c +52 -43
index 4aa5719..f1924d8 100644
@@ -194,15 +194,60 @@ static long nfs42_fallocate(struct file *filep, int mode, loff_t offset, loff_t
 	return nfs42_proc_allocate(filep, offset, len);
 }
 
+static ssize_t nfs42_copy_file_range(struct file *file_in, loff_t pos_in,
+				     struct file *file_out, loff_t pos_out,
+				     size_t len, unsigned int flags)
+{
+	struct inode *src_inode = file_inode(file_in);
+	struct inode *dst_inode = file_inode(file_out);
+	struct nfs_server *server = NFS_SERVER(src_inode);
+	unsigned int bs = server->clone_blksize;
+	int ret;
+
+	/* check alignment w.r.t. clone_blksize */
+	ret = -EINVAL;
+	if (bs) {
+		if (!IS_ALIGNED(pos_in, bs) || !IS_ALIGNED(pos_out, bs) ||
+		    (!IS_ALIGNED(len, bs) &&
+		     i_size_read(src_inode) != (pos_in + len)))
+			goto out;
+	}
+
+	/* XXX: do we lock at all? what if server needs CB_RECALL_LAYOUT? */
+	lock_two_nondirectories(src_inode, dst_inode);
+
+	/* flush all pending writes on both src and dst so that server
+	 * has the latest data
+	 */
+	ret = nfs_sync_inode(src_inode);
+	if (ret)
+		goto out_unlock;
+	ret = nfs_sync_inode(dst_inode);
+	if (ret)
+		goto out_unlock;
+
+	ret = nfs42_proc_clone(file_in, file_out, pos_in, pos_out, len);
+
+	/* truncate inode page cache of the dst range so that future
+	 * reads can fetch new data from server
+	 */
+	if (!ret)
+		truncate_inode_pages_range(&dst_inode->i_data, pos_out,
+					   pos_out + len - 1);
+
+out_unlock:
+	unlock_two_nondirectories(src_inode, dst_inode);
+out:
+	return ret < 0 ? ret : len;
+}
+
 static noinline long
 nfs42_ioctl_clone(struct file *dst_file, unsigned long srcfd,
 		  u64 src_off, u64 dst_off, u64 count)
 {
 	struct inode *dst_inode = file_inode(dst_file);
-	struct nfs_server *server = NFS_SERVER(dst_inode);
 	struct fd src_file;
 	struct inode *src_inode;
-	unsigned int bs = server->clone_blksize;
 	int ret;
 
 	/* dst file must be opened for writing */
@@ -240,48 +285,11 @@ nfs42_ioctl_clone(struct file *dst_file, unsigned long srcfd,
 	    src_inode->i_sb != dst_inode->i_sb)
 		goto out_fput;
 
-	/* check alignment w.r.t. clone_blksize */
-	ret = -EINVAL;
-	if (bs) {
-		if (!IS_ALIGNED(src_off, bs) || !IS_ALIGNED(dst_off, bs))
-			goto out_fput;
-		if (!IS_ALIGNED(count, bs) && i_size_read(src_inode) != (src_off + count))
-			goto out_fput;
-	}
-
-	/* XXX: do we lock at all? what if server needs CB_RECALL_LAYOUT? */
-	if (dst_inode < src_inode) {
-		mutex_lock_nested(&dst_inode->i_mutex, I_MUTEX_PARENT);
-		mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_CHILD);
-	} else {
-		mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_PARENT);
-		mutex_lock_nested(&dst_inode->i_mutex, I_MUTEX_CHILD);
-	}
-
-	/* flush all pending writes on both src and dst so that server
-	 * has the latest data */
-	ret = nfs_sync_inode(src_inode);
-	if (ret)
-		goto out_unlock;
-	ret = nfs_sync_inode(dst_inode);
-	if (ret)
-		goto out_unlock;
-
-	ret = nfs42_proc_clone(src_file.file, dst_file, src_off, dst_off, count);
-
-	/* truncate inode page cache of the dst range so that future reads can fetch
-	 * new data from server */
-	if (!ret)
-		truncate_inode_pages_range(&dst_inode->i_data, dst_off, dst_off + count - 1);
+	ret = nfs42_copy_file_range(src_file.file, src_off, dst_file,
+				    dst_off, count, 0);
+	if (ret > 0)
+		ret = 0;
 
-out_unlock:
-	if (dst_inode < src_inode) {
-		mutex_unlock(&src_inode->i_mutex);
-		mutex_unlock(&dst_inode->i_mutex);
-	} else {
-		mutex_unlock(&dst_inode->i_mutex);
-		mutex_unlock(&src_inode->i_mutex);
-	}
 out_fput:
 	fdput(src_file);
 out_drop_write:
@@ -344,6 +352,7 @@ const struct file_operations nfs4_file_operations = {
 	.splice_write	= iter_file_splice_write,
 #ifdef CONFIG_NFS_V4_2
 	.fallocate	= nfs42_fallocate,
+	.copy_file_range = nfs42_copy_file_range,
 #endif /* CONFIG_NFS_V4_2 */
 	.check_flags	= nfs_check_flags,
 	.setlease	= simple_nosetlease,
Signature · 1 line
1.8.3.1
#9 lore raw

PATCH 5/9 btrfs: remove btrfs_ioctl_clone(_range)

In reply to Peng Tao

BTRFS_IOC_CLONE/BTRFS_IOC_CLONE_RANGE is now handled by generic layer and goes through the .copy_file_range method.

---
 fs/btrfs/ioctl.c | 49 -------------------------------------------------
 1 file changed, 49 deletions(-)
fs/btrfs/ioctl.c +0 -49
index d3697e8..b568e24 100644
@@ -3854,51 +3854,6 @@ ssize_t btrfs_copy_file_range(struct file *file_in, loff_t pos_in,
 	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:
-	mnt_drop_write_file(file);
-	return ret;
-}
-
-static long btrfs_ioctl_clone_range(struct file *file, void __user *argp)
-{
-	struct btrfs_ioctl_clone_range_args args;
-
-	if (copy_from_user(&args, argp, sizeof(args)))
-		return -EFAULT;
-	return btrfs_ioctl_clone(file, args.src_fd, args.src_offset,
-				 args.src_length, args.dest_offset);
-}
-
 /*
  * there are many ways the trans_start and trans_end ioctls can lead
  * to deadlocks.  They should only be used by applications that
@@ -5438,10 +5393,6 @@ long btrfs_ioctl(struct file *file, unsigned int
 		return btrfs_ioctl_dev_info(root, argp);
 	case BTRFS_IOC_BALANCE:
 		return btrfs_ioctl_balance(file, NULL);
-	case BTRFS_IOC_CLONE:
-		return btrfs_ioctl_clone(file, arg, 0, 0, 0);
-	case BTRFS_IOC_CLONE_RANGE:
-		return btrfs_ioctl_clone_range(file, argp);
 	case BTRFS_IOC_TRANS_START:
 		return btrfs_ioctl_trans_start(file);
 	case BTRFS_IOC_TRANS_END:
Signature · 1 line
1.8.3.1
#10 lore raw

PATCH 6/9 cifs: remove private handler of BTRFS_IOC_CLONE

In reply to Peng Tao

BTRFS_IOC_CLONE/BTRFS_IOC_CLONE_RANGE is now handled by generic layer and goes through the .copy_file_range method.

---
 fs/cifs/ioctl.c | 3 ---
 1 file changed, 3 deletions(-)
fs/cifs/ioctl.c +0 -3
index bbab940..5dfc63a 100644
@@ -267,9 +267,6 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
 		case CIFS_IOC_COPYCHUNK_FILE:
 			rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0, false);
 			break;
-		case BTRFS_IOC_CLONE:
-			rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0, true);
-			break;
 		case CIFS_IOC_SET_INTEGRITY:
 			if (pSMBFile == NULL)
 				break;
Signature · 1 line
1.8.3.1
#11 lore raw

PATCH 7/9 nfs42: remove private clone ioctl handler

In reply to Peng Tao

BTRFS_IOC_CLONE/BTRFS_IOC_CLONE_RANGE is now handled by generic layer and goes through the .copy_file_range method.

---
 fs/nfs/nfs4file.c | 96 -------------------------------------------------------
 1 file changed, 96 deletions(-)
fs/nfs/nfs4file.c +0 -96
index f1924d8..181b67b 100644
@@ -240,99 +240,8 @@ out_unlock:
 out:
 	return ret < 0 ? ret : len;
 }
-
-static noinline long
-nfs42_ioctl_clone(struct file *dst_file, unsigned long srcfd,
-		  u64 src_off, u64 dst_off, u64 count)
-{
-	struct inode *dst_inode = file_inode(dst_file);
-	struct fd src_file;
-	struct inode *src_inode;
-	int ret;
-
-	/* dst file must be opened for writing */
-	if (!(dst_file->f_mode & FMODE_WRITE))
-		return -EINVAL;
-
-	ret = mnt_want_write_file(dst_file);
-	if (ret)
-		return ret;
-
-	src_file = fdget(srcfd);
-	if (!src_file.file) {
-		ret = -EBADF;
-		goto out_drop_write;
-	}
-
-	src_inode = file_inode(src_file.file);
-
-	/* src and dst must be different files */
-	ret = -EINVAL;
-	if (src_inode == dst_inode)
-		goto out_fput;
-
-	/* src file must be opened for reading */
-	if (!(src_file.file->f_mode & FMODE_READ))
-		goto out_fput;
-
-	/* src and dst must be regular files */
-	ret = -EISDIR;
-	if (!S_ISREG(src_inode->i_mode) || !S_ISREG(dst_inode->i_mode))
-		goto out_fput;
-
-	ret = -EXDEV;
-	if (src_file.file->f_path.mnt != dst_file->f_path.mnt ||
-	    src_inode->i_sb != dst_inode->i_sb)
-		goto out_fput;
-
-	ret = nfs42_copy_file_range(src_file.file, src_off, dst_file,
-				    dst_off, count, 0);
-	if (ret > 0)
-		ret = 0;
-
-out_fput:
-	fdput(src_file);
-out_drop_write:
-	mnt_drop_write_file(dst_file);
-	return ret;
-}
-
-static long nfs42_ioctl_clone_range(struct file *dst_file, void __user *argp)
-{
-	struct nfs_ioctl_clone_range_args args;
-
-	if (copy_from_user(&args, argp, sizeof(args)))
-		return -EFAULT;
-
-	return nfs42_ioctl_clone(dst_file, args.src_fd, args.src_off, args.dst_off, args.count);
-}
-#else
-static long nfs42_ioctl_clone(struct file *dst_file, unsigned long srcfd,
-		u64 src_off, u64 dst_off, u64 count)
-{
-	return -ENOTTY;
-}
-
-static long nfs42_ioctl_clone_range(struct file *dst_file, void __user *argp)
-{
-	return -ENOTTY;
-}
 #endif /* CONFIG_NFS_V4_2 */
 
-long nfs4_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
-	void __user *argp = (void __user *)arg;
-
-	switch (cmd) {
-	case NFS_IOC_CLONE:
-		return nfs42_ioctl_clone(file, arg, 0, 0, 0);
-	case NFS_IOC_CLONE_RANGE:
-		return nfs42_ioctl_clone_range(file, argp);
-	}
-
-	return -ENOTTY;
-}
-
 const struct file_operations nfs4_file_operations = {
 #ifdef CONFIG_NFS_V4_2
 	.llseek		= nfs4_file_llseek,
@@ -356,9 +265,4 @@ const struct file_operations nfs4_file_operations = {
 #endif /* CONFIG_NFS_V4_2 */
 	.check_flags	= nfs_check_flags,
 	.setlease	= simple_nosetlease,
-#ifdef CONFIG_COMPAT
-	.unlocked_ioctl = nfs4_ioctl,
-#else
-	.compat_ioctl	= nfs4_ioctl,
-#endif /* CONFIG_COMPAT */
 };
Signature · 1 line
1.8.3.1
#12 lore raw

PATCH 8/9 nfsd: Pass filehandle to nfs4_preprocess_stateid_op()

In reply to Peng Tao

From: Anna Schumaker <[email protected]>

This will be needed so COPY can look up the saved_fh in addition to the current_fh.

---
 fs/nfsd/nfs4proc.c  | 16 +++++++++-------
 fs/nfsd/nfs4state.c |  6 +++---
 fs/nfsd/state.h     |  5 +++--
 3 files changed, 15 insertions(+), 12 deletions(-)
fs/nfsd/nfs4proc.c +9 -7
index 4ce6b97..eab6d6d 100644
@@ -774,8 +774,9 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 		clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
 
 	/* check stateid */
-	status = nfs4_preprocess_stateid_op(rqstp, cstate, &read->rd_stateid,
-			RD_STATE, &read->rd_filp, &read->rd_tmp_file);
+	status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
+					&read->rd_stateid, RD_STATE,
+					&read->rd_filp, &read->rd_tmp_file);
 	if (status) {
 		dprintk("NFSD: nfsd4_read: couldn't process stateid!\n");
 		goto out;
@@ -921,7 +922,8 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 
 	if (setattr->sa_iattr.ia_valid & ATTR_SIZE) {
 		status = nfs4_preprocess_stateid_op(rqstp, cstate,
-			&setattr->sa_stateid, WR_STATE, NULL, NULL);
+				&cstate->current_fh, &setattr->sa_stateid,
+				WR_STATE, NULL, NULL);
 		if (status) {
 			dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n");
 			return status;
@@ -985,8 +987,8 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 	if (write->wr_offset >= OFFSET_MAX)
 		return nfserr_inval;
 
-	status = nfs4_preprocess_stateid_op(rqstp, cstate, stateid, WR_STATE,
-			&filp, NULL);
+	status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
+						stateid, WR_STATE, &filp, NULL);
 	if (status) {
 		dprintk("NFSD: nfsd4_write: couldn't process stateid!\n");
 		return status;
@@ -1016,7 +1018,7 @@ nfsd4_fallocate(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 	__be32 status = nfserr_notsupp;
 	struct file *file;
 
-	status = nfs4_preprocess_stateid_op(rqstp, cstate,
+	status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
 					    &fallocate->falloc_stateid,
 					    WR_STATE, &file, NULL);
 	if (status != nfs_ok) {
@@ -1055,7 +1057,7 @@ nfsd4_seek(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 	__be32 status;
 	struct file *file;
 
-	status = nfs4_preprocess_stateid_op(rqstp, cstate,
+	status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
 					    &seek->seek_stateid,
 					    RD_STATE, &file, NULL);
 	if (status) {
fs/nfsd/nfs4state.c +3 -3
index 0f1d569..8f4d925 100644
@@ -4666,10 +4666,10 @@ nfs4_check_file(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfs4_stid *s,
  */
 __be32
 nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
-		struct nfsd4_compound_state *cstate, stateid_t *stateid,
-		int flags, struct file **filpp, bool *tmp_file)
+		struct nfsd4_compound_state *cstate, struct svc_fh *fhp,
+		stateid_t *stateid, int flags, struct file **filpp,
+		bool *tmp_file)
 {
-	struct svc_fh *fhp = &cstate->current_fh;
 	struct inode *ino = d_inode(fhp->fh_dentry);
 	struct net *net = SVC_NET(rqstp);
 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
fs/nfsd/state.h +3 -2
index 583ffc1..640f57f 100644
@@ -584,8 +584,9 @@ struct nfsd4_compound_state;
 struct nfsd_net;
 
 extern __be32 nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
-		struct nfsd4_compound_state *cstate, stateid_t *stateid,
-		int flags, struct file **filp, bool *tmp_file);
+		struct nfsd4_compound_state *cstate, struct svc_fh *fhp,
+		stateid_t *stateid, int flags, struct file **filp,
+		bool *tmp_file);
 __be32 nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
 		     stateid_t *stateid, unsigned char typemask,
 		     struct nfs4_stid **s, struct nfsd_net *nn);
Signature · 1 line
1.8.3.1
#13 lore raw

PATCH 9/9 NFSD: Implement the CLONE call

In reply to Peng Tao

From: Anna Schumaker <[email protected]>

I can simply call vfs_file_clone_range() and have the vfs do the right thing for the filesystem being exported.

[hch: change to implement the CLONE op instead of COPY]
---
 fs/nfsd/nfs4proc.c   | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/nfsd/nfs4xdr.c    | 21 ++++++++++++++
 fs/nfsd/vfs.c        | 18 ++++++++++++
 fs/nfsd/vfs.h        |  1 +
 fs/nfsd/xdr4.h       | 10 +++++++
 include/linux/nfs4.h |  4 +--
 6 files changed, 132 insertions(+), 2 deletions(-)
fs/nfsd/nfs4proc.c +80 -0
index eab6d6d..d1c5a87 100644
@@ -1012,6 +1012,80 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 }
 
 static __be32
+nfsd4_verify_clone(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+		   struct nfsd4_clone *clone, struct file **src,
+		   struct file **dst)
+{
+	struct inode *src_ino, *dst_ino;
+	__be32 status;
+
+	status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->save_fh,
+					    &clone->cl_src_stateid, RD_STATE,
+					    src, NULL);
+	if (status) {
+		dprintk("NFSD: %s: couldn't process src stateid!\n", __func__);
+		return status;
+	}
+
+	status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
+					    &clone->cl_dst_stateid, WR_STATE,
+					    dst, NULL);
+	if (status) {
+		dprintk("NFSD: %s: couldn't process dst stateid!\n", __func__);
+		fput(*src);
+	}
+
+	/* a few extra check to make sure we send back proper errors per RFC */
+	src_ino = file_inode(*src);
+	dst_ino = file_inode(*dst);
+
+	if (S_ISDIR(src_ino->i_mode) || S_ISDIR(dst_ino->i_mode)) {
+		status = nfserr_wrong_type;
+		goto out_fput;
+	}
+
+	if (src_ino == dst_ino) {
+		status = nfserr_inval;
+		goto out_fput;
+	}
+
+	if (!(*src)->f_op || !(*src)->f_op->copy_file_range) {
+		status = nfserr_notsupp;
+		goto out_fput;
+	}
+out:
+	return status;
+out_fput:
+	fput(*src);
+	fput(*dst);
+	goto out;
+}
+
+static __be32
+nfsd4_clone(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+		struct nfsd4_clone *clone)
+{
+	int ret;
+	__be32 status;
+	struct file *src = NULL, *dst = NULL;
+
+	status = nfsd4_verify_clone(rqstp, cstate, clone, &src, &dst);
+	if (status)
+		return status;
+
+	ret = nfsd4_clone_range(src, dst, clone->cl_src_pos,
+				clone->cl_count, clone->cl_dst_pos);
+	if (ret < 0)
+		status = nfserrno(ret);
+	else
+		status = nfs_ok;
+
+	fput(src);
+	fput(dst);
+	return status;
+}
+
+static __be32
 nfsd4_fallocate(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 		struct nfsd4_fallocate *fallocate, int flags)
 {
@@ -2277,6 +2351,12 @@ static struct nfsd4_operation nfsd4_ops[] = {
 		.op_name = "OP_DEALLOCATE",
 		.op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
 	},
+	[OP_CLONE] = {
+		.op_func = (nfsd4op_func)nfsd4_clone,
+		.op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME,
+		.op_name = "OP_CLONE",
+		.op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
+	},
 	[OP_SEEK] = {
 		.op_func = (nfsd4op_func)nfsd4_seek,
 		.op_name = "OP_SEEK",
fs/nfsd/nfs4xdr.c +21 -0
index 51c9e9c..924416f 100644
@@ -1675,6 +1675,25 @@ nfsd4_decode_fallocate(struct nfsd4_compoundargs *argp,
 }
 
 static __be32
+nfsd4_decode_clone(struct nfsd4_compoundargs *argp, struct nfsd4_clone *clone)
+{
+	DECODE_HEAD;
+
+	status = nfsd4_decode_stateid(argp, &clone->cl_src_stateid);
+	if (status)
+		return status;
+	status = nfsd4_decode_stateid(argp, &clone->cl_dst_stateid);
+	if (status)
+		return status;
+
+	READ_BUF(8 + 8 + 8);
+	p = xdr_decode_hyper(p, &clone->cl_src_pos);
+	p = xdr_decode_hyper(p, &clone->cl_dst_pos);
+	p = xdr_decode_hyper(p, &clone->cl_count);
+	DECODE_TAIL;
+}
+
+static __be32
 nfsd4_decode_seek(struct nfsd4_compoundargs *argp, struct nfsd4_seek *seek)
 {
 	DECODE_HEAD;
@@ -1785,6 +1804,7 @@ static nfsd4_dec nfsd4_dec_ops[] = {
 	[OP_READ_PLUS]		= (nfsd4_dec)nfsd4_decode_notsupp,
 	[OP_SEEK]		= (nfsd4_dec)nfsd4_decode_seek,
 	[OP_WRITE_SAME]		= (nfsd4_dec)nfsd4_decode_notsupp,
+	[OP_CLONE]		= (nfsd4_dec)nfsd4_decode_clone,
 };
 
 static inline bool
@@ -4292,6 +4312,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
 	[OP_READ_PLUS]		= (nfsd4_enc)nfsd4_encode_noop,
 	[OP_SEEK]		= (nfsd4_enc)nfsd4_encode_seek,
 	[OP_WRITE_SAME]		= (nfsd4_enc)nfsd4_encode_noop,
+	[OP_CLONE]		= (nfsd4_enc)nfsd4_encode_noop,
 };
 
 /*
fs/nfsd/vfs.c +18 -0
index 45c0497..9ae4992 100644
@@ -36,6 +36,7 @@
 #endif /* CONFIG_NFSD_V3 */
 
 #ifdef CONFIG_NFSD_V4
+#include "../internal.h"
 #include "acl.h"
 #include "idmap.h"
 #endif /* CONFIG_NFSD_V4 */
@@ -498,6 +499,23 @@ __be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp,
 }
 #endif
 
+int nfsd4_clone_range(struct file *src, struct file *dst, u64 src_pos,
+		      u64 count, u64 dst_pos)
+{
+	struct inode *src_ino = file_inode(src);
+	int rc;
+
+	/* vfs_copy_file_range thinks count == 0 is nothing to clone,
+	 * while in NFS it means from src_pos to eof
+	 */
+	if (count == 0)
+		count = i_size_read(src_ino) - src_pos;
+	rc = vfs_copy_file_range(src, src_pos, dst, dst_pos,
+				 count, COPY_FILE_CLONE_ONLY);
+
+	return rc < 0 ? rc : 0;
+}
+
 __be32 nfsd4_vfs_fallocate(struct svc_rqst *rqstp, struct svc_fh *fhp,
 			   struct file *file, loff_t offset, loff_t len,
 			   int flags)
fs/nfsd/vfs.h +1 -0
index fee2451..604830c 100644
@@ -56,6 +56,7 @@ __be32          nfsd4_set_nfs4_label(struct svc_rqst *, struct svc_fh *,
 		    struct xdr_netobj *);
 __be32		nfsd4_vfs_fallocate(struct svc_rqst *, struct svc_fh *,
 				    struct file *, loff_t, loff_t, int);
+int		nfsd4_clone_range(struct file *, struct file *, u64, u64, u64);
 #endif /* CONFIG_NFSD_V4 */
 __be32		nfsd_create(struct svc_rqst *, struct svc_fh *,
 				char *name, int len, struct iattr *attrs,
fs/nfsd/xdr4.h +10 -0
index 9f99100..3d70712 100644
@@ -491,6 +491,15 @@ struct nfsd4_fallocate {
 	u64		falloc_length;
 };
 
+struct nfsd4_clone {
+	/* request */
+	stateid_t	cl_src_stateid;
+	stateid_t	cl_dst_stateid;
+	u64		cl_src_pos;
+	u64		cl_dst_pos;
+	u64		cl_count;
+};
+
 struct nfsd4_seek {
 	/* request */
 	stateid_t	seek_stateid;
@@ -555,6 +564,7 @@ struct nfsd4_op {
 		/* NFSv4.2 */
 		struct nfsd4_fallocate		allocate;
 		struct nfsd4_fallocate		deallocate;
+		struct nfsd4_clone		clone;
 		struct nfsd4_seek		seek;
 	} u;
 	struct nfs4_replay *			replay;
include/linux/nfs4.h +2 -2
index e7e7853..43aeabd 100644
@@ -139,10 +139,10 @@ enum nfs_opnum4 {
 Needs to be updated if more operations are defined in future.*/
 
 #define FIRST_NFS4_OP	OP_ACCESS
-#define LAST_NFS4_OP 	OP_WRITE_SAME
 #define LAST_NFS40_OP	OP_RELEASE_LOCKOWNER
 #define LAST_NFS41_OP	OP_RECLAIM_COMPLETE
-#define LAST_NFS42_OP	OP_WRITE_SAME
+#define LAST_NFS42_OP	OP_CLONE
+#define LAST_NFS4_OP	LAST_NFS42_OP
 
 enum nfsstat4 {
 	NFS4_OK = 0,
Signature · 1 line
1.8.3.1