This patch adds READ_PLUS support for both NFS4_CONTENT_DATA and NFS4_CONTENT_HOLE segments. I keep things simple for now by only returning a hole segment if it is the first segment found at the given offset. Everything else, including other hole segments, will be encoded as data. Signed-off-by: Anna Schumaker <Anna.Schumaker@xxxxxxxxxx> --- fs/nfsd/nfs4proc.c | 16 +++++++ fs/nfsd/nfs4xdr.c | 113 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 127 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 0cfd257ffdaf..1c5f2c3da55f 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -2180,6 +2180,16 @@ static inline u32 nfsd4_read_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) return (op_encode_hdr_size + 2 + XDR_QUADLEN(rlen)) * sizeof(__be32); } +static inline u32 nfsd4_read_plus_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) +{ + u32 maxcount = svc_max_payload(rqstp); + u32 rlen = min(op->u.read.rd_length, maxcount); + /* enough extra xdr space for encoding either a hole or data segment. */ + u32 xdr = 5; + + return (op_encode_hdr_size + 2 + xdr + XDR_QUADLEN(rlen)) * sizeof(__be32); +} + static inline u32 nfsd4_readdir_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) { u32 maxcount = 0, rlen = 0; @@ -2701,6 +2711,12 @@ static const struct nfsd4_operation nfsd4_ops[] = { .op_name = "OP_COPY", .op_rsize_bop = nfsd4_copy_rsize, }, + [OP_READ_PLUS] = { + .op_func = nfsd4_read, + .op_name = "OP_READ_PLUS", + .op_rsize_bop = nfsd4_read_plus_rsize, + .op_get_currentstateid = nfsd4_get_readstateid, + }, [OP_SEEK] = { .op_func = nfsd4_seek, .op_name = "OP_SEEK", diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index bb487e5c022c..ec953efd24c2 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1882,7 +1882,7 @@ static const nfsd4_dec nfsd4_dec_ops[] = { [OP_LAYOUTSTATS] = (nfsd4_dec)nfsd4_decode_notsupp, [OP_OFFLOAD_CANCEL] = (nfsd4_dec)nfsd4_decode_offload_status, [OP_OFFLOAD_STATUS] = (nfsd4_dec)nfsd4_decode_offload_status, - [OP_READ_PLUS] = (nfsd4_dec)nfsd4_decode_notsupp, + [OP_READ_PLUS] = (nfsd4_dec)nfsd4_decode_read, [OP_SEEK] = (nfsd4_dec)nfsd4_decode_seek, [OP_WRITE_SAME] = (nfsd4_dec)nfsd4_decode_notsupp, [OP_CLONE] = (nfsd4_dec)nfsd4_decode_clone, @@ -4273,7 +4273,116 @@ nfsd4_encode_offload_status(struct nfsd4_compoundres *resp, __be32 nfserr, return nfserr_resource; p = xdr_encode_hyper(p, os->count); *p++ = cpu_to_be32(0); + return nfserr; +} + +static __be32 +nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp, struct nfsd4_read *read, + struct file *file) +{ + struct xdr_stream *xdr = &resp->xdr; + unsigned long maxcount; + __be32 *p, nfserr; + + p = xdr_reserve_space(xdr, 4 + 8 + 4); + if (!p) + return nfserr_resource; + xdr_commit_encode(xdr); + + maxcount = svc_max_payload(resp->rqstp); + maxcount = min_t(unsigned long, maxcount, + (xdr->buf->buflen - xdr->buf->len)); + maxcount = min_t(unsigned long, maxcount, read->rd_length); + + if (file->f_op->splice_read && + test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags)) + nfserr = nfsd4_encode_splice_read(resp, read, file, &maxcount); + else + nfserr = nfsd4_encode_readv(resp, read, file, &maxcount); + + *p++ = cpu_to_be32(NFS4_CONTENT_DATA); + p = xdr_encode_hyper(p, read->rd_offset); + *p++ = cpu_to_be32(maxcount); + + read->rd_offset += maxcount; + return nfserr; +} + +static __be32 +nfsd4_encode_read_plus_hole(struct nfsd4_compoundres *resp, struct nfsd4_read *read, + unsigned long length) +{ + __be32 *p = xdr_reserve_space(&resp->xdr, 4 + 8 + 8); + if (!p) + return nfserr_resource; + + length = min_t(unsigned long, read->rd_length, length); + + *p++ = cpu_to_be32(NFS4_CONTENT_HOLE); + p = xdr_encode_hyper(p, read->rd_offset); + p = xdr_encode_hyper(p, length); + + read->rd_offset += length; + read->rd_length -= length; + return nfs_ok; +} +static __be32 +nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr, + struct nfsd4_read *read) +{ + struct xdr_stream *xdr = &resp->xdr; + struct file *file = read->rd_filp; + int starting_len = xdr->buf->len; + struct raparms *ra = NULL; + loff_t data_pos; + __be32 *p; + u32 eof, segments = 0; + + if (nfserr) + goto out; + + /* eof flag, segment count */ + p = xdr_reserve_space(xdr, 4 + 4 ); + if (!p) { + nfserr = nfserr_resource; + goto out; + } + xdr_commit_encode(xdr); + + if (read->rd_tmp_file) + ra = nfsd_init_raparms(file); + + data_pos = vfs_llseek(file, read->rd_offset, SEEK_DATA); + if (data_pos == -ENXIO) + data_pos = i_size_read(file_inode(file)); + + if (data_pos > read->rd_offset) { + nfserr = nfsd4_encode_read_plus_hole(resp, read, + data_pos - read->rd_offset); + if (nfserr) + goto out; + segments++; + } + + if (read->rd_length > 0) { + nfserr = nfsd4_encode_read_plus_data(resp, read, file); + segments++; + } + + eof = (read->rd_offset >= i_size_read(file_inode(file))); + *p++ = cpu_to_be32(eof); + *p++ = cpu_to_be32(segments); + + if (ra) + nfsd_put_raparams(file, ra); + + if (nfserr) + xdr_truncate_encode(xdr, starting_len); + +out: + if (file) + fput(file); return nfserr; } @@ -4381,7 +4490,7 @@ static const nfsd4_enc nfsd4_enc_ops[] = { [OP_LAYOUTSTATS] = (nfsd4_enc)nfsd4_encode_noop, [OP_OFFLOAD_CANCEL] = (nfsd4_enc)nfsd4_encode_noop, [OP_OFFLOAD_STATUS] = (nfsd4_enc)nfsd4_encode_offload_status, - [OP_READ_PLUS] = (nfsd4_enc)nfsd4_encode_noop, + [OP_READ_PLUS] = (nfsd4_enc)nfsd4_encode_read_plus, [OP_SEEK] = (nfsd4_enc)nfsd4_encode_seek, [OP_WRITE_SAME] = (nfsd4_enc)nfsd4_encode_noop, [OP_CLONE] = (nfsd4_enc)nfsd4_encode_noop, -- 2.20.1