]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
NFSD: Clean up legacy NFS WRITE argument XDR decoders
authorChuck Lever <chuck.lever@oracle.com>
Tue, 27 Mar 2018 14:54:07 +0000 (10:54 -0400)
committerJ. Bruce Fields <bfields@redhat.com>
Tue, 3 Apr 2018 19:08:16 +0000 (15:08 -0400)
Move common code in NFSD's legacy NFS WRITE decoders into a helper.
The immediate benefit is reduction of code duplication and some nice
micro-optimizations (see below).

In the long term, this helper can perform a per-transport call-out
to fill the rq_vec (say, using RDMA Reads).

The legacy WRITE decoders and procs are changed to work like NFSv4,
which constructs the rq_vec just before it is about to call
vfs_writev.

Why? Calling a transport call-out from the proc instead of the XDR
decoder means that the incoming FH can be resolved to a particular
filesystem and file. This would allow pages from the backing file to
be presented to the transport to be filled, rather than presenting
anonymous pages and copying or flipping them into the file's page
cache later.

I also prefer using the pages in rq_arg.pages, instead of pulling
the data pages directly out of the rqstp::rq_pages array. This is
currently the way the NFSv3 write decoder works, but the other two
do not seem to take this approach. Fixing this removes the only
reference to rq_pages found in NFSD, eliminating an NFSD assumption
about how transports use the pages in rq_pages.

Lastly, avoid setting up the first element of rq_vec as a zero-
length buffer. This happens with an RDMA transport when a normal
Read chunk is present because the data payload is in rq_arg's
page list (none of it is in the head buffer).

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
fs/nfsd/nfs3proc.c
fs/nfsd/nfs3xdr.c
fs/nfsd/nfsproc.c
fs/nfsd/nfsxdr.c
fs/nfsd/xdr.h
fs/nfsd/xdr3.h
include/linux/sunrpc/svc.h
net/sunrpc/svc.c

index 1d0ce3c57d939d6de10418fe12cf4cd217d392e8..2dd95ebf49354d03d2acf9dec5e9109b55c09bad 100644 (file)
@@ -192,6 +192,7 @@ nfsd3_proc_write(struct svc_rqst *rqstp)
        struct nfsd3_writeres *resp = rqstp->rq_resp;
        __be32  nfserr;
        unsigned long cnt = argp->len;
+       unsigned int nvecs;
 
        dprintk("nfsd: WRITE(3)    %s %d bytes at %Lu%s\n",
                                SVCFH_fmt(&argp->fh),
@@ -201,9 +202,12 @@ nfsd3_proc_write(struct svc_rqst *rqstp)
 
        fh_copy(&resp->fh, &argp->fh);
        resp->committed = argp->stable;
+       nvecs = svc_fill_write_vector(rqstp, &argp->first, cnt);
+       if (!nvecs)
+               RETURN_STATUS(nfserr_io);
        nfserr = nfsd_write(rqstp, &resp->fh, argp->offset,
-                               rqstp->rq_vec, argp->vlen,
-                               &cnt, resp->committed);
+                           rqstp->rq_vec, nvecs, &cnt,
+                           resp->committed);
        resp->count = cnt;
        RETURN_STATUS(nfserr);
 }
index 1a70581e1cb2b1222a35b8169a7af804348beec7..e19fc5d8bcb5996b4f98d7da7a1299f263cbc0eb 100644 (file)
@@ -391,7 +391,7 @@ int
 nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p)
 {
        struct nfsd3_writeargs *args = rqstp->rq_argp;
-       unsigned int len, v, hdr, dlen;
+       unsigned int len, hdr, dlen;
        u32 max_blocksize = svc_max_payload(rqstp);
        struct kvec *head = rqstp->rq_arg.head;
        struct kvec *tail = rqstp->rq_arg.tail;
@@ -433,17 +433,9 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p)
                args->count = max_blocksize;
                len = args->len = max_blocksize;
        }
-       rqstp->rq_vec[0].iov_base = (void*)p;
-       rqstp->rq_vec[0].iov_len = head->iov_len - hdr;
-       v = 0;
-       while (len > rqstp->rq_vec[v].iov_len) {
-               len -= rqstp->rq_vec[v].iov_len;
-               v++;
-               rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_pages[v]);
-               rqstp->rq_vec[v].iov_len = PAGE_SIZE;
-       }
-       rqstp->rq_vec[v].iov_len = len;
-       args->vlen = v + 1;
+
+       args->first.iov_base = (void *)p;
+       args->first.iov_len = head->iov_len - hdr;
        return 1;
 }
 
index 43c0419b8ddbd0ead78fabce06be943ace1439ae..1995ea6bfd2baae21e230edaba4ffb93798dd5c4 100644 (file)
@@ -212,13 +212,18 @@ nfsd_proc_write(struct svc_rqst *rqstp)
        struct nfsd_attrstat *resp = rqstp->rq_resp;
        __be32  nfserr;
        unsigned long cnt = argp->len;
+       unsigned int nvecs;
 
        dprintk("nfsd: WRITE    %s %d bytes at %d\n",
                SVCFH_fmt(&argp->fh),
                argp->len, argp->offset);
 
-       nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh), argp->offset,
-                               rqstp->rq_vec, argp->vlen, &cnt, NFS_DATA_SYNC);
+       nvecs = svc_fill_write_vector(rqstp, &argp->first, cnt);
+       if (!nvecs)
+               return nfserr_io;
+       nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh),
+                           argp->offset, rqstp->rq_vec, nvecs,
+                           &cnt, NFS_DATA_SYNC);
        return nfsd_return_attrs(nfserr, resp);
 }
 
index 79b6064f8977a7b6b7b7202b35c74c9e67ce75ac..db24ae8b67e021e71da10f65457230b3c85ba319 100644 (file)
@@ -287,7 +287,6 @@ nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p)
        struct nfsd_writeargs *args = rqstp->rq_argp;
        unsigned int len, hdr, dlen;
        struct kvec *head = rqstp->rq_arg.head;
-       int v;
 
        p = decode_fh(p, &args->fh);
        if (!p)
@@ -323,17 +322,8 @@ nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p)
        if (dlen < XDR_QUADLEN(len)*4)
                return 0;
 
-       rqstp->rq_vec[0].iov_base = (void*)p;
-       rqstp->rq_vec[0].iov_len = head->iov_len - hdr;
-       v = 0;
-       while (len > rqstp->rq_vec[v].iov_len) {
-               len -= rqstp->rq_vec[v].iov_len;
-               v++;
-               rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_pages[v]);
-               rqstp->rq_vec[v].iov_len = PAGE_SIZE;
-       }
-       rqstp->rq_vec[v].iov_len = len;
-       args->vlen = v + 1;
+       args->first.iov_base = (void *)p;
+       args->first.iov_len = head->iov_len - hdr;
        return 1;
 }
 
index 2f4f22e6b8cbe9b79ac513b306880eb05e569938..a765c414015e0ce3c606c7a110806592d3cb31e8 100644 (file)
@@ -34,7 +34,7 @@ struct nfsd_writeargs {
        svc_fh                  fh;
        __u32                   offset;
        int                     len;
-       int                     vlen;
+       struct kvec             first;
 };
 
 struct nfsd_createargs {
index 056bf8a7364e22d75a47e646f206a1e5415df6e1..deccf7f691e9703c0ea7624d074db141a9e4bd12 100644 (file)
@@ -41,7 +41,7 @@ struct nfsd3_writeargs {
        __u32                   count;
        int                     stable;
        __u32                   len;
-       int                     vlen;
+       struct kvec             first;
 };
 
 struct nfsd3_createargs {
index dc4c009deec11d587b506554d7007100e741c944..fb3fcacc1e98c4fedd730a96cc3598be02dc1a5d 100644 (file)
@@ -495,6 +495,8 @@ void                   svc_wake_up(struct svc_serv *);
 void              svc_reserve(struct svc_rqst *rqstp, int space);
 struct svc_pool *  svc_pool_for_cpu(struct svc_serv *serv, int cpu);
 char *            svc_print_addr(struct svc_rqst *, char *, size_t);
+unsigned int      svc_fill_write_vector(struct svc_rqst *rqstp,
+                                        struct kvec *first, size_t total);
 
 #define        RPC_MAX_ADDRBUFLEN      (63U)
 
index f19987f5d3b5b7dc79de60cafa49bdca26e41ac8..a155e2de19aa7628a6e6f921c6e41eef132c3160 100644 (file)
@@ -1533,3 +1533,45 @@ u32 svc_max_payload(const struct svc_rqst *rqstp)
        return max;
 }
 EXPORT_SYMBOL_GPL(svc_max_payload);
+
+/**
+ * svc_fill_write_vector - Construct data argument for VFS write call
+ * @rqstp: svc_rqst to operate on
+ * @first: buffer containing first section of write payload
+ * @total: total number of bytes of write payload
+ *
+ * Returns the number of elements populated in the data argument array.
+ */
+unsigned int svc_fill_write_vector(struct svc_rqst *rqstp, struct kvec *first,
+                                  size_t total)
+{
+       struct kvec *vec = rqstp->rq_vec;
+       struct page **pages;
+       unsigned int i;
+
+       /* Some types of transport can present the write payload
+        * entirely in rq_arg.pages. In this case, @first is empty.
+        */
+       i = 0;
+       if (first->iov_len) {
+               vec[i].iov_base = first->iov_base;
+               vec[i].iov_len = min_t(size_t, total, first->iov_len);
+               total -= vec[i].iov_len;
+               ++i;
+       }
+
+       WARN_ON_ONCE(rqstp->rq_arg.page_base != 0);
+       pages = rqstp->rq_arg.pages;
+       while (total) {
+               vec[i].iov_base = page_address(*pages);
+               vec[i].iov_len = min_t(size_t, total, PAGE_SIZE);
+               total -= vec[i].iov_len;
+               ++i;
+
+               ++pages;
+       }
+
+       WARN_ON_ONCE(i > ARRAY_SIZE(rqstp->rq_vec));
+       return i;
+}
+EXPORT_SYMBOL_GPL(svc_fill_write_vector);