draft server xdr rewrite
Subsystems (2)
From: "J. Bruce Fields" <[email protected]>
The following patches fix two problems:
- The server can't support ACLs longer than 4k.
- The server can't return more than 4k of readdir data per rpc, limiting performance on large directories.
At fault in both cases is the compound xdr encoding, which is unable to deal with page boundaries.
Simplifying slightly:
I want to be able to write server code something like:
__be32 *p; p = reserve_space(12) return nfserr_resource; write64(&p, bignum); write32(&p, smallernum); p = reserve_space(xdr, &p, 4+namelen); if (!p) return nfserr_resource; write32(&p, namelen); writemem(&p, name); ...
and have page boundaries handled under the covers.
The following patches do that by replacing the __be32 * by approximately:
struct svcxdr_ptr {
__be32 *p;
struct page **next_page;
}with next_page a pointer into an array of available pages. The encoders can then traverse to the next page with something like:
ptr->p = page_address(*ptr->next_page++);
We also define a svcxdr_stream to keep track of various state. That and not wanting a struct as a return value makes the reserve_space above into:
if (!reserve_space(xdr, &p, 12)) return nfserr_resource; ...
Seem reasonable?
If so, I plan to convert the rest of the v4 encoding. (After revising these patches--e.g. readdir still has a few problems I want to fix. But this does pass the usual connectathon and pynfs testing already.)
--b.
J. Bruce Fields (7):
nfsd4: reserve space for integrity/privacy
nfsd4: simplify nfsd4_encode_fattr interface slightly
nfsd4: nfsd4_encode_fattr cleanup
nfsd4: encode_rdattr_error cleanup
nfsd4: rewrite xdr encoding of attributes
nfsd4: track maximum bytes
nfsd4: readdir encoding
fs/nfsd/nfs4proc.c | 17 +- fs/nfsd/nfs4xdr.c | 892 +++++++++++++++++++++++++++++++++-------------------- fs/nfsd/xdr4.h | 24 +- 3 files changed, 588 insertions(+), 345 deletions(-)
Signature · 1 line
1.7.11.7
PATCH 1/7 nfsd4: reserve space for integrity/privacy
From: "J. Bruce Fields" <[email protected]>
If a compound reply comes too close to filling up this first page, the gss code may end up dropping it, resulting in a client hang in the krb5i/krb5p case.
--- fs/nfsd/nfs4proc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
fs/nfsd/nfs4proc.c +7 -1
@@ -1202,7 +1202,13 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, resp->tagp = resp->p;/* reserve space for: taglen, tag, and opcnt */ resp->p +=2 +XDR_QUADLEN (args->taglen); - resp->end = rqstp->rq_res.head[0 ].iov_base +PAGE_SIZE ; +/* +* In the integrity and privacy cases we need to reserve a +* little extra space for a tail (see svcauth_gss_wrap_resp_priv +* and svcauth_gss_wrap_resp_integ): +*/ + resp->end = rqstp->rq_res.head[0 ].iov_base +PAGE_SIZE + -2 *RPC_MAX_AUTH_SIZE ; resp->taglen = args->taglen; resp->tag = args->tag; resp->opcnt =0 ;
Signature · 1 line
1.7.11.7
PATCH 2/7 nfsd4: simplify nfsd4_encode_fattr interface slightly
In reply to J. Bruce FieldsFrom: "J. Bruce Fields" <[email protected]>
It seems slightly simpler to make nfsd4_encode_fattr rather than its callers responsible for advancing the write pointer on success.
(Also: the count == 0 check in the verify case looks superfluous. Running out of buffer space is really the only reason fattr encoding should fail with eresource.)
--- fs/nfsd/nfs4proc.c | 7 ++++--- fs/nfsd/nfs4xdr.c | 21 ++++++++------------- fs/nfsd/xdr4.h | 2 +- 3 files changed, 13 insertions(+), 17 deletions(-)
fs/nfsd/nfs4proc.c +4 -3
@@ -993,14 +993,15 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,if (!buf)return nfserr_jukebox; + p = buf; status =nfsd4_encode_fattr (&cstate->current_fh, cstate->current_fh.fh_export, - cstate->current_fh.fh_dentry, buf, - &count, verify->ve_bmval, + cstate->current_fh.fh_dentry, &p, + count, verify->ve_bmval, rqstp,0 );/* this means that nfsd4_encode_fattr() ran out of space */ -if (status == nfserr_resource && count ==0 ) +if (status == nfserr_resource) status = nfserr_not_same;if (status) goto out_kfree;
fs/nfsd/nfs4xdr.c +8 -13
@@ -2006,12 +2006,11 @@ static int get_parent_attributes(struct svc_export *exp, struct kstat *stat)* Note: @fhp can be NULL; in this case, we might have to compose the filehandle * ourselves. * -* @countp is the buffer size in _words_; upon successful return this becomes -* replaced with the number of words written. +* countp is the buffer size in _words_ */ __be32 nfsd4_encode_fattr (struct svc_fh *fhp,struct svc_export *exp, -struct dentry *dentry,__be32 *buffer,int *countp,u32 *bmval, +struct dentry *dentry,__be32 **buffer,int count,u32 *bmval,struct svc_rqst *rqstp,int ignore_crossmnt) {u32 bmval0 = bmval[0 ];
@@ -2020,12 +2019,12 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,struct kstat stat;struct svc_fh tempfh;struct kstatfs statfs; -int buflen = *countp <<2 ; +int buflen = count <<2 ;__be32 *attrlenp;u32 dummy;u64 dummy64;u32 rdattr_err =0 ; -__be32 *p = buffer; +__be32 *p = *buffer;__be32 status;int err;int aclsupport =0 ;
@@ -2431,7 +2430,7 @@ out_acl: } *attrlenp =htonl ((char *)p - (char *)attrlenp -4 ); - *countp = p - buffer; + *buffer = p; status = nfs_ok; out:
@@ -2443,7 +2442,6 @@ out_nfserr: status =nfserrno (err); goto out; out_resource: - *countp =0 ; status = nfserr_resource; goto out; out_serverfault:
@@ -2462,7 +2460,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,__be32 **p,int buflen) {struct svc_export *exp = cd->rd_fhp->fh_export;struct dentry *dentry;
@@ -2568,10 +2566,9 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen, p =xdr_encode_hyper (p,NFS_OFFSET_MAX );/* offset of next entry */ p =xdr_encode_array (p, name, namlen);/* name length & name */ - nfserr =nfsd4_encode_dirent_fattr (cd, name, namlen, p, &buflen); + nfserr =nfsd4_encode_dirent_fattr (cd, name, namlen, &p, buflen);switch (nfserr) {case nfs_ok: - p += buflen;break ;case nfserr_resource: nfserr = nfserr_toosmall;
@@ -2698,10 +2695,8 @@ nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4 buflen = resp->end - resp->p - (COMPOUND_ERR_SLACK_SPACE >>2 ); nfserr =nfsd4_encode_fattr (fhp, fhp->fh_export, fhp->fh_dentry, - resp->p, &buflen, getattr->ga_bmval, + &resp->p, buflen, getattr->ga_bmval, resp->rqstp,0 ); -if (!nfserr) - resp->p += buflen;return nfserr; }
fs/nfsd/xdr4.h +1 -1
@@ -563,7 +563,7 @@ __be32 nfsd4_check_resp_size(struct nfsd4_compoundres *, u32);void nfsd4_encode_operation (struct nfsd4_compoundres *,struct nfsd4_op *);void nfsd4_encode_replay (struct nfsd4_compoundres *resp,struct nfsd4_op *op);__be32 nfsd4_encode_fattr (struct svc_fh *fhp,struct svc_export *exp, -struct dentry *dentry,__be32 *buffer,int *countp, +struct dentry *dentry,__be32 **buffer,int countp,u32 *bmval,struct svc_rqst *,int ignore_crossmnt); extern __be32 nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *,
Signature · 1 line
1.7.11.7
PATCH 3/7 nfsd4: nfsd4_encode_fattr cleanup
In reply to J. Bruce FieldsFrom: "J. Bruce Fields" <[email protected]>
Remove some pointless goto's.
--- fs/nfsd/nfs4xdr.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-)
fs/nfsd/nfs4xdr.c +4 -13
@@ -2129,8 +2129,10 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,if ((buflen -=4 ) <0 ) goto out_resource; dummy =nfs4_file_type (stat.mode); -if (dummy ==NF4BAD ) - goto out_serverfault; +if (dummy ==NF4BAD ) { + status = nfserr_serverfault; + goto out; + }WRITE32 (dummy); }if (bmval0 &FATTR4_WORD0_FH_EXPIRE_TYPE ) {
@@ -2226,8 +2228,6 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, status =nfsd4_encode_aclname (rqstp, ace->whotype, ace->who, ace->flag &NFS4_ACE_IDENTIFIER_GROUP , &p, &buflen); -if (status == nfserr_resource) - goto out_resource;if (status) goto out; }
@@ -2288,8 +2288,6 @@ out_acl: }if (bmval0 &FATTR4_WORD0_FS_LOCATIONS ) { status =nfsd4_encode_fs_locations (rqstp, exp, &p, &buflen); -if (status == nfserr_resource) - goto out_resource;if (status) goto out; }
@@ -2340,15 +2338,11 @@ out_acl: }if (bmval1 &FATTR4_WORD1_OWNER ) { status =nfsd4_encode_user (rqstp, stat.uid, &p, &buflen); -if (status == nfserr_resource) - goto out_resource;if (status) goto out; }if (bmval1 &FATTR4_WORD1_OWNER_GROUP ) { status =nfsd4_encode_group (rqstp, stat.gid, &p, &buflen); -if (status == nfserr_resource) - goto out_resource;if (status) goto out; }
@@ -2444,9 +2438,6 @@ out_nfserr: out_resource: status = nfserr_resource; goto out; -out_serverfault: - status = nfserr_serverfault; - goto out; }static inline int attributes_need_mount (u32 *bmval)
Signature · 1 line
1.7.11.7
PATCH 4/7 nfsd4: encode_rdattr_error cleanup
In reply to J. Bruce FieldsFrom: "J. Bruce Fields" <[email protected]>
There's a simpler way to write this.
--- fs/nfsd/nfs4xdr.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-)
fs/nfsd/nfs4xdr.c +1 -4
@@ -2514,17 +2514,14 @@ out_put:static __be32 *nfsd4_encode_rdattr_error (__be32 *p,int buflen,__be32 nfserr) { -__be32 *attrlenp; -if (buflen <6 )return NULL ; *p++ =htonl (2 ); *p++ =htonl (FATTR4_WORD0_RDATTR_ERROR );/* bmval0 */ *p++ =htonl (0 );/* bmval1 */ - attrlenp = p++; + *p++ =htonl (4 );/* attribute length */ *p++ = nfserr;/* no htonl */ - *attrlenp =htonl ((char *)p - (char *)attrlenp -4 );return p; }
Signature · 1 line
1.7.11.7
PATCH 5/7 nfsd4: rewrite xdr encoding of attributes
In reply to J. Bruce FieldsFrom: "J. Bruce Fields" <[email protected]>
Write xdr encoding primitives that can handle crossing page boundaries, and use them in nfsd4_encode_fattr.
The main practical advantage for now is that we can return arbitrarily large ACLs (well, up to our maximum rpc size). However, compounds with other operations following such a getattr may fail.
Eventually we plan to use the same xdr code through v4 at least.
--- fs/nfsd/nfs4proc.c | 6 +- fs/nfsd/nfs4xdr.c | 705 +++++++++++++++++++++++++++++++++++------------------ fs/nfsd/xdr4.h | 18 +- 3 files changed, 483 insertions(+), 246 deletions(-)
fs/nfsd/nfs4proc.c +3 -3
@@ -994,10 +994,10 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,return nfserr_jukebox; p = buf; - status =nfsd4_encode_fattr (&cstate->current_fh, + status =nfsd4_encode_fattr_to_buffer (&p, count, &cstate->current_fh, cstate->current_fh.fh_export, - cstate->current_fh.fh_dentry, &p, - count, verify->ve_bmval, + cstate->current_fh.fh_dentry, + verify->ve_bmval, rqstp,0 );/* this means that nfsd4_encode_fattr() ran out of space */
fs/nfsd/nfs4xdr.c +465 -240
1,121 lines not on this page · raw message
fs/nfsd/xdr4.h +15 -3
@@ -43,6 +43,19 @@#define NFSD4_MAX_TAGLEN 128#define XDR_LEN (n) (((n) + 3) & ~3) +struct svcxdr_ptr { +__be32 *p; +__be32 *end; +struct page **next_page; +}; + +struct svcxdr_stream { +struct svcxdr_ptr last_commit; +struct svcxdr_ptr ptr; +struct xdr_buf *buf; +struct kvec *this_iov; +}; +#define CURRENT_STATE_ID_FLAG (1<<0)#define SAVED_STATE_ID_FLAG (1<<1)
@@ -562,9 +575,8 @@ int nfs4svc_encode_compoundres(struct svc_rqst *, __be32 *,__be32 nfsd4_check_resp_size (struct nfsd4_compoundres *,u32 );void nfsd4_encode_operation (struct nfsd4_compoundres *,struct nfsd4_op *);void nfsd4_encode_replay (struct nfsd4_compoundres *resp,struct nfsd4_op *op); -__be32 nfsd4_encode_fattr (struct svc_fh *fhp,struct svc_export *exp, -struct dentry *dentry,__be32 **buffer,int countp, -u32 *bmval,struct svc_rqst *,int ignore_crossmnt); + +__be32 nfsd4_encode_fattr_to_buffer (__be32 **,int ,struct svc_fh *,struct svc_export *,struct dentry *,u32 *,struct svc_rqst *,int );extern __be32 nfsd4_setclientid (struct svc_rqst *rqstp,struct nfsd4_compound_state *,struct nfsd4_setclientid *setclid);
Signature · 1 line
1.7.11.7
Re: [PATCH 5/7] nfsd4: rewrite xdr encoding of attributes
Quoted 9 lines · J. Bruce Fields
From: "J. Bruce Fields" <[email protected]>
Write xdr encoding primitives that can handle crossing page boundaries, and use them in
nfsd4_encode_fattr.The main practical advantage for now is that we can return arbitrarily large ACLs (well, up to our maximum rpc size). However, compounds with other operations following such a getattr may fail.
Quoted 9 lines
Eventually we plan to use the same xdr code through v4 at least.
Signed-off-by J. Bruce Fields <[email protected]>--- fs/nfsd/nfs4proc.c | 6 +- fs/nfsd/nfs4xdr.c | 705 +++++++++++++++++++++++++++++++++++------------------ fs/nfsd/xdr4.h | 18 +- 3 files changed, 483 insertions(+), 246 deletions(-)
Why do these belong in the NFS server layer, and not the SUNRPC layer?
Also, why are you inventing an entirely new server-specific structure instead of just reusing the existing struct xdr_stream? The xdr_stream already has support for bounce buffers to deal with crossing page boundaries (although I've only implemented the decode side of the equation so far)...
Signature · 6 lines
Trond Myklebust Linux NFS client maintainer NetApp [email protected] www.netapp.com
Re: [PATCH 5/7] nfsd4: rewrite xdr encoding of attributes
Quoted 20 lines · Trond
Quoted 9 lines · J. Bruce Fields
From: "J. Bruce Fields" <[email protected]>
Write xdr encoding primitives that can handle crossing page boundaries, and use them in
nfsd4_encode_fattr.The main practical advantage for now is that we can return arbitrarily large ACLs (well, up to our maximum rpc size). However, compounds with other operations following such a getattr may fail.
Quoted 9 lines
Eventually we plan to use the same xdr code through v4 at least.
Signed-off-by J. Bruce Fields <[email protected]>--- fs/nfsd/nfs4proc.c | 6 +- fs/nfsd/nfs4xdr.c | 705 +++++++++++++++++++++++++++++++++++------------------ fs/nfsd/xdr4.h | 18 +- 3 files changed, 483 insertions(+), 246 deletions(-)
Quoted 7 lines
Why do these belong in the NFS server layer, and not the SUNRPC layer?
Also, why are you inventing an entirely new server-specific structure instead of just reusing the existing struct
xdr_stream? Thexdr_streamalready has support for bounce buffers to deal with crossing page boundaries (although I've only implemented the decode side of the equation so far)...
I thought a little about using a bounce buffer. What do you see as the advantage?
A couple issues I ran into: I want to be able to encode fields out of order, and to measure the offset between two points in the buffer.
For example, we encode attributes by encoding a dummy length field at the beginning, encoding the attributes, then calculating the length of the encoded data and going back to reencode the length field; roughly:
nfsd4_encode_fattr: ... encode bitmap attrlenp = p; ... encode attributes attrlen = (char *)p - (char *)attrlenp - 4; *attrlenp = htonl(attrlen);
Replacing p by a (__be32 *, page **) pair allows me to do basically the same thing:
attrlenp = p; ... attrlen = svcxdr_byte_offset(&attrlenp, &xdr->ptr) - 4; write32(&attrlenp, attrlen);
The "pointers" have all the information I need to calculate the length, and I don't have to worry about how many pages the attributes spanned or whether attrlenp itself spans a page boundary. (OK, actually it won't because it's 4 bytes. But we do the same thing elsewhere, e.g. when encoding readdir cookies.)
Using a bounce buffer also sets an arbitrary limits on how much space I can reserve at once. Maybe that's not a problem in practice.
--b.
Re: [PATCH 5/7] nfsd4: rewrite xdr encoding of attributes
Quoted 23 lines · J. Bruce Fields
Quoted 22 lines · Trond
Quoted 9 lines · J. Bruce Fields
From: "J. Bruce Fields" <[email protected]>
Write xdr encoding primitives that can handle crossing page boundaries, and use them in
nfsd4_encode_fattr.The main practical advantage for now is that we can return arbitrarily large ACLs (well, up to our maximum rpc size). However, compounds with other operations following such a getattr may fail.
Quoted 9 lines
Eventually we plan to use the same xdr code through v4 at least.
Signed-off-by J. Bruce Fields <[email protected]>--- fs/nfsd/nfs4proc.c | 6 +- fs/nfsd/nfs4xdr.c | 705 +++++++++++++++++++++++++++++++++++------------------ fs/nfsd/xdr4.h | 18 +- 3 files changed, 483 insertions(+), 246 deletions(-)Why do these belong in the NFS server layer, and not the SUNRPC layer?
Quoted 8 lines
Quoted 5 lines
Also, why are you inventing an entirely new server-specific structure instead of just reusing the existing struct
xdr_stream? Thexdr_streamalready has support for bounce buffers to deal with crossing page boundaries (although I've only implemented the decode side of the equation so far)...I thought a little about using a bounce buffer. What do you see as the advantage?
It makes the XDR buffer appear locally linear, despite being globally non-linear.
Quoted 2 lines
A couple issues I ran into: I want to be able to encode fields out of order, and to measure the offset between two points in the buffer.
Already done. See the xdr_stream_pos().
Quoted 23 lines
For example, we encode attributes by encoding a dummy length field at the beginning, encoding the attributes, then calculating the length of the encoded data and going back to reencode the length field; roughly:
nfsd4_encode_fattr: ... encode bitmap attrlenp = p; ... encode attributes attrlen = (char *)p - (char *)attrlenp - 4; *attrlenp = htonl(attrlen);Replacing p by a (
__be32*, page **) pair allows me to do basically the same thing:attrlenp = p; ... attrlen = svcxdr_byte_offset(&attrlenp, &xdr->ptr) - 4; write32(&attrlenp, attrlen);
Quoted 5 lines
The "pointers" have all the information I need to calculate the length, and I don't have to worry about how many pages the attributes spanned or whether attrlenp itself spans a page boundary. (OK, actually it won't because it's 4 bytes. But we do the same thing elsewhere, e.g. when encoding readdir cookies.)
Yeah, but it is just as simple to track the cursor offset. I already did that precisely to solve the above problem in NFSv4.
Quoted 2 lines
Using a bounce buffer also sets an arbitrary limits on how much space I can reserve at once. Maybe that's not a problem in practice.
The current decode implementation uses a single scratch page which can be preallocated for use in situations where we don't want to sleep. I can't think of any case (including readdir) where the record size is going to be greater than 4k. Even pathnames are limited to < that.
Signature · 6 lines
Trond Myklebust Linux NFS client maintainer NetApp [email protected] www.netapp.com
Re: [PATCH 5/7] nfsd4: rewrite xdr encoding of attributes
Quoted 30 lines · Trond
Quoted 29 lines · J. Bruce Fields
Quoted 27 lines · Trond
Quoted 9 lines · J. Bruce Fields
From: "J. Bruce Fields" <[email protected]>
Write xdr encoding primitives that can handle crossing page boundaries, and use them in
nfsd4_encode_fattr.The main practical advantage for now is that we can return arbitrarily large ACLs (well, up to our maximum rpc size). However, compounds with other operations following such a getattr may fail.
Quoted 9 lines
Eventually we plan to use the same xdr code through v4 at least.
Signed-off-by J. Bruce Fields <[email protected]>--- fs/nfsd/nfs4proc.c | 6 +- fs/nfsd/nfs4xdr.c | 705 +++++++++++++++++++++++++++++++++++------------------ fs/nfsd/xdr4.h | 18 +- 3 files changed, 483 insertions(+), 246 deletions(-)Why do these belong in the NFS server layer, and not the SUNRPC layer?
Also, why are you inventing an entirely new server-specific structure instead of just reusing the existing struct
xdr_stream? Thexdr_streamalready has support for bounce buffers to deal with crossing page boundaries (although I've only implemented the decode side of the equation so far)...
Quoted 5 lines
Quoted 2 lines
I thought a little about using a bounce buffer. What do you see as the advantage?
It makes the XDR buffer appear locally linear, despite being globally non-linear.
I still don't see why that's helpful. As long as you know how to write some data to the buffer, you don't need to know whether it's linear.
Quoted 4 lines
Quoted 2 lines
A couple issues I ran into: I want to be able to encode fields out of order, and to measure the offset between two points in the buffer.
Already done. See the
xdr_stream_pos().
Makes sense, thanks.
I also want to be able to revert to a previous state. (E.g., if something goes wrong partway through encoding attributes, I want to truncate what's been encoded so far.)
Quoted 23 lines
Quoted 23 lines
For example, we encode attributes by encoding a dummy length field at the beginning, encoding the attributes, then calculating the length of the encoded data and going back to reencode the length field; roughly:
nfsd4_encode_fattr: ... encode bitmap attrlenp = p; ... encode attributes attrlen = (char *)p - (char *)attrlenp - 4; *attrlenp = htonl(attrlen);Replacing p by a (
__be32*, page **) pair allows me to do basically the same thing:attrlenp = p; ... attrlen = svcxdr_byte_offset(&attrlenp, &xdr->ptr) - 4; write32(&attrlenp, attrlen);
Quoted 8 lines
Quoted 5 lines
The "pointers" have all the information I need to calculate the length, and I don't have to worry about how many pages the attributes spanned or whether attrlenp itself spans a page boundary. (OK, actually it won't because it's 4 bytes. But we do the same thing elsewhere, e.g. when encoding readdir cookies.)
Yeah, but it is just as simple to track the cursor offset. I already did that precisely to solve the above problem in NFSv4.
So for the case of encoding fields out of order: you're proposing remembering the offset of the earlier field, seeking back to that offset somehow, then writing the data by hand (and handling the case where that data might cross a boundary as a special case?). Or is there something simpler?
Quoted 7 lines
Quoted 2 lines
Using a bounce buffer also sets an arbitrary limits on how much space I can reserve at once. Maybe that's not a problem in practice.
The current decode implementation uses a single scratch page which can be preallocated for use in situations where we don't want to sleep. I can't think of any case (including readdir) where the record size is going to be greater than 4k. Even pathnames are limited to < that.
Yeah, I was wondering if there are any cases that matter. Read's not an issue since it's always going to be a special case anyway. Owner names are limited to 1k. Any of the pnfs ops? (Layouts or device info?)
--b.
Re: [PATCH 5/7] nfsd4: rewrite xdr encoding of attributes
Quoted 31 lines · J. Bruce Fields
Quoted 30 lines · Trond
Quoted 29 lines · J. Bruce Fields
Quoted 27 lines · Trond
Quoted 9 lines · J. Bruce Fields
From: "J. Bruce Fields" <[email protected]>
Write xdr encoding primitives that can handle crossing page boundaries, and use them in
nfsd4_encode_fattr.The main practical advantage for now is that we can return arbitrarily large ACLs (well, up to our maximum rpc size). However, compounds with other operations following such a getattr may fail.
Quoted 9 lines
Eventually we plan to use the same xdr code through v4 at least.
Signed-off-by J. Bruce Fields <[email protected]>--- fs/nfsd/nfs4proc.c | 6 +- fs/nfsd/nfs4xdr.c | 705 +++++++++++++++++++++++++++++++++++------------------ fs/nfsd/xdr4.h | 18 +- 3 files changed, 483 insertions(+), 246 deletions(-)Why do these belong in the NFS server layer, and not the SUNRPC layer?
Also, why are you inventing an entirely new server-specific structure instead of just reusing the existing struct
xdr_stream? Thexdr_streamalready has support for bounce buffers to deal with crossing page boundaries (although I've only implemented the decode side of the equation so far)...
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
I thought a little about using a bounce buffer. What do you see as the advantage?
It makes the XDR buffer appear locally linear, despite being globally non-linear.
I still don't see why that's helpful. As long as you know how to write some data to the buffer, you don't need to know whether it's linear.
It's about avoiding having to make all your helper functions (i.e. the existing xdr_encode/decode_foo() in net/sunrpc/xdr.c) page aware. Also, it can be useful to be able to pass a portion of an XDR buffer as a direct argument to a VFS function (e.g. readdir()).
So the way that the decode stuff works is through the xdr_inline_decode() function, which is passed a decode buffer size. If it sees that the buffer will fit inside the current page, then it returns a pointer to the page itself. If it sees that the buffer spans a page boundary, then it copies the data to the bounce buffer and passes a pointer to that. It means we don't need special versions of xdr_decode_string() etc that are page boundary aware.
As far as I can see, you basically want the same thing on the encode side. If you can encode data inline in the page, then have xdr_reserve_space() do that, but otherwise, have it return a scratch buffer and add an 'xdr_commit()' to take care of copying data from the scratch buffer across the page boundary. Saves a lot of trouble.
Quoted 40 lines
Quoted 4 lines
Quoted 2 lines
A couple issues I ran into: I want to be able to encode fields out of order, and to measure the offset between two points in the buffer.
Already done. See the
xdr_stream_pos().Makes sense, thanks.
I also want to be able to revert to a previous state. (E.g., if something goes wrong partway through encoding attributes, I want to truncate what's been encoded so far.)
Quoted 29 lines
Quoted 28 lines
For example, we encode attributes by encoding a dummy length field at the beginning, encoding the attributes, then calculating the length of the encoded data and going back to reencode the length field; roughly:
nfsd4_encode_fattr: ... encode bitmap attrlenp = p; ... encode attributes attrlen = (char *)p - (char *)attrlenp - 4; *attrlenp = htonl(attrlen);Replacing p by a (
__be32*, page **) pair allows me to do basically the same thing:attrlenp = p; ... attrlen = svcxdr_byte_offset(&attrlenp, &xdr->ptr) - 4; write32(&attrlenp, attrlen);The "pointers" have all the information I need to calculate the length, and I don't have to worry about how many pages the attributes spanned or whether attrlenp itself spans a page boundary. (OK, actually it won't because it's 4 bytes. But we do the same thing elsewhere, e.g. when encoding readdir cookies.)
Quoted 8 lines
Quoted 2 lines
Yeah, but it is just as simple to track the cursor offset. I already did that precisely to solve the above problem in NFSv4.
So for the case of encoding fields out of order: you're proposing remembering the offset of the earlier field, seeking back to that offset somehow, then writing the data by hand (and handling the case where that data might cross a boundary as a special case?). Or is there something simpler?
Are there any use-cases other than attribute array lengths? We already have write_bytes_to_xdr_buf() that can write a memory buffer to any arbitrary offset in an xdr_buf. Maybe we could add a simplified version of that for 32-bit writes?
An alternative, if you already have an estimate for the total buffer size, is to xdr_reserve_space() that buffer, and then add an xdr_adjust_space() in order to grow/shrink the buffer (copying it into a bounce buffer if the new size crosses a page boundary).
Quoted 11 lines
Quoted 7 lines
Quoted 2 lines
Using a bounce buffer also sets an arbitrary limits on how much space I can reserve at once. Maybe that's not a problem in practice.
The current decode implementation uses a single scratch page which can be preallocated for use in situations where we don't want to sleep. I can't think of any case (including readdir) where the record size is going to be greater than 4k. Even pathnames are limited to < that.
Yeah, I was wondering if there are any cases that matter. Read's not an issue since it's always going to be a special case anyway. Owner names are limited to 1k. Any of the pnfs ops? (Layouts or device info?)
layoutget for a block device can be unwieldy and needs to be managed by the layout driver. I don't think the current Linux pNFS client block driver is happy with > 4k layouts, though...
Signature · 6 lines
Trond Myklebust Linux NFS client maintainer NetApp [email protected] www.netapp.com
Re: [PATCH 5/7] nfsd4: rewrite xdr encoding of attributes
Quoted 35 lines · Trond
Quoted 34 lines · J. Bruce Fields
Quoted 33 lines · Trond
Quoted 31 lines · J. Bruce Fields
Quoted 20 lines · Trond
Quoted 9 lines · J. Bruce Fields
From: "J. Bruce Fields" <[email protected]>
Write xdr encoding primitives that can handle crossing page boundaries, and use them in
nfsd4_encode_fattr.The main practical advantage for now is that we can return arbitrarily large ACLs (well, up to our maximum rpc size). However, compounds with other operations following such a getattr may fail.
Quoted 9 lines
Eventually we plan to use the same xdr code through v4 at least.
Signed-off-by J. Bruce Fields <[email protected]>--- fs/nfsd/nfs4proc.c | 6 +- fs/nfsd/nfs4xdr.c | 705 +++++++++++++++++++++++++++++++++++------------------ fs/nfsd/xdr4.h | 18 +- 3 files changed, 483 insertions(+), 246 deletions(-)Quoted 7 lines
Why do these belong in the NFS server layer, and not the SUNRPC layer?
Also, why are you inventing an entirely new server-specific structure instead of just reusing the existing struct
xdr_stream? Thexdr_streamalready has support for bounce buffers to deal with crossing page boundaries (although I've only implemented the decode side of the equation so far)...I thought a little about using a bounce buffer. What do you see as the advantage?
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
It makes the XDR buffer appear locally linear, despite being globally non-linear.
I still don't see why that's helpful. As long as you know how to write some data to the buffer, you don't need to know whether it's linear.
It's about avoiding having to make all your helper functions (i.e. the existing xdr_encode/decode_foo() in
net/sunrpc/xdr.c) page aware.
Sure, but the helper functions are easy. And there's much less code in the encoders than in the callers, so I'd rather optimize for the convenience of the latter:
$ wc -l net/sunrpc/xdr.c 1289 net/sunrpc/xdr.c $ wc -l fs/nfs*/*xdr.c 999 fs/nfs/callback_xdr.c 1117 fs/nfsd/nfs3xdr.c 3975 fs/nfsd/nfs4xdr.c 546 fs/nfsd/nfsxdr.c 1136 fs/nfs/nfs2xdr.c 2546 fs/nfs/nfs3xdr.c 7293 fs/nfs/nfs4xdr.c 17612 total
Quoted 2 lines
Also, it can be useful to be able to pass a portion of an XDR buffer as a direct argument to a VFS function (e.g.
readdir()).
I don't see how we'd use it in readdir?
Quoted 8 lines
So the way that the decode stuff works is through the
xdr_inline_decode()function, which is passed a decode buffer size. If it sees that the buffer will fit inside the current page, then it returns a pointer to the page itself. If it sees that the buffer spans a page boundary, then it copies the data to the bounce buffer and passes a pointer to that. It means we don't need special versions ofxdr_decode_string()etc that are page boundary aware.
Quoted 5 lines
As far as I can see, you basically want the same thing on the encode side. If you can encode data inline in the page, then have
xdr_reserve_space()do that, but otherwise, have it return a scratch buffer and add an 'xdr_commit()' to take care of copying data from the scratch buffer across the page boundary. Saves a lot of trouble.
With more callers than callees, I'd take slightly more complicated encoders in return for not having to add xdr_commit's all over.
Quoted 7 lines
Quoted 5 lines
So for the case of encoding fields out of order: you're proposing remembering the offset of the earlier field, seeking back to that offset somehow, then writing the data by hand (and handling the case where that data right cross a boundary as a special case?). Or is there something simpler?
Are there any use-cases other than attribute array lengths?
The only other nested xdr I can think of is from pNFS.
Readdir cookies are also written out of order. As are compound status and compound opcnt. (And tags, for some reason. That's weird.) That might be all.
Quoted 4 lines
We already
havewrite_bytes_to_xdr_buf()that can write a memory buffer to any arbitrary offset in anxdr_buf. Maybe we could add a simplified version of that for 32-bit writes?
I guess so.
Quoted 4 lines
An alternative, if you already have an estimate for the total buffer size, is to
xdr_reserve_space()that buffer, and then add anxdr_adjust_space()in order to grow/shrink the buffer (copying it into a bounce buffer if the new size crosses a page boundary).
I don't want to try to reserve the whole attribute buffer. (It could include ACLs of arbitrary length.)
--b.
PATCH 6/7 nfsd4: track maximum bytes
In reply to J. Bruce FieldsFrom: "J. Bruce Fields" <[email protected]>
All reserve_space is doing currently is checking that we don't run out of pages, but some users (e.g. readdir) also want to check some other limit.
XXX: also for 4.1 shouldn't we be checking session-negotiated maximum?
--- fs/nfsd/nfs4xdr.c | 11 +++++++++-- fs/nfsd/xdr4.h | 1 + 2 files changed, 10 insertions(+), 2 deletions(-)
fs/nfsd/nfs4xdr.c +9 -2
@@ -1669,6 +1669,7 @@ static __be32 svcxdr_stream_init_from_resp(struct svcxdr_stream *xdr, struct nfs buf->head[0 ].iov_len = (char *)resp->p - (char *)buf->head[0 ].iov_base; buf->len = buf->head[0 ].iov_len; + xdr->maxbytes =INT_MAX ; xdr->buf = buf; xdr->this_iov = buf->head; xdr->ptr.p = resp->p;
@@ -1683,6 +1684,7 @@ static __be32 svcxdr_stream_init_from_resp(struct svcxdr_stream *xdr, struct nfs*/ static void svcxdr_stream_init_from_buffer (struct svcxdr_stream *xdr,__be32 *buf,int len) { + xdr->maxbytes =INT_MAX ; xdr->ptr.p = buf; xdr->ptr.end = buf + len; xdr->ptr.next_page =NULL ;
@@ -1724,8 +1726,12 @@ static bool svcxdr_reserve_space(struct svcxdr_stream *xdr, struct svcxdr_ptr *p *ptr = xdr->ptr; bytes =roundup (bytes,4 ); - -return svcxdr_seek (&xdr->ptr, bytes); +if (bytes > xdr->maxbytes) +return false; +if (!svcxdr_seek (&xdr->ptr, bytes)) +return false; + xdr->maxbytes -= bytes; +return true; }/* XXX: basic idea here:
@@ -1827,6 +1833,7 @@ static int svcxdr_byte_offset(struct svcxdr_ptr *from, struct svcxdr_ptr *to)static void svcxdr_reset (struct svcxdr_stream *xdr,struct svcxdr_ptr *to) {WARN_ON_ONCE (svcxdr_ptr_misordered (&xdr->last_commit, to)); + xdr->maxbytes +=svcxdr_byte_offset (to, &xdr->ptr); xdr->ptr = *to; }
fs/nfsd/xdr4.h +1 -0
@@ -54,6 +54,7 @@ struct svcxdr_stream {struct svcxdr_ptr ptr;struct xdr_buf *buf;struct kvec *this_iov; +int maxbytes; };#define CURRENT_STATE_ID_FLAG (1<<0)
Signature · 1 line
1.7.11.7
PATCH 7/7 nfsd4: readdir encoding
In reply to J. Bruce FieldsFrom: "J. Bruce Fields" <[email protected]>
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.
--- fs/nfsd/nfs4xdr.c | 157 +++++++++++++++++++++++++++++------------------------- fs/nfsd/xdr4.h | 5 +- 2 files changed, 85 insertions(+), 77 deletions(-)
fs/nfsd/nfs4xdr.c +83 -74
@@ -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; }
fs/nfsd/xdr4.h +2 -3
@@ -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 {
Signature · 1 line
1.7.11.7