Commit dcffe12e authored by Neil Brown's avatar Neil Brown Committed by Linus Torvalds

[PATCH] kNFSd - 2 of 2 - Change NFSv4 reply encoding to cope with multiple pages.

This allows NFSv4 responses to cover move than one page.  There are
still limits though.  There can be at most one 'data' response which
includes READ, READLINK, READDIR.  For these responses, the interesting
data goes in a separate page or, for READ, list of pages.

All responses before the 'data' response must fit in one page, and all
responses after it must also fit in one (separate) page.
parent 89fc0a31
...@@ -566,11 +566,14 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, ...@@ -566,11 +566,14 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
fh_init(&current_fh, NFS4_FHSIZE); fh_init(&current_fh, NFS4_FHSIZE);
fh_init(&save_fh, NFS4_FHSIZE); fh_init(&save_fh, NFS4_FHSIZE);
resp->p = rqstp->rq_resbuf.buf + 3 + XDR_QUADLEN(args->taglen); resp->xbuf = &rqstp->rq_res;
resp->end = rqstp->rq_resbuf.base + rqstp->rq_resbuf.buflen; resp->p = rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len;
resp->p += 3 + XDR_QUADLEN(args->taglen);
resp->end = rqstp->rq_res.head[0].iov_base + PAGE_SIZE;
resp->taglen = args->taglen; resp->taglen = args->taglen;
resp->tag = args->tag; resp->tag = args->tag;
resp->opcnt = 0; resp->opcnt = 0;
resp->rqstp = rqstp;
/* /*
* According to RFC3010, this takes precedence over all other errors. * According to RFC3010, this takes precedence over all other errors.
...@@ -596,6 +599,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, ...@@ -596,6 +599,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
* failed response to the next operation. If we don't * failed response to the next operation. If we don't
* have enough room, fail with ERR_RESOURCE. * have enough room, fail with ERR_RESOURCE.
*/ */
/* FIXME - is slack_space *really* words, or bytes??? - neilb */
slack_space = (char *)resp->end - (char *)resp->p; slack_space = (char *)resp->end - (char *)resp->p;
if (slack_space < COMPOUND_SLACK_SPACE + COMPOUND_ERR_SLACK_SPACE) { if (slack_space < COMPOUND_SLACK_SPACE + COMPOUND_ERR_SLACK_SPACE) {
BUG_ON(slack_space < COMPOUND_ERR_SLACK_SPACE); BUG_ON(slack_space < COMPOUND_ERR_SLACK_SPACE);
......
...@@ -1689,27 +1689,52 @@ static int ...@@ -1689,27 +1689,52 @@ static int
nfsd4_encode_read(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_read *read) nfsd4_encode_read(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_read *read)
{ {
u32 eof; u32 eof;
unsigned long maxcount; int v, pn;
unsigned long maxcount, len;
ENCODE_HEAD; ENCODE_HEAD;
if (nfserr) if (nfserr)
return nfserr; return nfserr;
if (resp->xbuf->page_len)
return nfserr_resource;
maxcount = (char *)resp->end - (char *)resp->p - COMPOUND_ERR_SLACK_SPACE - 8; RESERVE_SPACE(8); /* eof flag and byte count */
maxcount = NFSSVC_MAXBLKSIZE;
if (maxcount > read->rd_length) if (maxcount > read->rd_length)
maxcount = read->rd_length; maxcount = read->rd_length;
RESERVE_SPACE(maxcount + 8);
len = maxcount;
v = 0;
while (len > 0) {
pn = resp->rqstp->rq_resused;
svc_take_page(resp->rqstp);
read->rd_iov[v].iov_base = page_address(resp->rqstp->rq_respages[pn]);
read->rd_iov[v].iov_len = len < PAGE_SIZE ? len : PAGE_SIZE;
v++;
len -= PAGE_SIZE;
}
read->rd_vlen = v;
nfserr = nfsd_read(read->rd_rqstp, read->rd_fhp, nfserr = nfsd_read(read->rd_rqstp, read->rd_fhp,
read->rd_offset, (char *)p + 8, &maxcount); read->rd_offset,
read->rd_iov, read->rd_vlen,
&maxcount);
if (nfserr) if (nfserr)
return nfserr; return nfserr;
eof = (read->rd_offset + maxcount >= read->rd_fhp->fh_dentry->d_inode->i_size); eof = (read->rd_offset + maxcount >= read->rd_fhp->fh_dentry->d_inode->i_size);
WRITE32(eof); WRITE32(eof);
WRITE32(maxcount); WRITE32(maxcount);
p += XDR_QUADLEN(maxcount);
ADJUST_ARGS(); ADJUST_ARGS();
resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base;
resp->p = resp->xbuf->tail[0].iov_base;
resp->xbuf->page_len = maxcount;
if (maxcount&3) {
*(resp->p)++ = 0;
resp->xbuf->tail[0].iov_base += maxcount&3;
resp->xbuf->tail[0].iov_len = 4 - (maxcount&3);
}
return 0; return 0;
} }
...@@ -1717,13 +1742,24 @@ static int ...@@ -1717,13 +1742,24 @@ static int
nfsd4_encode_readlink(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_readlink *readlink) nfsd4_encode_readlink(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_readlink *readlink)
{ {
int maxcount; int maxcount;
char *page;
ENCODE_HEAD; ENCODE_HEAD;
if (nfserr) if (nfserr)
return nfserr; return nfserr;
if (resp->xbuf->page_len)
return nfserr_resource;
maxcount = (char *)resp->end - (char *)resp->p - COMPOUND_ERR_SLACK_SPACE - 4; svc_take_page(resp->rqstp);
RESERVE_SPACE(maxcount + 4); page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]);
svc_take_page(resp->rqstp);
resp->xbuf->tail[0].iov_base =
page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]);
resp->xbuf->tail[0].iov_len = 0;
maxcount = PAGE_SIZE;
RESERVE_SPACE(4);
/* /*
* XXX: By default, the ->readlink() VFS op will truncate symlinks * XXX: By default, the ->readlink() VFS op will truncate symlinks
...@@ -1731,13 +1767,20 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_r ...@@ -1731,13 +1767,20 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_r
* not, one easy fix is: if ->readlink() precisely fills the buffer, * not, one easy fix is: if ->readlink() precisely fills the buffer,
* assume that truncation occured, and return NFS4ERR_RESOURCE. * assume that truncation occured, and return NFS4ERR_RESOURCE.
*/ */
nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp, (char *)p + 4, &maxcount); nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp, page, &maxcount);
if (nfserr) if (nfserr)
return nfserr; return nfserr;
WRITE32(maxcount); WRITE32(maxcount);
p += XDR_QUADLEN(maxcount);
ADJUST_ARGS(); ADJUST_ARGS();
resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base;
resp->p = resp->xbuf->tail[0].iov_base;
resp->xbuf->page_len = maxcount;
if (maxcount&3) {
*(resp->p)++ = 0;
resp->xbuf->tail[0].iov_base += maxcount&3;
resp->xbuf->tail[0].iov_len = 4 - (maxcount&3);
}
return 0; return 0;
} }
...@@ -1746,14 +1789,22 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re ...@@ -1746,14 +1789,22 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re
{ {
int maxcount; int maxcount;
loff_t offset; loff_t offset;
u32 *page;
ENCODE_HEAD; ENCODE_HEAD;
if (nfserr) if (nfserr)
return nfserr; return nfserr;
if (resp->xbuf->page_len)
return nfserr_resource;
RESERVE_SPACE(16); RESERVE_SPACE(8); /* verifier */
/* XXX: Following NFSv3, we ignore the READDIR verifier for now. */
WRITE32(0);
WRITE32(0);
ADJUST_ARGS();
maxcount = (char *)resp->end - (char *)p - COMPOUND_ERR_SLACK_SPACE; maxcount = PAGE_SIZE;
if (maxcount > readdir->rd_maxcount) if (maxcount > readdir->rd_maxcount)
maxcount = readdir->rd_maxcount; maxcount = readdir->rd_maxcount;
...@@ -1766,13 +1817,11 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re ...@@ -1766,13 +1817,11 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re
if (maxcount < 0) if (maxcount < 0)
return nfserr_readdir_nospc; return nfserr_readdir_nospc;
/* XXX: Following NFSv3, we ignore the READDIR verifier for now. */ svc_take_page(resp->rqstp);
WRITE32(0); page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]);
WRITE32(0);
readdir->common.err = 0; readdir->common.err = 0;
readdir->buflen = maxcount; readdir->buflen = maxcount;
readdir->buffer = p; readdir->buffer = page;
readdir->offset = NULL; readdir->offset = NULL;
offset = readdir->rd_cookie; offset = readdir->rd_cookie;
...@@ -1781,7 +1830,7 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re ...@@ -1781,7 +1830,7 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re
&readdir->common, nfsd4_encode_dirent); &readdir->common, nfsd4_encode_dirent);
if (nfserr == nfs_ok && if (nfserr == nfs_ok &&
readdir->common.err == nfserr_readdir_nospc && readdir->common.err == nfserr_readdir_nospc &&
readdir->buffer == p) readdir->buffer == page)
nfserr = nfserr_readdir_nospc; nfserr = nfserr_readdir_nospc;
if (!nfserr) { if (!nfserr) {
if (readdir->offset) if (readdir->offset)
...@@ -1790,7 +1839,7 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re ...@@ -1790,7 +1839,7 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re
p = readdir->buffer; p = readdir->buffer;
*p++ = 0; /* no more entries */ *p++ = 0; /* no more entries */
*p++ = htonl(readdir->common.err == nfserr_eof); *p++ = htonl(readdir->common.err == nfserr_eof);
ADJUST_ARGS(); resp->xbuf->page_len = ((char*)p) - (char*)page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]);
} }
return nfserr; return nfserr;
} }
...@@ -1971,17 +2020,6 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op) ...@@ -1971,17 +2020,6 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
* END OF "GENERIC" ENCODE ROUTINES. * END OF "GENERIC" ENCODE ROUTINES.
*/ */
static inline int
xdr_ressize_check(struct svc_rqst *rqstp, u32 *p)
{
struct svc_buf *buf = &rqstp->rq_resbuf;
buf->len = p - buf->base;
dprintk("nfsd: ressize_check p %p base %p len %d\n",
p, buf->base, buf->buflen);
return (buf->len <= buf->buflen);
}
int int
nfs4svc_encode_voidres(struct svc_rqst *rqstp, u32 *p, void *dummy) nfs4svc_encode_voidres(struct svc_rqst *rqstp, u32 *p, void *dummy)
{ {
...@@ -1996,7 +2034,7 @@ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compoun ...@@ -1996,7 +2034,7 @@ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compoun
args->p = p; args->p = p;
args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len; args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len;
args->pagelist = rqstp->rq_arg.pages; args->pagelist = rqstp->rq_arg.pages;
args->pagelen = rqstp->rq_args.page_len; args->pagelen = rqstp->rq_arg.page_len;
args->tmpp = NULL; args->tmpp = NULL;
args->to_free = NULL; args->to_free = NULL;
args->ops = args->iops; args->ops = args->iops;
...@@ -2027,12 +2065,18 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compound ...@@ -2027,12 +2065,18 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compound
/* /*
* All that remains is to write the tag and operation count... * All that remains is to write the tag and operation count...
*/ */
struct iovec *iov;
*p++ = htonl(resp->taglen); *p++ = htonl(resp->taglen);
memcpy(p, resp->tag, resp->taglen); memcpy(p, resp->tag, resp->taglen);
p += XDR_QUADLEN(resp->taglen); p += XDR_QUADLEN(resp->taglen);
*p++ = htonl(resp->opcnt); *p++ = htonl(resp->opcnt);
BUG_ON(!xdr_ressize_check(rqstp, resp->p)); if (rqstp->rq_res.page_len)
iov = &rqstp->rq_res.tail[0];
else
iov = &rqstp->rq_res.head[0];
iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base;
BUG_ON(iov->iov_len > PAGE_SIZE);
return 1; return 1;
} }
......
...@@ -173,6 +173,9 @@ struct nfsd4_read { ...@@ -173,6 +173,9 @@ struct nfsd4_read {
stateid_t rd_stateid; /* request */ stateid_t rd_stateid; /* request */
u64 rd_offset; /* request */ u64 rd_offset; /* request */
u32 rd_length; /* request */ u32 rd_length; /* request */
struct iovec rd_iov[RPCSVC_MAXPAGES];
int rd_vlen;
struct svc_rqst *rd_rqstp; /* response */ struct svc_rqst *rd_rqstp; /* response */
struct svc_fh * rd_fhp; /* response */ struct svc_fh * rd_fhp; /* response */
}; };
...@@ -311,6 +314,8 @@ struct nfsd4_compoundres { ...@@ -311,6 +314,8 @@ struct nfsd4_compoundres {
/* scratch variables for XDR encode */ /* scratch variables for XDR encode */
u32 * p; u32 * p;
u32 * end; u32 * end;
struct xdr_buf * xbuf;
struct svc_rqst * rqstp;
u32 taglen; u32 taglen;
char * tag; char * tag;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment