NFS: Add READ_PLUS data segment support
authorAnna Schumaker <Anna.Schumaker@Netapp.com>
Wed, 28 May 2014 17:41:22 +0000 (13:41 -0400)
committerAnna Schumaker <Anna.Schumaker@Netapp.com>
Wed, 7 Oct 2020 18:28:39 +0000 (14:28 -0400)
This patch adds client support for decoding a single NFS4_CONTENT_DATA
segment returned by the server. This is the simplest implementation
possible, since it does not account for any hole segments in the reply.

Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
fs/nfs/nfs42xdr.c
fs/nfs/nfs4client.c
fs/nfs/nfs4proc.c
fs/nfs/nfs4xdr.c
include/linux/nfs4.h
include/linux/nfs_fs_sb.h
include/linux/nfs_xdr.h

index cc50085e151c5af3c0139965128d33fe42195d1b..930b4ca212c122cf4d3ab3aca52c61193d7b2e27 100644 (file)
 #define encode_deallocate_maxsz                (op_encode_hdr_maxsz + \
                                         encode_fallocate_maxsz)
 #define decode_deallocate_maxsz                (op_decode_hdr_maxsz)
+#define encode_read_plus_maxsz         (op_encode_hdr_maxsz + \
+                                        encode_stateid_maxsz + 3)
+#define NFS42_READ_PLUS_SEGMENT_SIZE   (1 /* data_content4 */ + \
+                                        2 /* data_info4.di_offset */ + \
+                                        2 /* data_info4.di_length */)
+#define decode_read_plus_maxsz         (op_decode_hdr_maxsz + \
+                                        1 /* rpr_eof */ + \
+                                        1 /* rpr_contents count */ + \
+                                        NFS42_READ_PLUS_SEGMENT_SIZE)
 #define encode_seek_maxsz              (op_encode_hdr_maxsz + \
                                         encode_stateid_maxsz + \
                                         2 /* offset */ + \
                                         decode_putfh_maxsz + \
                                         decode_deallocate_maxsz + \
                                         decode_getattr_maxsz)
+#define NFS4_enc_read_plus_sz          (compound_encode_hdr_maxsz + \
+                                        encode_sequence_maxsz + \
+                                        encode_putfh_maxsz + \
+                                        encode_read_plus_maxsz)
+#define NFS4_dec_read_plus_sz          (compound_decode_hdr_maxsz + \
+                                        decode_sequence_maxsz + \
+                                        decode_putfh_maxsz + \
+                                        decode_read_plus_maxsz)
 #define NFS4_enc_seek_sz               (compound_encode_hdr_maxsz + \
                                         encode_sequence_maxsz + \
                                         encode_putfh_maxsz + \
@@ -324,6 +341,16 @@ static void encode_deallocate(struct xdr_stream *xdr,
        encode_fallocate(xdr, args);
 }
 
+static void encode_read_plus(struct xdr_stream *xdr,
+                            const struct nfs_pgio_args *args,
+                            struct compound_hdr *hdr)
+{
+       encode_op_hdr(xdr, OP_READ_PLUS, decode_read_plus_maxsz, hdr);
+       encode_nfs4_stateid(xdr, &args->stateid);
+       encode_uint64(xdr, args->offset);
+       encode_uint32(xdr, args->count);
+}
+
 static void encode_seek(struct xdr_stream *xdr,
                        const struct nfs42_seek_args *args,
                        struct compound_hdr *hdr)
@@ -722,6 +749,28 @@ static void nfs4_xdr_enc_deallocate(struct rpc_rqst *req,
        encode_nops(&hdr);
 }
 
+/*
+ * Encode READ_PLUS request
+ */
+static void nfs4_xdr_enc_read_plus(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
+                                  const void *data)
+{
+       const struct nfs_pgio_args *args = data;
+       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->fh, &hdr);
+       encode_read_plus(xdr, args, &hdr);
+
+       rpc_prepare_reply_pages(req, args->pages, args->pgbase,
+                               args->count, hdr.replen);
+       encode_nops(&hdr);
+}
+
 /*
  * Encode SEEK request
  */
@@ -970,6 +1019,71 @@ static int decode_deallocate(struct xdr_stream *xdr, struct nfs42_falloc_res *re
        return decode_op_hdr(xdr, OP_DEALLOCATE);
 }
 
+static int decode_read_plus_data(struct xdr_stream *xdr, struct nfs_pgio_res *res,
+                                uint32_t *eof)
+{
+       uint32_t count, recvd;
+       uint64_t offset;
+       __be32 *p;
+
+       p = xdr_inline_decode(xdr, 8 + 4);
+       if (unlikely(!p))
+               return -EIO;
+
+       p = xdr_decode_hyper(p, &offset);
+       count = be32_to_cpup(p);
+       recvd = xdr_read_pages(xdr, count);
+       res->count += recvd;
+
+       if (count > recvd) {
+               dprintk("NFS: server cheating in read reply: "
+                               "count %u > recvd %u\n", count, recvd);
+               *eof = 0;
+               return 1;
+       }
+
+       return 0;
+}
+
+static int decode_read_plus(struct xdr_stream *xdr, struct nfs_pgio_res *res)
+{
+       uint32_t eof, segments, type;
+       int status;
+       __be32 *p;
+
+       status = decode_op_hdr(xdr, OP_READ_PLUS);
+       if (status)
+               return status;
+
+       p = xdr_inline_decode(xdr, 4 + 4);
+       if (unlikely(!p))
+               return -EIO;
+
+       eof = be32_to_cpup(p++);
+       segments = be32_to_cpup(p++);
+       if (segments == 0)
+               goto out;
+
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(!p))
+               return -EIO;
+
+       type = be32_to_cpup(p++);
+       if (type == NFS4_CONTENT_DATA)
+               status = decode_read_plus_data(xdr, res, &eof);
+       else
+               return -EINVAL;
+
+       if (status)
+               return status;
+       if (segments > 1)
+               eof = 0;
+
+out:
+       res->eof = eof;
+       return 0;
+}
+
 static int decode_seek(struct xdr_stream *xdr, struct nfs42_seek_res *res)
 {
        int status;
@@ -1146,6 +1260,33 @@ out:
        return status;
 }
 
+/*
+ * Decode READ_PLUS request
+ */
+static int nfs4_xdr_dec_read_plus(struct rpc_rqst *rqstp,
+                                 struct xdr_stream *xdr,
+                                 void *data)
+{
+       struct nfs_pgio_res *res = data;
+       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_read_plus(xdr, res);
+       if (!status)
+               status = res->count;
+out:
+       return status;
+}
+
 /*
  * Decode SEEK request
  */
index daacc78a3d480d82ab42d52f99c293f959b89d19..be7915c861cef6289bf3f3311fee07969029c65c 100644 (file)
@@ -1045,6 +1045,8 @@ static int nfs4_server_common_setup(struct nfs_server *server,
        server->caps |= server->nfs_client->cl_mvops->init_caps;
        if (server->flags & NFS_MOUNT_NORDIRPLUS)
                        server->caps &= ~NFS_CAP_READDIRPLUS;
+       if (server->nfs_client->cl_proto == XPRT_TRANSPORT_RDMA)
+               server->caps &= ~NFS_CAP_READ_PLUS;
        /*
         * Don't use NFS uid/gid mapping if we're using AUTH_SYS or lower
         * authentication.
index f7ef2ca699a5f6268ca78137f995f77d9c3cc963..d09fd32368202f4b477eebb2b27fb6a5765f0f10 100644 (file)
 
 #include "nfs4trace.h"
 
+#ifdef CONFIG_NFS_V4_2
+#include "nfs42.h"
+#endif /* CONFIG_NFS_V4_2 */
+
 #define NFSDBG_FACILITY                NFSDBG_PROC
 
 #define NFS4_BITMASK_SZ                3
@@ -5272,28 +5276,60 @@ static bool nfs4_read_stateid_changed(struct rpc_task *task,
        return true;
 }
 
-static int nfs4_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
+static bool nfs4_read_plus_not_supported(struct rpc_task *task,
+                                        struct nfs_pgio_header *hdr)
 {
+       struct nfs_server *server = NFS_SERVER(hdr->inode);
+       struct rpc_message *msg = &task->tk_msg;
 
+       if (msg->rpc_proc == &nfs4_procedures[NFSPROC4_CLNT_READ_PLUS] &&
+           server->caps & NFS_CAP_READ_PLUS && task->tk_status == -ENOTSUPP) {
+               server->caps &= ~NFS_CAP_READ_PLUS;
+               msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ];
+               rpc_restart_call_prepare(task);
+               return true;
+       }
+       return false;
+}
+
+static int nfs4_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
+{
        dprintk("--> %s\n", __func__);
 
        if (!nfs4_sequence_done(task, &hdr->res.seq_res))
                return -EAGAIN;
        if (nfs4_read_stateid_changed(task, &hdr->args))
                return -EAGAIN;
+       if (nfs4_read_plus_not_supported(task, hdr))
+               return -EAGAIN;
        if (task->tk_status > 0)
                nfs_invalidate_atime(hdr->inode);
        return hdr->pgio_done_cb ? hdr->pgio_done_cb(task, hdr) :
                                    nfs4_read_done_cb(task, hdr);
 }
 
+#ifdef CONFIG_NFS_V4_2
+static void nfs42_read_plus_support(struct nfs_server *server, struct rpc_message *msg)
+{
+       if (server->caps & NFS_CAP_READ_PLUS)
+               msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ_PLUS];
+       else
+               msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ];
+}
+#else
+static void nfs42_read_plus_support(struct nfs_server *server, struct rpc_message *msg)
+{
+       msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ];
+}
+#endif /* CONFIG_NFS_V4_2 */
+
 static void nfs4_proc_read_setup(struct nfs_pgio_header *hdr,
                                 struct rpc_message *msg)
 {
        hdr->timestamp   = jiffies;
        if (!hdr->pgio_done_cb)
                hdr->pgio_done_cb = nfs4_read_done_cb;
-       msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ];
+       nfs42_read_plus_support(NFS_SERVER(hdr->inode), msg);
        nfs4_init_sequence(&hdr->args.seq_args, &hdr->res.seq_res, 0, 0);
 }
 
@@ -10215,7 +10251,8 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
                | NFS_CAP_SEEK
                | NFS_CAP_LAYOUTSTATS
                | NFS_CAP_CLONE
-               | NFS_CAP_LAYOUTERROR,
+               | NFS_CAP_LAYOUTERROR
+               | NFS_CAP_READ_PLUS,
        .init_client = nfs41_init_client,
        .shutdown_client = nfs41_shutdown_client,
        .match_stateid = nfs41_match_stateid,
index 3336ea3407a073b09c9cb8e362fb8bb7f106dacf..c6dbfcae75171e57039b67c82ed36dc093590068 100644 (file)
@@ -7615,6 +7615,7 @@ const struct rpc_procinfo nfs4_procedures[] = {
        PROC42(SETXATTR,        enc_setxattr,           dec_setxattr),
        PROC42(LISTXATTRS,      enc_listxattrs,         dec_listxattrs),
        PROC42(REMOVEXATTR,     enc_removexattr,        dec_removexattr),
+       PROC42(READ_PLUS,       enc_read_plus,          dec_read_plus),
 };
 
 static unsigned int nfs_version4_counts[ARRAY_SIZE(nfs4_procedures)];
index b8360be141daf7aa8dd3d8ceccf9d20ab195e7d3..9dc7eeac924f08f715e21b3105897eef0977d238 100644 (file)
@@ -551,13 +551,13 @@ enum {
 
        NFSPROC4_CLNT_LOOKUPP,
        NFSPROC4_CLNT_LAYOUTERROR,
-
        NFSPROC4_CLNT_COPY_NOTIFY,
 
        NFSPROC4_CLNT_GETXATTR,
        NFSPROC4_CLNT_SETXATTR,
        NFSPROC4_CLNT_LISTXATTRS,
        NFSPROC4_CLNT_REMOVEXATTR,
+       NFSPROC4_CLNT_READ_PLUS,
 };
 
 /* nfs41 types */
index 7eae72a8762eebaeccd0456d260cb22b3951ce36..38e60ec742df70d21eaabb6d5a70d21b1b72b664 100644 (file)
@@ -287,5 +287,6 @@ struct nfs_server {
 #define NFS_CAP_LAYOUTERROR    (1U << 26)
 #define NFS_CAP_COPY_NOTIFY    (1U << 27)
 #define NFS_CAP_XATTR          (1U << 28)
+#define NFS_CAP_READ_PLUS      (1U << 29)
 
 #endif
index 0599efd57eb9f11911ab1dde66b8d2f6917cf6af..d63cb862d58e5116c69eba7f728fde8202f69f88 100644 (file)
@@ -657,7 +657,7 @@ struct nfs_pgio_args {
 struct nfs_pgio_res {
        struct nfs4_sequence_res        seq_res;
        struct nfs_fattr *      fattr;
-       __u32                   count;
+       __u64                   count;
        __u32                   op_status;
        union {
                struct {