Commit f6455f35 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] kNFSd: return more than one page of directory entries.

From: NeilBrown <neilb@cse.unsw.edu.au>

From: Bruce Allan <bwa@us.ibm.com>

This requires checking if the entry will fit in the current page and, if it
doesn't, encoding it into the next page, and then copying it back to lie
across the boundary between two pages.
parent 255e9f80
...@@ -456,29 +456,43 @@ static int ...@@ -456,29 +456,43 @@ static int
nfsd3_proc_readdirplus(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp, nfsd3_proc_readdirplus(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp,
struct nfsd3_readdirres *resp) struct nfsd3_readdirres *resp)
{ {
int nfserr, count; int nfserr, count = 0;
loff_t offset; loff_t offset;
int i;
caddr_t page_addr = NULL;
dprintk("nfsd: READDIR+(3) %s %d bytes at %d\n", dprintk("nfsd: READDIR+(3) %s %d bytes at %d\n",
SVCFH_fmt(&argp->fh), SVCFH_fmt(&argp->fh),
argp->count, (u32) argp->cookie); argp->count, (u32) argp->cookie);
/* Make sure we've room for the NULL ptr & eof flag, and shrink to /* Convert byte count to number of words (i.e. >> 2),
* client read size */ * and reserve room for the NULL ptr & eof flag (-2 words) */
count = (argp->count >> 2) - 2; resp->count = (argp->count >> 2) - 2;
/* Read directory and encode entries on the fly */ /* Read directory and encode entries on the fly */
fh_copy(&resp->fh, &argp->fh); fh_copy(&resp->fh, &argp->fh);
resp->buflen = count;
resp->common.err = nfs_ok; resp->common.err = nfs_ok;
resp->buffer = argp->buffer; resp->buffer = argp->buffer;
resp->buflen = resp->count;
resp->rqstp = rqstp; resp->rqstp = rqstp;
offset = argp->cookie; offset = argp->cookie;
nfserr = nfsd_readdir(rqstp, &resp->fh, &offset, nfserr = nfsd_readdir(rqstp, &resp->fh,
&resp->common, nfs3svc_encode_entry_plus); &offset,
&resp->common,
nfs3svc_encode_entry_plus);
memcpy(resp->verf, argp->verf, 8); memcpy(resp->verf, argp->verf, 8);
resp->count = resp->buffer - argp->buffer; for (i=1; i<rqstp->rq_resused ; i++) {
page_addr = page_address(rqstp->rq_respages[i]);
if (((caddr_t)resp->buffer >= page_addr) &&
((caddr_t)resp->buffer < page_addr + PAGE_SIZE)) {
count += (caddr_t)resp->buffer - page_addr;
break;
}
count += PAGE_SIZE;
}
resp->count = count >> 2;
if (resp->offset) if (resp->offset)
xdr_encode_hyper(resp->offset, offset); xdr_encode_hyper(resp->offset, offset);
......
...@@ -560,6 +560,8 @@ int ...@@ -560,6 +560,8 @@ int
nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, u32 *p, nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, u32 *p,
struct nfsd3_readdirargs *args) struct nfsd3_readdirargs *args)
{ {
int len, pn;
if (!(p = decode_fh(p, &args->fh))) if (!(p = decode_fh(p, &args->fh)))
return 0; return 0;
p = xdr_decode_hyper(p, &args->cookie); p = xdr_decode_hyper(p, &args->cookie);
...@@ -567,11 +569,17 @@ nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, u32 *p, ...@@ -567,11 +569,17 @@ nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, u32 *p,
args->dircount = ntohl(*p++); args->dircount = ntohl(*p++);
args->count = ntohl(*p++); args->count = ntohl(*p++);
if (args->count > PAGE_SIZE) len = (args->count > NFSSVC_MAXBLKSIZE) ? NFSSVC_MAXBLKSIZE :
args->count = PAGE_SIZE; args->count;
args->count = len;
svc_take_page(rqstp); while (len > 0) {
args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused-1]); pn = rqstp->rq_resused;
svc_take_page(rqstp);
if (!args->buffer)
args->buffer = page_address(rqstp->rq_respages[pn]);
len -= PAGE_SIZE;
}
return xdr_argsize_check(rqstp, p); return xdr_argsize_check(rqstp, p);
} }
...@@ -754,12 +762,64 @@ nfs3svc_encode_readdirres(struct svc_rqst *rqstp, u32 *p, ...@@ -754,12 +762,64 @@ nfs3svc_encode_readdirres(struct svc_rqst *rqstp, u32 *p,
p = resp->buffer; p = resp->buffer;
*p++ = 0; /* no more entries */ *p++ = 0; /* no more entries */
*p++ = htonl(resp->common.err == nfserr_eof); *p++ = htonl(resp->common.err == nfserr_eof);
rqstp->rq_res.page_len = (((unsigned long)p-1) & ~PAGE_MASK)+1; rqstp->rq_res.page_len = (resp->count + 2) << 2;
return 1; return 1;
} else } else
return xdr_ressize_check(rqstp, p); return xdr_ressize_check(rqstp, p);
} }
static inline u32 *
encode_entry_baggage(struct nfsd3_readdirres *cd, u32 *p, const char *name,
int namlen, ino_t ino)
{
*p++ = xdr_one; /* mark entry present */
p = xdr_encode_hyper(p, ino); /* file id */
p = xdr_encode_array(p, name, namlen);/* name length & name */
cd->offset = p; /* remember pointer */
p = xdr_encode_hyper(p, NFS_OFFSET_MAX);/* offset of next entry */
return p;
}
static inline u32 *
encode_entryplus_baggage(struct nfsd3_readdirres *cd, u32 *p,
struct svc_fh *fhp)
{
p = encode_post_op_attr(cd->rqstp, p, fhp);
*p++ = xdr_one; /* yes, a file handle follows */
p = encode_fh(p, fhp);
fh_put(fhp);
return p;
}
static int
compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp,
const char *name, int namlen)
{
struct svc_export *exp;
struct dentry *dparent, *dchild;
dparent = cd->fh.fh_dentry;
exp = cd->fh.fh_export;
fh_init(fhp, NFS3_FHSIZE);
if (isdotent(name, namlen)) {
if (namlen == 2) {
dchild = dget_parent(dparent);
} else
dchild = dget(dparent);
} else
dchild = lookup_one_len(name, dparent, namlen);
if (IS_ERR(dchild))
return 1;
if (d_mountpoint(dchild))
return 1;
if (fh_compose(fhp, exp, dchild, &cd->fh) != 0 || !dchild->d_inode)
return 1;
return 0;
}
/* /*
* Encode a directory entry. This one works for both normal readdir * Encode a directory entry. This one works for both normal readdir
* and readdirplus. * and readdirplus.
...@@ -776,9 +836,14 @@ static int ...@@ -776,9 +836,14 @@ static int
encode_entry(struct readdir_cd *ccd, const char *name, encode_entry(struct readdir_cd *ccd, const char *name,
int namlen, off_t offset, ino_t ino, unsigned int d_type, int plus) int namlen, off_t offset, ino_t ino, unsigned int d_type, int plus)
{ {
struct nfsd3_readdirres *cd = container_of(ccd, struct nfsd3_readdirres, common); struct nfsd3_readdirres *cd = container_of(ccd, struct nfsd3_readdirres,
common);
u32 *p = cd->buffer; u32 *p = cd->buffer;
int slen, elen; caddr_t curr_page_addr = NULL;
int pn; /* current page number */
int slen; /* string (name) length */
int elen; /* estimated entry length in words */
int num_entry_words = 0; /* actual number of words */
if (cd->offset) if (cd->offset)
xdr_encode_hyper(cd->offset, (u64) offset); xdr_encode_hyper(cd->offset, (u64) offset);
...@@ -795,48 +860,93 @@ encode_entry(struct readdir_cd *ccd, const char *name, ...@@ -795,48 +860,93 @@ encode_entry(struct readdir_cd *ccd, const char *name,
slen = XDR_QUADLEN(namlen); slen = XDR_QUADLEN(namlen);
elen = slen + NFS3_ENTRY_BAGGAGE elen = slen + NFS3_ENTRY_BAGGAGE
+ (plus? NFS3_ENTRYPLUS_BAGGAGE : 0); + (plus? NFS3_ENTRYPLUS_BAGGAGE : 0);
if (cd->buflen < elen) { if (cd->buflen < elen) {
cd->common.err = nfserr_toosmall; cd->common.err = nfserr_toosmall;
return -EINVAL; return -EINVAL;
} }
*p++ = xdr_one; /* mark entry present */
p = xdr_encode_hyper(p, ino); /* file id */
p = xdr_encode_array(p, name, namlen);/* name length & name */
cd->offset = p; /* remember pointer */ /* determine which page in rq_respages[] we are currently filling */
p = xdr_encode_hyper(p, NFS_OFFSET_MAX); /* offset of next entry */ for (pn=1; pn < cd->rqstp->rq_resused; pn++) {
curr_page_addr = page_address(cd->rqstp->rq_respages[pn]);
/* throw in readdirplus baggage */ if (((caddr_t)cd->buffer >= curr_page_addr) &&
if (plus) { ((caddr_t)cd->buffer < curr_page_addr + PAGE_SIZE))
struct svc_fh fh; break;
struct svc_export *exp; }
struct dentry *dparent, *dchild;
if ((caddr_t)(cd->buffer + elen) < (curr_page_addr + PAGE_SIZE)) {
/* encode entry in current page */
p = encode_entry_baggage(cd, p, name, namlen, ino);
/* throw in readdirplus baggage */
if (plus) {
struct svc_fh fh;
dparent = cd->fh.fh_dentry; if (compose_entry_fh(cd, &fh, name, namlen) > 0)
exp = cd->fh.fh_export; goto noexec;
fh_init(&fh, NFS3_FHSIZE); p = encode_entryplus_baggage(cd, p, &fh);
if (isdotent(name, namlen)) { }
if (namlen == 2) { num_entry_words = p - cd->buffer;
dchild = dget_parent(dparent); } else if (cd->rqstp->rq_respages[pn+1] != NULL) {
/* temporarily encode entry into next page, then move back to
* current and next page in rq_respages[] */
u32 *p1, *tmp;
int len1, len2;
/* grab next page for temporary storage of entry */
p1 = tmp = page_address(cd->rqstp->rq_respages[pn+1]);
p1 = encode_entry_baggage(cd, p1, name, namlen, ino);
/* throw in readdirplus baggage */
if (plus) {
struct svc_fh fh;
if (compose_entry_fh(cd, &fh, name, namlen) > 0) {
/* zero out the filehandle */
*p1++ = 0;
*p1++ = 0;
} else } else
dchild = dget(dparent); p1 = encode_entryplus_baggage(cd, p1, &fh);
} else }
dchild = lookup_one_len(name, dparent,namlen);
if (IS_ERR(dchild)) /* determine entry word length and lengths to go in pages */
goto noexec; num_entry_words = p1 - tmp;
if (d_mountpoint(dchild)) len1 = curr_page_addr + PAGE_SIZE - (caddr_t)cd->buffer;
goto noexec; if ((num_entry_words << 2) <= len1) {
if (fh_compose(&fh, exp, dchild, &cd->fh) != 0 || !dchild->d_inode) /* the actual number of words in the entry is less
goto noexec; * than elen and can still fit in the current page
p = encode_post_op_attr(cd->rqstp, p, &fh); */
*p++ = xdr_one; /* yes, a file handle follows */ memmove(p, tmp, num_entry_words << 2);
p = encode_fh(p, &fh); p += num_entry_words;
fh_put(&fh);
/* update offset */
cd->offset = cd->buffer + (cd->offset - tmp);
} else {
len2 = (num_entry_words << 2) - len1;
/* move from temp page to current and next pages */
memmove(p, tmp, len1);
memmove(tmp, (caddr_t)tmp+len1, len2);
/* update offset */
if (((cd->offset - tmp) << 2) <= len1)
cd->offset = p + (cd->offset - tmp);
else
cd->offset -= len1 >> 2;
p = tmp + (len2 >> 2);
}
}
else {
cd->common.err = nfserr_toosmall;
return -EINVAL;
} }
out: out:
cd->buflen -= p - cd->buffer; cd->buflen -= num_entry_words;
cd->buffer = p; cd->buffer = p;
cd->common.err = nfs_ok; cd->common.err = nfs_ok;
return 0; return 0;
......
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