On Fri, 2013-07-19 at 17:03 -0400, bjschuma@xxxxxxxxxx wrote: > From: Bryan Schumaker <bjschuma@xxxxxxxxxx> > > This adds the copy_range file_ops function pointer used by the > sys_copy_range() function call. This patch only implements sync copies, > so if an async copy happens we decode the stateid and ignore it. > > Signed-off-by: Bryan Schumaker <bjschuma@xxxxxxxxxx> > --- > fs/nfs/inode.c | 2 + > fs/nfs/nfs4_fs.h | 4 ++ > fs/nfs/nfs4file.c | 53 +++++++++++++++++ > fs/nfs/nfs4proc.c | 16 ++++++ > fs/nfs/nfs4xdr.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++ > include/linux/nfs4.h | 11 +++- > include/linux/nfs_xdr.h | 30 ++++++++++ > 7 files changed, 265 insertions(+), 1 deletion(-) > > diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c > index af6e806..80849a0 100644 > --- a/fs/nfs/inode.c > +++ b/fs/nfs/inode.c > @@ -677,6 +677,7 @@ struct nfs_lock_context *nfs_get_lock_context(struct nfs_open_context *ctx) > kfree(new); > return res; > } > +EXPORT_SYMBOL_GPL(nfs_get_lock_context); > > void nfs_put_lock_context(struct nfs_lock_context *l_ctx) > { > @@ -689,6 +690,7 @@ void nfs_put_lock_context(struct nfs_lock_context *l_ctx) > spin_unlock(&inode->i_lock); > kfree(l_ctx); > } > +EXPORT_SYMBOL_GPL(nfs_put_lock_context); > > /** > * nfs_close_context - Common close_context() routine NFSv2/v3 > diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h > index ee81e35..26c7cf0 100644 > --- a/fs/nfs/nfs4_fs.h > +++ b/fs/nfs/nfs4_fs.h > @@ -300,6 +300,10 @@ is_ds_client(struct nfs_client *clp) > } > #endif /* CONFIG_NFS_V4_1 */ > > +#ifdef CONFIG_NFS_V4_2 > +int nfs42_proc_copy(struct nfs_server *, struct nfs42_copy_args *, struct nfs42_copy_res *); > +#endif /* CONFIG_NFS_V4_2 */ > + > extern const struct nfs4_minor_version_ops *nfs_v4_minor_ops[]; > > extern const u32 nfs4_fattr_bitmap[3]; > diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c > index e5b804d..ca77ab4 100644 > --- a/fs/nfs/nfs4file.c > +++ b/fs/nfs/nfs4file.c > @@ -117,6 +117,56 @@ nfs4_file_fsync(struct file *file, loff_t start, loff_t end, int datasync) > return ret; > } > > +#ifdef CONFIG_NFS_V4_2 > +static int nfs4_find_copy_stateid(struct file *file, nfs4_stateid *stateid, > + fmode_t mode) > +{ > + struct nfs_open_context *open; > + struct nfs_lock_context *lock; > + int ret; > + > + open = nfs_file_open_context(file); > + if (!open) > + return PTR_ERR(open); PTR_ERR(open) == 0 here. Was that really the intention? > + > + lock = nfs_get_lock_context(open); nfs_get_lock_context() will return an ERR_PTR on failure. > + ret = nfs4_set_rw_stateid(stateid, open, lock, mode); > + > + if (lock) > + nfs_put_lock_context(lock); > + return ret; > +} > + > +static ssize_t nfs4_copy_range(struct file *file_in, loff_t pos_in, > + struct file *file_out, loff_t pos_out, > + size_t count) > +{ > + int err; > + struct nfs42_copy_args args = { > + .src_fh = NFS_FH(file_inode(file_in)), > + .src_pos = pos_in, > + .dst_fh = NFS_FH(file_inode(file_out)), > + .dst_pos = pos_out, > + .count = count, > + }; > + struct nfs42_copy_res res; > + > + err = nfs4_find_copy_stateid(file_in, &args.src_stateid, FMODE_READ); > + if (err) > + return err; > + > + err = nfs4_find_copy_stateid(file_out, &args.dst_stateid, FMODE_WRITE); > + if (err) > + return err; > + > + err = nfs42_proc_copy(NFS_SERVER(file_inode(file_out)), &args, &res); > + if (err) > + return err; > + > + return res.cp_res.wr_bytes_copied; > +} > +#endif /* CONFIG_NFS_V4_2 */ > + > const struct file_operations nfs4_file_operations = { > .llseek = nfs_file_llseek, > .read = do_sync_read, > @@ -134,4 +184,7 @@ const struct file_operations nfs4_file_operations = { > .splice_write = nfs_file_splice_write, > .check_flags = nfs_check_flags, > .setlease = nfs_setlease, > +#ifdef CONFIG_NFS_V4_2 > + .copy_range = nfs4_copy_range, > +#endif /* CONFIG_NFS_V4_2 */ > }; > diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c > index cf11799..f7eb4fd 100644 > --- a/fs/nfs/nfs4proc.c > +++ b/fs/nfs/nfs4proc.c > @@ -7346,6 +7346,22 @@ static bool nfs41_match_stateid(const nfs4_stateid *s1, > > #endif /* CONFIG_NFS_V4_1 */ > > +#ifdef CONFIG_NFS_V4_2 > +int nfs42_proc_copy(struct nfs_server *server, struct nfs42_copy_args *args, > + struct nfs42_copy_res *res) > +{ > + struct rpc_message msg = { > + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY], > + .rpc_argp = args, > + .rpc_resp = res, Shouldn't you set .rpc_cred to the correct value too? I assume that needs to reflect the credential used when opening file_out. ? > + }; > + > + dprintk("NFS call copy %p\n", &args); > + return nfs4_call_sync(server->client, server, &msg, > + &(args->seq_args), &(res->seq_res), 0); The () around args->seq_args and res->seq_res are redundant here. > +} > +#endif /* CONFIG_NFS_V4_2 */ > + > static bool nfs4_match_stateid(const nfs4_stateid *s1, > const nfs4_stateid *s2) > { > diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c > index 0abfb846..d70c6bc 100644 > --- a/fs/nfs/nfs4xdr.c > +++ b/fs/nfs/nfs4xdr.c > @@ -416,6 +416,16 @@ static int nfs4_stat_to_errno(int); > #define decode_sequence_maxsz 0 > #endif /* CONFIG_NFS_V4_1 */ > > +#ifdef CONFIG_NFS_V4_2 > +#define encode_copy_maxsz (op_encode_hdr_maxsz + \ > + XDR_QUADLEN(NFS4_STATEID_SIZE) + \ > + XDR_QUADLEN(NFS4_STATEID_SIZE) + \ > + 2 + 2 + 2 + 1 + 1 + 1) > +#define decode_copy_maxsz (op_decode_hdr_maxsz + \ > + 1 + XDR_QUADLEN(NFS4_STATEID_SIZE) + \ > + 2 + 1 + XDR_QUADLEN(NFS4_VERIFIER_SIZE)) > +#endif /* CONFIG_NFS_V4_2 */ > + > #define NFS4_enc_compound_sz (1024) /* XXX: large enough? */ > #define NFS4_dec_compound_sz (1024) /* XXX: large enough? */ > #define NFS4_enc_read_sz (compound_encode_hdr_maxsz + \ > @@ -875,6 +885,19 @@ const u32 nfs41_maxgetdevinfo_overhead = ((RPC_MAX_REPHEADER_WITH_AUTH + > EXPORT_SYMBOL_GPL(nfs41_maxgetdevinfo_overhead); > #endif /* CONFIG_NFS_V4_1 */ > > +#ifdef CONFIG_NFS_V4_2 > +#define NFS4_enc_copy_sz (compound_encode_hdr_maxsz + \ > + encode_putfh_maxsz + \ > + encode_savefh_maxsz + \ > + encode_putfh_maxsz + \ > + encode_copy_maxsz) > +#define NFS4_dec_copy_sz (compound_decode_hdr_maxsz + \ > + decode_putfh_maxsz + \ > + decode_savefh_maxsz + \ > + decode_putfh_maxsz + \ > + decode_copy_maxsz) > +#endif /* CONFIG_NFS_V4_2 */ > + > static const umode_t nfs_type2fmt[] = { > [NF4BAD] = 0, > [NF4REG] = S_IFREG, > @@ -2048,6 +2071,27 @@ static void encode_free_stateid(struct xdr_stream *xdr, > } > #endif /* CONFIG_NFS_V4_1 */ > > +#ifdef CONFIG_NFS_V4_2 > +static void encode_copy(struct xdr_stream *xdr, > + struct nfs42_copy_args *args, > + struct compound_hdr *hdr) > +{ > + encode_op_hdr(xdr, OP_COPY, decode_copy_maxsz, hdr); > + encode_nfs4_stateid(xdr, &args->src_stateid); > + encode_nfs4_stateid(xdr, &args->dst_stateid); > + > + /* TODO: Partial file copy with changable offsets */ > + encode_uint64(xdr, args->src_pos); /* src offset */ > + encode_uint64(xdr, args->dst_pos); /* dst offset */ > + encode_uint64(xdr, args->count); /* count */ > + > + encode_uint32(xdr, COPY4_METADATA); /* flags */ Are we sure that we want COPY4_METADATA here? I assumed the copy_range would copy file data ranges only. > + > + encode_uint32(xdr, 0); /* ca_destination */ > + encode_uint32(xdr, 0); /* src server list */ > +} > +#endif /* CONFIG_NFS_V4_2 */ > + > /* > * END OF "GENERIC" ENCODE ROUTINES. > */ > @@ -2994,6 +3038,29 @@ static void nfs4_xdr_enc_free_stateid(struct rpc_rqst *req, > } > #endif /* CONFIG_NFS_V4_1 */ > > +#ifdef CONFIG_NFS_V4_2 > +/* > + * Encode COPY request > + */ > +static void nfs4_xdr_enc_copy(struct rpc_rqst *req, > + struct xdr_stream *xdr, > + struct nfs42_copy_args *args) > +{ > + struct compound_hdr hdr = { > + .minorversion = nfs4_xdr_minorversion(&args->seq_args), > + }; > + > + encode_compound_hdr(xdr, req, &hdr); > + encode_sequence(xdr, &args->seq_args, &hdr); > + encode_putfh(xdr, args->src_fh, &hdr); > + encode_savefh(xdr, &hdr); > + encode_putfh(xdr, args->dst_fh, &hdr); > + encode_copy(xdr, args, &hdr); > + encode_nops(&hdr); > + return; > +} > +#endif /* CONFIG_NFS_V4_2 */ > + > static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) > { > dprintk("nfs: %s: prematurely hit end of receive buffer. " > @@ -5943,6 +6010,54 @@ out_overflow: > } > #endif /* CONFIG_NFS_V4_1 */ > > +#ifdef CONFIG_NFS_V4_2 > +static int decode_write_response(struct xdr_stream *xdr, > + struct nfs42_write_response *write_res) > +{ > + __be32 *p; > + int num_ids; > + > + p = xdr_inline_decode(xdr, 4); > + if (unlikely(!p)) > + goto out_overflow; > + num_ids = be32_to_cpup(p); > + > + if (num_ids == 0) > + write_res->wr_stateid = NULL; > + else { > + write_res->wr_stateid = kmalloc(sizeof(nfs4_stateid), GFP_KERNEL); Please don't allocate from XDR routines. You are better off preallocating the above structure in the nfs42_proc_ routine. Better yet, embed it in struct nfs42_write_response. > + if (decode_stateid(xdr, write_res->wr_stateid) != 0) > + goto out_free; > + } > + > + p = xdr_inline_decode(xdr, 12); > + if (unlikely(!p)) > + goto out_free; > + p = xdr_decode_hyper(p, &write_res->wr_bytes_copied); > + write_res->wr_committed = be32_to_cpup(p); > + > + return decode_write_verifier(xdr, &write_res->wr_verf); > + > +out_free: > + kfree(write_res->wr_stateid); > + > +out_overflow: > + print_overflow_msg(__func__, xdr); > + return -EIO; > +} > + > +static int decode_copy(struct xdr_stream *xdr, struct nfs42_copy_res *res) > +{ > + int status; > + > + status = decode_op_hdr(xdr, OP_COPY); > + if (status) > + return status; > + > + return decode_write_response(xdr, &res->cp_res); > +} > +#endif /* CONFIG_NFS_V4_2 */ > + > /* > * END OF "GENERIC" DECODE ROUTINES. > */ > @@ -7155,6 +7270,38 @@ out: > } > #endif /* CONFIG_NFS_V4_1 */ > > +#ifdef CONFIG_NFS_V4_2 > +/* > + * Decode COPY request > + */ > +static int nfs4_xdr_dec_copy(struct rpc_rqst *rqstp, > + struct xdr_stream *xdr, > + struct nfs42_copy_res *res) > +{ > + struct compound_hdr hdr; > + int status; > + > + status = decode_compound_hdr(xdr, &hdr); > + if (status) > + goto out; > + status = decode_sequence(xdr, &res->seq_res, rqstp); > + if (status) > + goto out; > + status = decode_putfh(xdr); > + if (status) > + goto out; > + status = decode_savefh(xdr); > + if (status) > + goto out; > + status = decode_putfh(xdr); > + if (status) > + goto out; > + status = decode_copy(xdr, res); > +out: > + return status; > +} > +#endif /* CONFIG_NFS_V4_2 */ > + > /** > * nfs4_decode_dirent - Decode a single NFSv4 directory entry stored in > * the local page cache. > @@ -7364,6 +7511,9 @@ struct rpc_procinfo nfs4_procedures[] = { > enc_bind_conn_to_session, dec_bind_conn_to_session), > PROC(DESTROY_CLIENTID, enc_destroy_clientid, dec_destroy_clientid), > #endif /* CONFIG_NFS_V4_1 */ > +#if defined(CONFIG_NFS_V4_2) > + PROC(COPY, enc_copy, dec_copy), > +#endif /* CONFIG_NFS_V4_2 */ > }; > > const struct rpc_version nfs_version4 = { > diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h > index ebf60c6..347de63 100644 > --- a/include/linux/nfs4.h > +++ b/include/linux/nfs4.h > @@ -120,7 +120,7 @@ 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_RECLAIM_COMPLETE > +#define LAST_NFS4_OP OP_COPY > > enum nfsstat4 { > NFS4_OK = 0, > @@ -333,6 +333,12 @@ enum lock_type4 { > NFS4_WRITEW_LT = 4 > }; > > +#ifdef CONFIG_NFS_V4_2 > +enum copy_flags4 { > + COPY4_GUARDED = (1 << 0), > + COPY4_METADATA = (1 << 1), > +}; > +#endif > > /* Mandatory Attributes */ > #define FATTR4_WORD0_SUPPORTED_ATTRS (1UL << 0) > @@ -481,6 +487,9 @@ enum { > NFSPROC4_CLNT_GETDEVICELIST, > NFSPROC4_CLNT_BIND_CONN_TO_SESSION, > NFSPROC4_CLNT_DESTROY_CLIENTID, > + > + /* nfs42 */ > + NFSPROC4_CLNT_COPY, > }; > > /* nfs41 types */ > diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h > index 8651574..0bc6b14 100644 > --- a/include/linux/nfs_xdr.h > +++ b/include/linux/nfs_xdr.h > @@ -1204,6 +1204,36 @@ struct pnfs_ds_commit_info { > > #endif /* CONFIG_NFS_V4_1 */ > > +#ifdef CONFIG_NFS_V4_2 > +struct nfs42_write_response > +{ > + nfs4_stateid *wr_stateid; > + u64 wr_bytes_copied; > + int wr_committed; > + struct nfs_write_verifier wr_verf; > +}; > + > +struct nfs42_copy_args { > + struct nfs4_sequence_args seq_args; > + > + struct nfs_fh *src_fh; > + nfs4_stateid src_stateid; > + u64 src_pos; > + > + struct nfs_fh *dst_fh; > + nfs4_stateid dst_stateid; > + u64 dst_pos; > + > + u64 count; > +}; > + > +struct nfs42_copy_res { > + struct nfs4_sequence_res seq_res; > + unsigned int status; > + struct nfs42_write_response cp_res; > +}; > +#endif > + > struct nfs_page; > > #define NFS_PAGEVEC_SIZE (8U) -- Trond Myklebust Linux NFS client maintainer NetApp Trond.Myklebust@xxxxxxxxxx www.netapp.com ��.n��������+%������w��{.n�����{��w���jg��������ݢj����G�������j:+v���w�m������w�������h�����٥