NFSD: Clean up legacy NFS WRITE argument XDR decoders
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>
This commit is contained in:
parent
fff4080b2f
commit
8154ef2776
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ struct nfsd_writeargs {
|
|||
svc_fh fh;
|
||||
__u32 offset;
|
||||
int len;
|
||||
int vlen;
|
||||
struct kvec first;
|
||||
};
|
||||
|
||||
struct nfsd_createargs {
|
||||
|
|
|
@ -41,7 +41,7 @@ struct nfsd3_writeargs {
|
|||
__u32 count;
|
||||
int stable;
|
||||
__u32 len;
|
||||
int vlen;
|
||||
struct kvec first;
|
||||
};
|
||||
|
||||
struct nfsd3_createargs {
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue