NFSD: Add READ_PLUS data support
authorAnna Schumaker <Anna.Schumaker@Netapp.com>
Mon, 28 Sep 2020 17:08:58 +0000 (13:08 -0400)
committerJ. Bruce Fields <bfields@redhat.com>
Mon, 12 Oct 2020 14:29:45 +0000 (10:29 -0400)
This patch adds READ_PLUS support for returning a single
NFS4_CONTENT_DATA segment to the client. This is basically the same as
the READ operation, only with the extra information about data segments.

Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
fs/nfsd/nfs4proc.c
fs/nfsd/nfs4xdr.c

index b09334414b98a7d43a486663280ffbd2e4b82205..3dd69b6315c880c2520f6d67020e0d429bcd1546 100644 (file)
@@ -2590,6 +2590,20 @@ 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);
+       /*
+        * If we detect that the file changed during hole encoding, then we
+        * recover by encoding the remaining reply as data. This means we need
+        * to set aside enough room to encode two data segments.
+        */
+       u32 seg_len = 2 * (1 + 2 + 1);
+
+       return (op_encode_hdr_size + 2 + seg_len + XDR_QUADLEN(rlen)) * sizeof(__be32);
+}
+
 static inline u32 nfsd4_readdir_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
 {
        u32 maxcount = 0, rlen = 0;
@@ -3162,6 +3176,13 @@ 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_release = nfsd4_read_release,
+               .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",
index a37828c375aea9b2c0ca42b838a9f57fa0c57067..206670a2029005a8ced07984a4eee7fcd89f7197 100644 (file)
@@ -2173,7 +2173,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,
@@ -2261,7 +2261,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
                 */
                cachethis |= nfsd4_cache_this_op(op);
 
-               if (op->opnum == OP_READ) {
+               if (op->opnum == OP_READ || op->opnum == OP_READ_PLUS) {
                        readcount++;
                        readbytes += nfsd4_max_reply(argp->rqstp, op);
                } else
@@ -4597,6 +4597,87 @@ 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,
+                           unsigned long maxcount,  u32 *eof)
+{
+       struct xdr_stream *xdr = &resp->xdr;
+       struct file *file = read->rd_nf->nf_file;
+       int starting_len = xdr->buf->len;
+       __be32 nfserr;
+       __be32 *p, tmp;
+       __be64 tmp64;
+
+       maxcount = min_t(unsigned long, maxcount, (xdr->buf->buflen - xdr->buf->len));
+
+       /* Content type, offset, byte count */
+       p = xdr_reserve_space(xdr, 4 + 8 + 4);
+       if (!p)
+               return nfserr_resource;
+
+       read->rd_vlen = xdr_reserve_space_vec(xdr, resp->rqstp->rq_vec, maxcount);
+       if (read->rd_vlen < 0)
+               return nfserr_resource;
+
+       nfserr = nfsd_readv(resp->rqstp, read->rd_fhp, file, read->rd_offset,
+                           resp->rqstp->rq_vec, read->rd_vlen, &maxcount, eof);
+       if (nfserr)
+               return nfserr;
+
+       tmp = htonl(NFS4_CONTENT_DATA);
+       write_bytes_to_xdr_buf(xdr->buf, starting_len,      &tmp,   4);
+       tmp64 = cpu_to_be64(read->rd_offset);
+       write_bytes_to_xdr_buf(xdr->buf, starting_len + 4,  &tmp64, 8);
+       tmp = htonl(maxcount);
+       write_bytes_to_xdr_buf(xdr->buf, starting_len + 12, &tmp,   4);
+       return nfs_ok;
+}
+
+static __be32
+nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr,
+                      struct nfsd4_read *read)
+{
+       unsigned long maxcount;
+       struct xdr_stream *xdr = &resp->xdr;
+       struct file *file;
+       int starting_len = xdr->buf->len;
+       int segments = 0;
+       __be32 *p, tmp;
+       u32 eof;
+
+       if (nfserr)
+               return nfserr;
+       file = read->rd_nf->nf_file;
+
+       /* eof flag, segment count */
+       p = xdr_reserve_space(xdr, 4 + 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);
+
+       eof = read->rd_offset >= i_size_read(file_inode(file));
+       if (!eof) {
+               nfserr = nfsd4_encode_read_plus_data(resp, read, maxcount, &eof);
+               segments++;
+       }
+
+       if (nfserr)
+               xdr_truncate_encode(xdr, starting_len);
+       else {
+               tmp = htonl(eof);
+               write_bytes_to_xdr_buf(xdr->buf, starting_len,     &tmp, 4);
+               tmp = htonl(segments);
+               write_bytes_to_xdr_buf(xdr->buf, starting_len + 4, &tmp, 4);
+       }
 
        return nfserr;
 }
@@ -4974,7 +5055,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,