From: "J. Bruce Fields" <bfields@xxxxxxxxxx> Rewrite readdir encoding using the new xdr primitives. This allows us to return more than 4k of data in a readdir reply, improving performance on large directories. Signed-off-by: J. Bruce Fields <bfields@xxxxxxxxxx> --- fs/nfsd/nfs4xdr.c | 157 +++++++++++++++++++++++++++++------------------------- fs/nfsd/xdr4.h | 5 +- 2 files changed, 85 insertions(+), 77 deletions(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index a378435..84ad067 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1870,6 +1870,11 @@ static void write64(struct svcxdr_ptr *ptr, u64 n) writemem(ptr, i, sizeof(i)); } +static void write_nfserr(struct svcxdr_ptr *ptr, __be32 err) +{ + writemem(ptr, &err, sizeof(err)); +} + static bool write_opaque(struct svcxdr_stream *xdr, const void *data, int len) { struct svcxdr_ptr ptr; @@ -2672,7 +2677,7 @@ static inline int attributes_need_mount(u32 *bmval) static __be32 nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd, - const char *name, int namlen, __be32 **p, int buflen) + const char *name, int namlen, struct svcxdr_stream *xdr) { struct svc_export *exp = cd->rd_fhp->fh_export; struct dentry *dentry; @@ -2724,7 +2729,7 @@ nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd, } out_encode: - nfserr = nfsd4_encode_fattr_to_buffer(p, buflen, NULL, exp, dentry, cd->rd_bmval, + nfserr = nfsd4_encode_fattr(xdr, NULL, exp, dentry, cd->rd_bmval, cd->rd_rqstp, ignore_crossmnt); out_put: dput(dentry); @@ -2732,18 +2737,20 @@ out_put: return nfserr; } -static __be32 * -nfsd4_encode_rdattr_error(__be32 *p, int buflen, __be32 nfserr) +static bool +nfsd4_encode_rdattr_error(struct svcxdr_stream *xdr, __be32 nfserr) { - if (buflen < 6) - return NULL; - *p++ = htonl(2); - *p++ = htonl(FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */ - *p++ = htonl(0); /* bmval1 */ + struct svcxdr_ptr ptr; - *p++ = htonl(4); /* attribute length */ - *p++ = nfserr; /* no htonl */ - return p; + if (!svcxdr_reserve_space(xdr, &ptr, 5*4)) + return false; + write32(&ptr, 2); + write32(&ptr, FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */ + write32(&ptr, 0); /* bmval1 */ + + write32(&ptr, 4); /* attribute length */ + write_nfserr(&ptr, nfserr); + return true; } static int @@ -2752,9 +2759,10 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen, { struct readdir_cd *ccd = ccdv; struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common); - int buflen; - __be32 *p = cd->buffer; - __be32 *cookiep; + struct svcxdr_stream *xdr = cd->xdr; + struct svcxdr_ptr start = xdr->ptr; + struct svcxdr_ptr cookiep; + struct svcxdr_ptr ptr; __be32 nfserr = nfserr_toosmall; /* In nfsv4, "." and ".." never make it onto the wire.. */ @@ -2763,27 +2771,30 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen, return 0; } - if (cd->offset) - xdr_encode_hyper(cd->offset, (u64) offset); + if (cd->offset.p) { + ptr = cd->offset; + write64(&ptr, (u64)offset); + } - buflen = cd->buflen - 4 - XDR_QUADLEN(namlen); - if (buflen < 0) + if (!svcxdr_reserve_space(xdr, &ptr, 3*4)) goto fail; + write32(&ptr, 1); /* mark entry present */ + cookiep = ptr; + write64(&ptr, NFS_OFFSET_MAX); /* offset of next entry */ - *p++ = xdr_one; /* mark entry present */ - cookiep = p; - p = xdr_encode_hyper(p, NFS_OFFSET_MAX); /* offset of next entry */ - p = xdr_encode_array(p, name, namlen); /* name length & name */ + if (!write_opaque(xdr, name, namlen)) + goto fail; - nfserr = nfsd4_encode_dirent_fattr(cd, name, namlen, &p, buflen); + nfserr = nfsd4_encode_dirent_fattr(cd, name, namlen, xdr); switch (nfserr) { case nfs_ok: break; case nfserr_resource: nfserr = nfserr_toosmall; goto fail; - case nfserr_noent: - goto skip_entry; + case nfserr_noent: /* skip entry: */ + svcxdr_reset(xdr, &start); + goto out; default: /* * If the client requested the RDATTR_ERROR attribute, @@ -2794,19 +2805,17 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen, */ if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR)) goto fail; - p = nfsd4_encode_rdattr_error(p, buflen, nfserr); - if (p == NULL) { + if (!nfsd4_encode_rdattr_error(xdr, nfserr)) { nfserr = nfserr_toosmall; goto fail; } } - cd->buflen -= (p - cd->buffer); - cd->buffer = p; cd->offset = cookiep; -skip_entry: +out: cd->common.err = nfs_ok; return 0; fail: + svcxdr_reset(xdr, &start); cd->common.err = nfserr; return -EINVAL; } @@ -3231,46 +3240,45 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4 { int maxcount; loff_t offset; - __be32 *page, *savep, *tailbase; - __be32 *p; + struct svcxdr_stream xdr; + struct svcxdr_ptr p; + struct svcxdr_ptr savep; if (nfserr) return nfserr; - if (resp->xbuf->page_len) - return nfserr_resource; if (!*resp->rqstp->rq_next_page) return nfserr_resource; - RESERVE_SPACE(NFS4_VERIFIER_SIZE); - savep = p; - - /* XXX: Following NFSv3, we ignore the READDIR verifier for now. */ - WRITE32(0); - WRITE32(0); - ADJUST_ARGS(); - resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base; - tailbase = p; - - maxcount = PAGE_SIZE; + nfserr = svcxdr_stream_init_from_resp(&xdr, resp); + if (nfserr) + return nfserr; + maxcount = svc_max_payload(resp->rqstp); if (maxcount > readdir->rd_maxcount) maxcount = readdir->rd_maxcount; - /* - * Convert from bytes to words, account for the two words already - * written, make sure to leave two words at the end for the next - * pointer and eof field. + * Allow space for the two words (entry follows, eof) at the + * end. Note we generally assume rd_maxcount is at fault. + * Which is the most likely case. If someone sent a readdir at + * the end of an extremely long compound this might result in us + * returning the wrong error: */ - maxcount = (maxcount >> 2) - 4; - if (maxcount < 0) { - nfserr = nfserr_toosmall; + maxcount -= 8; + nfserr = nfserr_toosmall; + if (maxcount < 0) goto err_no_verf; - } - page = page_address(*(resp->rqstp->rq_next_page++)); + xdr.maxbytes = maxcount; + if (!svcxdr_reserve_space(&xdr, &p, 8)) + goto err_no_verf; + savep = p; + + /* XXX: Following NFSv3, we ignore the READDIR verifier for now. */ + write32(&p, 0); + write32(&p, 0); + + readdir->xdr = &xdr; readdir->common.err = 0; - readdir->buflen = maxcount; - readdir->buffer = page; - readdir->offset = NULL; + readdir->offset.p = NULL; offset = readdir->rd_cookie; nfserr = nfsd_readdir(readdir->rd_rqstp, readdir->rd_fhp, @@ -3278,30 +3286,31 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4 &readdir->common, nfsd4_encode_dirent); if (nfserr == nfs_ok && readdir->common.err == nfserr_toosmall && - readdir->buffer == page) + xdr.ptr.p == xdr.last_commit.p + 2) /* We made no progress at all */ nfserr = nfserr_toosmall; if (nfserr) goto err_no_verf; - if (readdir->offset) - xdr_encode_hyper(readdir->offset, offset); - - p = readdir->buffer; - *p++ = 0; /* no more entries */ - *p++ = htonl(readdir->common.err == nfserr_eof); - resp->xbuf->page_len = ((char*)p) - - (char*)page_address(*(resp->rqstp->rq_next_page-1)); + if (readdir->offset.p) { + p = readdir->offset; + write64(&p, offset); + } + xdr.maxbytes += 8; + if (!svcxdr_reserve_space(&xdr, &p, 8)) { + WARN_ON_ONCE(1); + goto err_no_verf; + } + write32(&p, 0); /* no more entries */ + write32(&p, readdir->common.err == nfserr_eof); - /* Use rest of head for padding and remaining ops: */ - resp->xbuf->tail[0].iov_base = tailbase; - resp->xbuf->tail[0].iov_len = 0; - resp->p = resp->xbuf->tail[0].iov_base; - resp->end = resp->p + (PAGE_SIZE - resp->xbuf->head[0].iov_len)/4; + svcxdr_stream_update_resp(&xdr, resp); return 0; err_no_verf: - p = savep; - ADJUST_ARGS(); + /* + * returning without svcxdr_stream_update_resp will leave + * xdr_buf unchanged, as if we'd encoded nothing: + */ return nfserr; } diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 36b8c2c..9c58f05 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -299,9 +299,8 @@ struct nfsd4_readdir { struct svc_fh * rd_fhp; /* response */ struct readdir_cd common; - __be32 * buffer; - int buflen; - __be32 * offset; + struct svcxdr_stream *xdr; + struct svcxdr_ptr offset; }; struct nfsd4_release_lockowner { -- 1.7.11.7 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html