2015-10-24 15:28:46

by Peng Tao

[permalink] [raw]
Subject: [PATCH 0/9] 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(-)

--
1.8.3.1



2015-10-24 15:29:29

by Peng Tao

[permalink] [raw]
Subject: [PATCH 6/9] cifs: remove private handler of BTRFS_IOC_CLONE

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

Signed-off-by: Peng Tao <[email protected]>
---
fs/cifs/ioctl.c | 3 ---
1 file changed, 3 deletions(-)

diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
index bbab940..5dfc63a 100644
--- a/fs/cifs/ioctl.c
+++ b/fs/cifs/ioctl.c
@@ -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;
--
1.8.3.1


2015-10-24 15:29:36

by Peng Tao

[permalink] [raw]
Subject: [PATCH 7/9] nfs42: remove private clone ioctl handler

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

Signed-off-by: Peng Tao <[email protected]>
---
fs/nfs/nfs4file.c | 96 -------------------------------------------------------
1 file changed, 96 deletions(-)

diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
index f1924d8..181b67b 100644
--- a/fs/nfs/nfs4file.c
+++ b/fs/nfs/nfs4file.c
@@ -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 */
};
--
1.8.3.1


2015-10-24 15:29:44

by Peng Tao

[permalink] [raw]
Subject: [PATCH 8/9] nfsd: Pass filehandle to nfs4_preprocess_stateid_op()

From: Anna Schumaker <[email protected]>

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

Signed-off-by: Anna Schumaker <[email protected]>
---
fs/nfsd/nfs4proc.c | 16 +++++++++-------
fs/nfsd/nfs4state.c | 6 +++---
fs/nfsd/state.h | 5 +++--
3 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 4ce6b97..eab6d6d 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -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) {
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 0f1d569..8f4d925 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -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);
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 583ffc1..640f57f 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -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);
--
1.8.3.1


2015-10-24 15:29:51

by Peng Tao

[permalink] [raw]
Subject: [PATCH 9/9] NFSD: Implement the CLONE call

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.

Signed-off-by: Anna Schumaker <[email protected]>
[hch: change to implement the CLONE op instead of COPY]
Signed-off-by: Christoph Hellwig <[email protected]>
Signed-off-by: Peng Tao <[email protected]>
---
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(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index eab6d6d..d1c5a87 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -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",
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 51c9e9c..924416f 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -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,
};

/*
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 45c0497..9ae4992 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -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)
diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h
index fee2451..604830c 100644
--- a/fs/nfsd/vfs.h
+++ b/fs/nfsd/vfs.h
@@ -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,
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 9f99100..3d70712 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -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;
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index e7e7853..43aeabd 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -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,
--
1.8.3.1


2015-10-24 15:29:15

by Peng Tao

[permalink] [raw]
Subject: [PATCH 4/9] vfs: pull btrfs clone API to vfs layer

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.

Signed-off-by: Peng Tao <[email protected]>
---
fs/ioctl.c | 40 ++++++++++++++++++++++++++++++++++++++++
include/uapi/linux/fs.h | 9 +++++++++
2 files changed, 49 insertions(+)

diff --git a/fs/ioctl.c b/fs/ioctl.c
index 5d01d26..9a78426 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -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);
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index 9b964a5..ac7f1c5 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -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)
--
1.8.3.1


2015-10-24 15:29:09

by Peng Tao

[permalink] [raw]
Subject: [PATCH 3/9] nfs42: add .copy_file_range file operation

Signed-off-by: Peng Tao <[email protected]>
---
fs/nfs/nfs4file.c | 95 ++++++++++++++++++++++++++++++-------------------------
1 file changed, 52 insertions(+), 43 deletions(-)

diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
index 4aa5719..f1924d8 100644
--- a/fs/nfs/nfs4file.c
+++ b/fs/nfs/nfs4file.c
@@ -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,
--
1.8.3.1


2015-10-24 15:29:01

by Peng Tao

[permalink] [raw]
Subject: [PATCH 2/9] cifs: add .copy_file_range file operation

Signed-off-by: Peng Tao <[email protected]>
---
fs/cifs/cifsfs.c | 22 ++++++++++++
fs/cifs/cifsfs.h | 4 ++-
fs/cifs/ioctl.c | 100 +++++++++++++++++++++++++++++++------------------------
3 files changed, 82 insertions(+), 44 deletions(-)

diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index e739950..6ef7c3c 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -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,
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index c3cc160..797439b 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -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 */
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
index 28a77bf..bbab940 100644
--- a/fs/cifs/ioctl.c
+++ b/fs/cifs/ioctl.c
@@ -34,68 +34,36 @@
#include "cifs_ioctl.h"
#include <linux/btrfs.h>

-static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file,
- unsigned long srcfd, u64 off, u64 len, u64 destoff,
- bool dup_extents)
+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)
{
- int rc;
- struct cifsFileInfo *smb_file_target = dst_file->private_data;
+ struct inode *src_inode = file_inode(src_file);
struct inode *target_inode = file_inode(dst_file);
- struct cifs_tcon *target_tcon;
- struct fd src_file;
struct cifsFileInfo *smb_file_src;
- struct inode *src_inode;
+ struct cifsFileInfo *smb_file_target;
struct cifs_tcon *src_tcon;
+ struct cifs_tcon *target_tcon;
+ int rc;

- cifs_dbg(FYI, "ioctl clone range\n");
- /* the destination must be opened for writing */
- if (!(dst_file->f_mode & FMODE_WRITE)) {
- cifs_dbg(FYI, "file target not open for write\n");
- return -EINVAL;
- }
-
- /* check if target volume is readonly and take reference */
- rc = mnt_want_write_file(dst_file);
- if (rc) {
- cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc);
- return rc;
- }
-
- src_file = fdget(srcfd);
- if (!src_file.file) {
- rc = -EBADF;
- goto out_drop_write;
- }
-
- if (src_file.file->f_op->unlocked_ioctl != cifs_ioctl) {
- rc = -EBADF;
- cifs_dbg(VFS, "src file seems to be from a different filesystem type\n");
- goto out_fput;
- }
-
- if ((!src_file.file->private_data) || (!dst_file->private_data)) {
+ if ((!src_file->private_data) || (!dst_file->private_data)) {
rc = -EBADF;
cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n");
- goto out_fput;
+ goto out;
}

rc = -EXDEV;
smb_file_target = dst_file->private_data;
- smb_file_src = src_file.file->private_data;
+ smb_file_src = src_file->private_data;
src_tcon = tlink_tcon(smb_file_src->tlink);
target_tcon = tlink_tcon(smb_file_target->tlink);

/* check if source and target are on same tree connection */
if (src_tcon != target_tcon) {
cifs_dbg(VFS, "file copy src and target on different volume\n");
- goto out_fput;
+ goto out;
}

- src_inode = file_inode(src_file.file);
- rc = -EINVAL;
- if (S_ISDIR(src_inode->i_mode))
- goto out_fput;
-
/*
* Note: cifs case is easier than btrfs since server responsible for
* checks for proper open modes and file type and if it wants
@@ -131,6 +99,52 @@ out_unlock:
/* although unlocking in the reverse order from locking is not
strictly necessary here it is a little cleaner to be consistent */
unlock_two_nondirectories(src_inode, target_inode);
+out:
+ return rc;
+}
+
+static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file,
+ unsigned long srcfd, u64 off, u64 len, u64 destoff,
+ bool dup_extents)
+{
+ int rc;
+ struct fd src_file;
+ struct inode *src_inode;
+
+ cifs_dbg(FYI, "ioctl clone range\n");
+ /* the destination must be opened for writing */
+ if (!(dst_file->f_mode & FMODE_WRITE)) {
+ cifs_dbg(FYI, "file target not open for write\n");
+ return -EINVAL;
+ }
+
+ /* check if target volume is readonly and take reference */
+ rc = mnt_want_write_file(dst_file);
+ if (rc) {
+ cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc);
+ return rc;
+ }
+
+ src_file = fdget(srcfd);
+ if (!src_file.file) {
+ rc = -EBADF;
+ goto out_drop_write;
+ }
+
+ if (src_file.file->f_op->unlocked_ioctl != cifs_ioctl) {
+ rc = -EBADF;
+ cifs_dbg(VFS, "src file seems to be from a different filesystem type\n");
+ goto out_fput;
+ }
+
+ src_inode = file_inode(src_file.file);
+ rc = -EINVAL;
+ if (S_ISDIR(src_inode->i_mode))
+ goto out_fput;
+
+ rc = cifs_file_clone_range(xid, src_file.file, dst_file, off, len,
+ destoff, dup_extents);
+
out_fput:
fdput(src_file);
out_drop_write:
--
1.8.3.1


2015-10-24 15:29:21

by Peng Tao

[permalink] [raw]
Subject: [PATCH 5/9] btrfs: remove btrfs_ioctl_clone(_range)

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

Signed-off-by: Peng Tao <[email protected]>
---
fs/btrfs/ioctl.c | 49 -------------------------------------------------
1 file changed, 49 deletions(-)

diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index d3697e8..b568e24 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -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:
--
1.8.3.1


2015-10-24 15:28:53

by Peng Tao

[permalink] [raw]
Subject: [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.

Signed-off-by: Peng Tao <[email protected]>
---
fs/read_write.c | 9 +++++----
include/linux/fs.h | 3 +++
2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/fs/read_write.c b/fs/read_write.c
index d2da7e4..da11a7f 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -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) {
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 6220307..1c430fd 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -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);
--
1.8.3.1


2015-10-27 17:14:12

by Steve French

[permalink] [raw]
Subject: 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.

On Sat, Oct 24, 2015 at 6:17 PM, Peng Tao <[email protected]> wrote:
> Signed-off-by: Peng Tao <[email protected]>
> ---
> fs/cifs/cifsfs.c | 22 ++++++++++++
> fs/cifs/cifsfs.h | 4 ++-
> fs/cifs/ioctl.c | 100 +++++++++++++++++++++++++++++++------------------------
> 3 files changed, 82 insertions(+), 44 deletions(-)
>
> diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
> index e739950..6ef7c3c 100644
> --- a/fs/cifs/cifsfs.c
> +++ b/fs/cifs/cifsfs.c
> @@ -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,


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

--
Thanks,

Steve

2015-11-13 08:58:28

by Christoph Hellwig

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

On Sun, Oct 25, 2015 at 07:17:08AM +0800, Peng Tao wrote:
> 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.