[PATCH 5/7] nfsd4: rewrite xdr encoding of attributes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: "J. Bruce Fields" <bfields@xxxxxxxxxx>

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.

Signed-off-by: J. Bruce Fields <bfields@xxxxxxxxxx>
---
 fs/nfsd/nfs4proc.c |   6 +-
 fs/nfsd/nfs4xdr.c  | 705 +++++++++++++++++++++++++++++++++++------------------
 fs/nfsd/xdr4.h     |  18 +-
 3 files changed, 483 insertions(+), 246 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 6b5c20c..f62e139 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -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 */
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index a7876f7..19f773e 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -1644,18 +1644,237 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
 	p += XDR_QUADLEN(nbytes);				\
 }} while (0)
 
-static void write32(__be32 **p, u32 n)
+static void next_page(struct svcxdr_ptr *ptr)
 {
-	*(*p)++ = htonl(n);
+	struct page *page;
+
+	page = *(ptr->next_page++);
+	WARN_ON(!page);
+	ptr->p = page_address(page);
+	ptr->end = ptr->p + PAGE_SIZE / 4;
+}
+
+static __be32 svcxdr_stream_init_from_resp(struct svcxdr_stream *xdr, struct nfsd4_compoundres *resp)
+{
+	struct svc_rqst *rqstp = resp->rqstp;
+	struct xdr_buf *buf = &rqstp->rq_res;
+
+	/*
+	 * Fail if we've moved past encoding the head.  (Arbitrary
+	 * restriction for now, to be lifted later.)
+	 */
+	if ((void *)resp->p < buf->head[0].iov_base
+			|| (void *)resp->p >= buf->head[0].iov_base + PAGE_SIZE)
+		return nfserr_resource;
+	buf->head[0].iov_len = (char *)resp->p - (char *)buf->head[0].iov_base;
+	buf->len = buf->head[0].iov_len;
+
+	xdr->buf = buf;
+	xdr->this_iov = buf->head;
+	xdr->ptr.p = resp->p;
+	xdr->ptr.end = resp->end;
+	xdr->ptr.next_page = rqstp->rq_next_page;
+	xdr->last_commit = xdr->ptr;
+	return nfs_ok;
+}
+
+/* XXX: len is in words not bytes.  This is confusing.  Callers could
+ * probably use more documentation on this point.
+ */
+static void svcxdr_stream_init_from_buffer(struct svcxdr_stream *xdr, __be32 *buf, int len)
+{
+	xdr->ptr.p = buf;
+	xdr->ptr.end = buf + len;
+	xdr->ptr.next_page = NULL;
+	xdr->last_commit = xdr->ptr;
+
+	xdr->buf = NULL;
+	xdr->this_iov = NULL;
+}
+
+static bool svcxdr_seek(struct svcxdr_ptr *res, unsigned int bytes)
+{
+	struct svcxdr_ptr ptr = *res;
+	unsigned int avail, this;
+
+	bytes = roundup(bytes, 4);
+
+	while (bytes) {
+		avail = (char *)ptr.end - (char *)ptr.p;
+		this = min(bytes, avail);
+		bytes -= this;
+		ptr.p += this / 4;
+		if (!bytes)
+			break;
+		if (!ptr.next_page || !*(ptr.next_page))
+			return false;
+		next_page(&ptr);
+	}
+	*res = ptr;
+	return true;
+}
+
+/*
+ * If there's still space to encode "bytes" bytes, then return true,
+ * advance the xdr stream and the end of the xdr buf by "bytes", and
+ * set ptr to the previous value of the svcxdr_stream pointer.
+ */
+static bool svcxdr_reserve_space(struct svcxdr_stream *xdr, struct svcxdr_ptr *ptr, int bytes)
+{
+	*ptr = xdr->ptr;
+
+	bytes = roundup(bytes, 4);
+
+	return svcxdr_seek(&xdr->ptr, bytes);
+}
+
+/* XXX: basic idea here:
+ * 	- useful to keep track of reserved space (though could actually
+ * 	  use caller's ptr for that; but this gives some consistency
+ * 	  checks?); hence xdr->ptr.
+ *	- also need to know where last committed, hence xdr->last_commit
+ *	- also need running pointer which is caller's ptr.
+ *	- another alternative would be to just track total reserved so
+ *	  far and re-seek, but re-seeking from start on every
+ *	  reserve seems wasteful.
+ */
+
+/* XXX: need to modify callers. */
+static void svcxdr_commit(struct svcxdr_stream *xdr)
+{
+	struct svcxdr_ptr *ptr = &xdr->ptr;
+	int page_bytes, page_offset;
+
+	if (!xdr->buf) {
+		xdr->last_commit = *ptr;
+		return;
+	}
+	if (xdr->last_commit.next_page == ptr->next_page) {
+		int this = (char *)ptr->p - (char *)xdr->last_commit.p;
+		WARN_ON_ONCE(this > PAGE_SIZE);
+
+		if (xdr->this_iov)
+			xdr->this_iov->iov_len += this;
+		else
+			xdr->buf->page_len += this;
+		xdr->buf->len += this;
+		xdr->last_commit = *ptr;
+		return;
+	}
+	if (xdr->this_iov) {
+		int this = (char *)xdr->last_commit.end
+				- (char *)xdr->last_commit.p;
+
+		xdr->this_iov->iov_len += this;
+		xdr->this_iov = NULL;
+		xdr->buf->len += this;
+	}
+	page_bytes = PAGE_SIZE *
+			(ptr->next_page - xdr->last_commit.next_page - 1);
+	page_offset = (void *)ptr->p - page_address(*(ptr->next_page - 1));
+
+	xdr->buf->page_len += page_bytes + page_offset;
+	xdr->buf->len += page_bytes + page_offset;
+	xdr->last_commit = *ptr;
+	return;
+}
+
+static void svcxdr_stream_update_resp(struct svcxdr_stream *xdr, struct nfsd4_compoundres *resp)
+{
+	struct svc_rqst *rqstp = resp->rqstp;
+
+	svcxdr_commit(xdr);
+	resp->p = xdr->last_commit.p;
+	resp->end = xdr->last_commit.end;
+	if (xdr->ptr.next_page)
+		rqstp->rq_next_page = xdr->ptr.next_page;
+}
+
+static void svcxdr_stream_update_buffer(struct svcxdr_stream *xdr, __be32 **p)
+{
+	svcxdr_commit(xdr);
+	*p = xdr->last_commit.p;
 }
 
-static void write64(__be32 **p, u64 n)
+static bool svcxdr_ptr_misordered(struct svcxdr_ptr *from, struct svcxdr_ptr *to)
 {
-	write32(p, (n >> 32));
-	write32(p, (u32)n);
+	unsigned long bytes;
+
+	if (from->next_page > to->next_page)
+		return true;
+	if (from->next_page < to->next_page)
+		return false;
+	bytes = (void *)to->p - (void *)from->p;
+	return bytes > PAGE_SIZE;
 }
 
-static void write_change(__be32 **p, struct kstat *stat, struct inode *inode)
+static int svcxdr_byte_offset(struct svcxdr_ptr *from, struct svcxdr_ptr *to)
+{
+	struct svcxdr_ptr ptr = *from;
+	int bytes = 0;
+	int final_bytes;
+
+	BUG_ON(svcxdr_ptr_misordered(from, to));
+	while (ptr.next_page < to->next_page) {
+		bytes += (void *)ptr.end - (void *)ptr.p;
+		next_page(&ptr);
+	}
+	final_bytes = (void *)to->p - (void *)ptr.p;
+	bytes += final_bytes;
+	return bytes;
+}
+
+static void svcxdr_reset(struct svcxdr_stream *xdr, struct svcxdr_ptr *to)
+{
+	WARN_ON_ONCE(svcxdr_ptr_misordered(&xdr->last_commit, to));
+	xdr->ptr = *to;
+}
+
+static void writemem(struct svcxdr_ptr *ptr, const void *data, int nbytes)
+{
+	int this, avail;
+	int paddedlen = roundup(nbytes, 4);
+	int padding = paddedlen - nbytes;
+
+	while (nbytes) {
+		avail = (char *)ptr->end - (char *)ptr->p;
+		this = min(nbytes, avail);
+		memcpy(ptr->p, data, this);
+		nbytes -= this;
+		if (!nbytes)
+			break;
+		next_page(ptr);
+		paddedlen -= this;
+		data += this;
+	}
+	ptr->p += paddedlen / 4;
+	memset((char *)ptr->p - padding, 0, padding);
+}
+
+static void write32(struct svcxdr_ptr *ptr, u32 n)
+{
+	__be32 i = htonl(n);
+	writemem(ptr, &i, sizeof(i));
+}
+
+static void write64(struct svcxdr_ptr *ptr, u64 n)
+{
+	__be32 i[2] = { htonl(n >> 32), htonl((u32)n) };
+	writemem(ptr, i, sizeof(i));
+}
+
+static bool write_opaque(struct svcxdr_stream *xdr, const void *data, int len)
+{
+	struct svcxdr_ptr ptr;
+
+	if (!svcxdr_reserve_space(xdr, &ptr, 4 + len))
+		return false;
+	write32(&ptr, len);
+	writemem(&ptr, data, len);
+	return true;
+}
+
+static void write_change(struct svcxdr_ptr *p, struct kstat *stat, struct inode *inode)
 {
 	if (IS_I_VERSION(inode)) {
 		write64(p, inode->i_version);
@@ -1665,18 +1884,21 @@ static void write_change(__be32 **p, struct kstat *stat, struct inode *inode)
 	}
 }
 
-static void write_cinfo(__be32 **p, struct nfsd4_change_info *c)
+static void write_cinfo(__be32 **pp, struct nfsd4_change_info *c)
 {
-	write32(p, c->atomic);
+	__be32 *p = *pp;
+
+	WRITE32(c->atomic);
 	if (c->change_supported) {
-		write64(p, c->before_change);
-		write64(p, c->after_change);
+		WRITE64(c->before_change);
+		WRITE64(c->after_change);
 	} else {
-		write32(p, c->before_ctime_sec);
-		write32(p, c->before_ctime_nsec);
-		write32(p, c->after_ctime_sec);
-		write32(p, c->after_ctime_nsec);
+		WRITE32(c->before_ctime_sec);
+		WRITE32(c->before_ctime_nsec);
+		WRITE32(c->after_ctime_sec);
+		WRITE32(c->after_ctime_nsec);
 	}
+	*pp = p;
 }
 
 #define RESERVE_SPACE(nbytes)	do {				\
@@ -1718,19 +1940,19 @@ static void encode_seqid_op_tail(struct nfsd4_compoundres *resp, __be32 *save, _
 /* Encode as an array of strings the string given with components
  * separated @sep, escaped with esc_enter and esc_exit.
  */
-static __be32 nfsd4_encode_components_esc(char sep, char *components,
-				   __be32 **pp, int *buflen,
+static __be32 nfsd4_encode_components_esc(struct svcxdr_stream *xdr, char sep, char *components,
 				   char esc_enter, char esc_exit)
 {
-	__be32 *p = *pp;
-	__be32 *countp = p;
+	struct svcxdr_ptr p;
+	struct svcxdr_ptr countp;
 	int strlen, count=0;
 	char *str, *end, *next;
 
 	dprintk("nfsd4_encode_components(%s)\n", components);
-	if ((*buflen -= 4) < 0)
+	if (!svcxdr_reserve_space(xdr, &p, 4))
 		return nfserr_resource;
-	WRITE32(0); /* We will fill this in with @count later */
+	countp = p;
+	write32(&p, 0); /* We will fill this in with @count later */
 	end = str = components;
 	while (*end) {
 		bool found_esc = false;
@@ -1752,62 +1974,52 @@ static __be32 nfsd4_encode_components_esc(char sep, char *components,
 
 		strlen = end - str;
 		if (strlen) {
-			if ((*buflen -= ((XDR_QUADLEN(strlen) << 2) + 4)) < 0)
+			if (!write_opaque(xdr, str, strlen))
 				return nfserr_resource;
-			WRITE32(strlen);
-			WRITEMEM(str, strlen);
 			count++;
-		}
-		else
+		} else
 			end++;
 		str = end;
 	}
-	*pp = p;
-	p = countp;
-	WRITE32(count);
+	write32(&countp, count);
 	return 0;
 }
 
 /* Encode as an array of strings the string given with components
  * separated @sep.
  */
-static __be32 nfsd4_encode_components(char sep, char *components,
-				   __be32 **pp, int *buflen)
+static __be32 nfsd4_encode_components(struct svcxdr_stream *xdr, char sep, char *components)
 {
-	return nfsd4_encode_components_esc(sep, components, pp, buflen, 0, 0);
+	return nfsd4_encode_components_esc(xdr, sep, components, 0, 0);
 }
 
 /*
  * encode a location element of a fs_locations structure
  */
-static __be32 nfsd4_encode_fs_location4(struct nfsd4_fs_location *location,
-				    __be32 **pp, int *buflen)
+static __be32 nfsd4_encode_fs_location4(struct svcxdr_stream *xdr, struct nfsd4_fs_location *location)
 {
 	__be32 status;
-	__be32 *p = *pp;
 
-	status = nfsd4_encode_components_esc(':', location->hosts, &p, buflen,
+	status = nfsd4_encode_components_esc(xdr, ':', location->hosts,
 						'[', ']');
 	if (status)
 		return status;
-	status = nfsd4_encode_components('/', location->path, &p, buflen);
+	status = nfsd4_encode_components(xdr, '/', location->path);
 	if (status)
 		return status;
-	*pp = p;
 	return 0;
 }
 
 /*
  * Encode a path in RFC3530 'pathname4' format
  */
-static __be32 nfsd4_encode_path(const struct path *root,
-		const struct path *path, __be32 **pp, int *buflen)
+static __be32 nfsd4_encode_path(struct svcxdr_stream *xdr, const struct path *root, const struct path *path)
 {
 	struct path cur = {
 		.mnt = path->mnt,
 		.dentry = path->dentry,
 	};
-	__be32 *p = *pp;
+	struct svcxdr_ptr p;
 	struct dentry **components = NULL;
 	unsigned int ncomponents = 0;
 	__be32 err = nfserr_jukebox;
@@ -1839,26 +2051,22 @@ static __be32 nfsd4_encode_path(const struct path *root,
 		cur.dentry = dget_parent(cur.dentry);
 	}
 
-	*buflen -= 4;
-	if (*buflen < 0)
+	err = nfserr_resource;
+	if (!svcxdr_reserve_space(xdr, &p, 4))
 		goto out_free;
-	WRITE32(ncomponents);
+	write32(&p, ncomponents);
 
 	while (ncomponents) {
 		struct dentry *dentry = components[ncomponents - 1];
 		unsigned int len = dentry->d_name.len;
 
-		*buflen -= 4 + (XDR_QUADLEN(len) << 2);
-		if (*buflen < 0)
+		if (!write_opaque(xdr, dentry->d_name.name, len))
 			goto out_free;
-		WRITE32(len);
-		WRITEMEM(dentry->d_name.name, len);
 		dprintk("/%s", dentry->d_name.name);
 		dput(dentry);
 		ncomponents--;
 	}
 
-	*pp = p;
 	err = 0;
 out_free:
 	dprintk(")\n");
@@ -1869,8 +2077,7 @@ out_free:
 	return err;
 }
 
-static __be32 nfsd4_encode_fsloc_fsroot(struct svc_rqst *rqstp,
-		const struct path *path, __be32 **pp, int *buflen)
+static __be32 nfsd4_encode_fsloc_fsroot(struct svcxdr_stream *xdr, struct svc_rqst *rqstp, const struct path *path)
 {
 	struct svc_export *exp_ps;
 	__be32 res;
@@ -1878,7 +2085,7 @@ static __be32 nfsd4_encode_fsloc_fsroot(struct svc_rqst *rqstp,
 	exp_ps = rqst_find_fsidzero_export(rqstp);
 	if (IS_ERR(exp_ps))
 		return nfserrno(PTR_ERR(exp_ps));
-	res = nfsd4_encode_path(&exp_ps->ex_path, path, pp, buflen);
+	res = nfsd4_encode_path(xdr, &exp_ps->ex_path, path);
 	exp_put(exp_ps);
 	return res;
 }
@@ -1886,28 +2093,24 @@ static __be32 nfsd4_encode_fsloc_fsroot(struct svc_rqst *rqstp,
 /*
  *  encode a fs_locations structure
  */
-static __be32 nfsd4_encode_fs_locations(struct svc_rqst *rqstp,
-				     struct svc_export *exp,
-				     __be32 **pp, int *buflen)
+static __be32 nfsd4_encode_fs_locations(struct svcxdr_stream *xdr, struct svc_rqst *rqstp, struct svc_export *exp)
 {
 	__be32 status;
+	struct svcxdr_ptr ptr;
 	int i;
-	__be32 *p = *pp;
 	struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs;
 
-	status = nfsd4_encode_fsloc_fsroot(rqstp, &exp->ex_path, &p, buflen);
+	status = nfsd4_encode_fsloc_fsroot(xdr, rqstp, &exp->ex_path);
 	if (status)
 		return status;
-	if ((*buflen -= 4) < 0)
+	if (!svcxdr_reserve_space(xdr, &ptr, 4))
 		return nfserr_resource;
-	WRITE32(fslocs->locations_count);
+	write32(&ptr, fslocs->locations_count);
 	for (i=0; i<fslocs->locations_count; i++) {
-		status = nfsd4_encode_fs_location4(&fslocs->locations[i],
-						   &p, buflen);
+		status = nfsd4_encode_fs_location4(xdr, &fslocs->locations[i]);
 		if (status)
 			return status;
 	}
-	*pp = p;
 	return 0;
 }
 
@@ -1926,44 +2129,40 @@ static u32 nfs4_file_type(umode_t mode)
 }
 
 static __be32
-nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
-			__be32 **p, int *buflen)
+nfsd4_encode_name(struct svcxdr_stream *xdr, struct svc_rqst *rqstp, int whotype, uid_t id, int group)
 {
+	char buf[IDMAP_NAMESZ]; /* XXX: fix mapping fns to write to xdr */
 	int status;
 
-	if (*buflen < (XDR_QUADLEN(IDMAP_NAMESZ) << 2) + 4)
-		return nfserr_resource;
 	if (whotype != NFS4_ACL_WHO_NAMED)
-		status = nfs4_acl_write_who(whotype, (u8 *)(*p + 1));
+		status = nfs4_acl_write_who(whotype, buf);
 	else if (group)
-		status = nfsd_map_gid_to_name(rqstp, id, (u8 *)(*p + 1));
+		status = nfsd_map_gid_to_name(rqstp, id, buf);
 	else
-		status = nfsd_map_uid_to_name(rqstp, id, (u8 *)(*p + 1));
+		status = nfsd_map_uid_to_name(rqstp, id, buf);
 	if (status < 0)
 		return nfserrno(status);
-	*p = xdr_encode_opaque(*p, NULL, status);
-	*buflen -= (XDR_QUADLEN(status) << 2) + 4;
-	BUG_ON(*buflen < 0);
-	return 0;
+	if (!write_opaque(xdr, buf, status))
+		return nfserr_resource;
+	return nfs_ok;
 }
 
 static inline __be32
-nfsd4_encode_user(struct svc_rqst *rqstp, uid_t uid, __be32 **p, int *buflen)
+nfsd4_encode_user(struct svcxdr_stream *xdr, struct svc_rqst *rqstp, uid_t uid)
 {
-	return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, uid, 0, p, buflen);
+	return nfsd4_encode_name(xdr, rqstp, NFS4_ACL_WHO_NAMED, uid, 0);
 }
 
 static inline __be32
-nfsd4_encode_group(struct svc_rqst *rqstp, uid_t gid, __be32 **p, int *buflen)
+nfsd4_encode_group(struct svcxdr_stream *xdr, struct svc_rqst *rqstp, uid_t gid)
 {
-	return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, gid, 1, p, buflen);
+	return nfsd4_encode_name(xdr, rqstp, NFS4_ACL_WHO_NAMED, gid, 1);
 }
 
 static inline __be32
-nfsd4_encode_aclname(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
-		__be32 **p, int *buflen)
+nfsd4_encode_aclname(struct svcxdr_stream *xdr, struct svc_rqst *rqstp, int whotype, uid_t id, int group)
 {
-	return nfsd4_encode_name(rqstp, whotype, id, group, p, buflen);
+	return nfsd4_encode_name(xdr, rqstp, whotype, id, group);
 }
 
 #define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \
@@ -2008,9 +2207,9 @@ static int get_parent_attributes(struct svc_export *exp, struct kstat *stat)
  *
  * 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 count, u32 *bmval,
+static __be32
+nfsd4_encode_fattr(struct svcxdr_stream *xdr, struct svc_fh *fhp,
+		struct svc_export *exp, struct dentry *dentry, u32 *bmval,
 		struct svc_rqst *rqstp, int ignore_crossmnt)
 {
 	u32 bmval0 = bmval[0];
@@ -2019,14 +2218,15 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
 	struct kstat stat;
 	struct svc_fh tempfh;
 	struct kstatfs statfs;
-	int buflen = count << 2;
-	__be32 *attrlenp;
+	struct svcxdr_ptr attrlenp;
+	struct svcxdr_ptr ptr;
+	struct svcxdr_ptr start = xdr->ptr;
 	u32 dummy;
 	u64 dummy64;
 	u32 rdattr_err = 0;
-	__be32 *p = *buffer;
 	__be32 status;
 	int err;
+	int attrlen;
 	int aclsupport = 0;
 	struct nfs4_acl *acl = NULL;
 	struct nfsd4_compoundres *resp = rqstp->rq_resp;
@@ -2083,25 +2283,28 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
 	}
 
 	if (bmval2) {
-		if ((buflen -= 16) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 16))
 			goto out_resource;
-		WRITE32(3);
-		WRITE32(bmval0);
-		WRITE32(bmval1);
-		WRITE32(bmval2);
+		write32(&ptr, 3);
+		write32(&ptr, bmval0);
+		write32(&ptr, bmval1);
+		write32(&ptr, bmval2);
 	} else if (bmval1) {
-		if ((buflen -= 12) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 12))
 			goto out_resource;
-		WRITE32(2);
-		WRITE32(bmval0);
-		WRITE32(bmval1);
+		write32(&ptr, 2);
+		write32(&ptr, bmval0);
+		write32(&ptr, bmval1);
 	} else {
-		if ((buflen -= 8) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 8))
 			goto out_resource;
-		WRITE32(1);
-		WRITE32(bmval0);
+		write32(&ptr, 1);
+		write32(&ptr, bmval0);
 	}
-	attrlenp = p++;                /* to be backfilled later */
+	attrlenp = ptr;
+	if (!svcxdr_reserve_space(xdr, &ptr, 4))
+		goto out_resource;
+	write32(&ptr, 0); /* to be filled in later */
 
 	if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) {
 		u32 word0 = nfsd_suppattrs0(minorversion);
@@ -2111,301 +2314,299 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
 		if (!aclsupport)
 			word0 &= ~FATTR4_WORD0_ACL;
 		if (!word2) {
-			if ((buflen -= 12) < 0)
+			if (!svcxdr_reserve_space(xdr, &ptr, 12))
 				goto out_resource;
-			WRITE32(2);
-			WRITE32(word0);
-			WRITE32(word1);
+			write32(&ptr, 2);
+			write32(&ptr, word0);
+			write32(&ptr, word1);
 		} else {
-			if ((buflen -= 16) < 0)
+			if (!svcxdr_reserve_space(xdr, &ptr, 16))
 				goto out_resource;
-			WRITE32(3);
-			WRITE32(word0);
-			WRITE32(word1);
-			WRITE32(word2);
+			write32(&ptr, 3);
+			write32(&ptr, word0);
+			write32(&ptr, word1);
+			write32(&ptr, word2);
 		}
 	}
 	if (bmval0 & FATTR4_WORD0_TYPE) {
-		if ((buflen -= 4) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 4))
 			goto out_resource;
 		dummy = nfs4_file_type(stat.mode);
 		if (dummy == NF4BAD) {
 			status = nfserr_serverfault;
 			goto out;
 		}
-		WRITE32(dummy);
+		write32(&ptr, dummy);
 	}
 	if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) {
-		if ((buflen -= 4) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 4))
 			goto out_resource;
 		if (exp->ex_flags & NFSEXP_NOSUBTREECHECK)
-			WRITE32(NFS4_FH_PERSISTENT);
+			write32(&ptr, NFS4_FH_PERSISTENT);
 		else
-			WRITE32(NFS4_FH_PERSISTENT|NFS4_FH_VOL_RENAME);
+			write32(&ptr, NFS4_FH_PERSISTENT|NFS4_FH_VOL_RENAME);
 	}
 	if (bmval0 & FATTR4_WORD0_CHANGE) {
-		if ((buflen -= 8) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 8))
 			goto out_resource;
-		write_change(&p, &stat, dentry->d_inode);
+		write_change(&ptr, &stat, dentry->d_inode);
 	}
 	if (bmval0 & FATTR4_WORD0_SIZE) {
-		if ((buflen -= 8) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 8))
 			goto out_resource;
-		WRITE64(stat.size);
+		write64(&ptr, stat.size);
 	}
 	if (bmval0 & FATTR4_WORD0_LINK_SUPPORT) {
-		if ((buflen -= 4) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 4))
 			goto out_resource;
-		WRITE32(1);
+		write32(&ptr, 1);
 	}
 	if (bmval0 & FATTR4_WORD0_SYMLINK_SUPPORT) {
-		if ((buflen -= 4) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 4))
 			goto out_resource;
-		WRITE32(1);
+		write32(&ptr, 1);
 	}
 	if (bmval0 & FATTR4_WORD0_NAMED_ATTR) {
-		if ((buflen -= 4) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 4))
 			goto out_resource;
-		WRITE32(0);
+		write32(&ptr, 0);
 	}
 	if (bmval0 & FATTR4_WORD0_FSID) {
-		if ((buflen -= 16) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 16))
 			goto out_resource;
 		if (exp->ex_fslocs.migrated) {
-			WRITE64(NFS4_REFERRAL_FSID_MAJOR);
-			WRITE64(NFS4_REFERRAL_FSID_MINOR);
+			write64(&ptr, NFS4_REFERRAL_FSID_MAJOR);
+			write64(&ptr, NFS4_REFERRAL_FSID_MINOR);
 		} else switch(fsid_source(fhp)) {
 		case FSIDSOURCE_FSID:
-			WRITE64((u64)exp->ex_fsid);
-			WRITE64((u64)0);
+			write64(&ptr, (u64)exp->ex_fsid);
+			write64(&ptr, 0);
 			break;
 		case FSIDSOURCE_DEV:
-			WRITE32(0);
-			WRITE32(MAJOR(stat.dev));
-			WRITE32(0);
-			WRITE32(MINOR(stat.dev));
+			write32(&ptr, 0);
+			write32(&ptr, MAJOR(stat.dev));
+			write32(&ptr, 0);
+			write32(&ptr, MINOR(stat.dev));
 			break;
 		case FSIDSOURCE_UUID:
-			WRITEMEM(exp->ex_uuid, 16);
+			writemem(&ptr, exp->ex_uuid, 16);
 			break;
 		}
 	}
 	if (bmval0 & FATTR4_WORD0_UNIQUE_HANDLES) {
-		if ((buflen -= 4) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 4))
 			goto out_resource;
-		WRITE32(0);
+		write32(&ptr, 0);
 	}
 	if (bmval0 & FATTR4_WORD0_LEASE_TIME) {
-		if ((buflen -= 4) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 4))
 			goto out_resource;
-		WRITE32(nn->nfsd4_lease);
+		write32(&ptr, nn->nfsd4_lease);
 	}
 	if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) {
-		if ((buflen -= 4) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 4))
 			goto out_resource;
-		WRITE32(rdattr_err);
+		write32(&ptr, rdattr_err);
 	}
 	if (bmval0 & FATTR4_WORD0_ACL) {
 		struct nfs4_ace *ace;
 
 		if (acl == NULL) {
-			if ((buflen -= 4) < 0)
+			if (!svcxdr_reserve_space(xdr, &ptr, 4))
 				goto out_resource;
 
-			WRITE32(0);
+			write32(&ptr, 0);
 			goto out_acl;
 		}
-		if ((buflen -= 4) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 4))
 			goto out_resource;
-		WRITE32(acl->naces);
+		write32(&ptr, acl->naces);
 
 		for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
-			if ((buflen -= 4*3) < 0)
+			if (!svcxdr_reserve_space(xdr, &ptr, 4*3))
 				goto out_resource;
-			WRITE32(ace->type);
-			WRITE32(ace->flag);
-			WRITE32(ace->access_mask & NFS4_ACE_MASK_ALL);
-			status = nfsd4_encode_aclname(rqstp, ace->whotype,
-				ace->who, ace->flag & NFS4_ACE_IDENTIFIER_GROUP,
-				&p, &buflen);
+			write32(&ptr, ace->type);
+			write32(&ptr, ace->flag);
+			write32(&ptr, ace->access_mask & NFS4_ACE_MASK_ALL);
+			status = nfsd4_encode_aclname(xdr, rqstp, ace->whotype,
+				ace->who, ace->flag & NFS4_ACE_IDENTIFIER_GROUP);
 			if (status)
 				goto out;
 		}
 	}
 out_acl:
 	if (bmval0 & FATTR4_WORD0_ACLSUPPORT) {
-		if ((buflen -= 4) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 4))
 			goto out_resource;
-		WRITE32(aclsupport ?
+		write32(&ptr, aclsupport ?
 			ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0);
 	}
 	if (bmval0 & FATTR4_WORD0_CANSETTIME) {
-		if ((buflen -= 4) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 4))
 			goto out_resource;
-		WRITE32(1);
+		write32(&ptr, 1);
 	}
 	if (bmval0 & FATTR4_WORD0_CASE_INSENSITIVE) {
-		if ((buflen -= 4) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 4))
 			goto out_resource;
-		WRITE32(0);
+		write32(&ptr, 0);
 	}
 	if (bmval0 & FATTR4_WORD0_CASE_PRESERVING) {
-		if ((buflen -= 4) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 4))
 			goto out_resource;
-		WRITE32(1);
+		write32(&ptr, 1);
 	}
 	if (bmval0 & FATTR4_WORD0_CHOWN_RESTRICTED) {
-		if ((buflen -= 4) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 4))
 			goto out_resource;
-		WRITE32(1);
+		write32(&ptr, 1);
 	}
 	if (bmval0 & FATTR4_WORD0_FILEHANDLE) {
-		buflen -= (XDR_QUADLEN(fhp->fh_handle.fh_size) << 2) + 4;
-		if (buflen < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, (XDR_QUADLEN(fhp->fh_handle.fh_size) << 2) + 4))
 			goto out_resource;
-		WRITE32(fhp->fh_handle.fh_size);
-		WRITEMEM(&fhp->fh_handle.fh_base, fhp->fh_handle.fh_size);
+		write32(&ptr, fhp->fh_handle.fh_size);
+		writemem(&ptr, &fhp->fh_handle.fh_base, fhp->fh_handle.fh_size);
 	}
 	if (bmval0 & FATTR4_WORD0_FILEID) {
-		if ((buflen -= 8) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 8))
 			goto out_resource;
-		WRITE64(stat.ino);
+		write64(&ptr, stat.ino);
 	}
 	if (bmval0 & FATTR4_WORD0_FILES_AVAIL) {
-		if ((buflen -= 8) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 8))
 			goto out_resource;
-		WRITE64((u64) statfs.f_ffree);
+		write64(&ptr, (u64) statfs.f_ffree);
 	}
 	if (bmval0 & FATTR4_WORD0_FILES_FREE) {
-		if ((buflen -= 8) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 8))
 			goto out_resource;
-		WRITE64((u64) statfs.f_ffree);
+		write64(&ptr, (u64) statfs.f_ffree);
 	}
 	if (bmval0 & FATTR4_WORD0_FILES_TOTAL) {
-		if ((buflen -= 8) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 8))
 			goto out_resource;
-		WRITE64((u64) statfs.f_files);
+		write64(&ptr, (u64) statfs.f_files);
 	}
 	if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) {
-		status = nfsd4_encode_fs_locations(rqstp, exp, &p, &buflen);
+		status = nfsd4_encode_fs_locations(xdr, rqstp, exp);
 		if (status)
 			goto out;
 	}
 	if (bmval0 & FATTR4_WORD0_HOMOGENEOUS) {
-		if ((buflen -= 4) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 4))
 			goto out_resource;
-		WRITE32(1);
+		write32(&ptr, 1);
 	}
 	if (bmval0 & FATTR4_WORD0_MAXFILESIZE) {
-		if ((buflen -= 8) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 8))
 			goto out_resource;
-		WRITE64(~(u64)0);
+		write64(&ptr, ~(u64)0);
 	}
 	if (bmval0 & FATTR4_WORD0_MAXLINK) {
-		if ((buflen -= 4) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 4))
 			goto out_resource;
-		WRITE32(255);
+		write32(&ptr, 255);
 	}
 	if (bmval0 & FATTR4_WORD0_MAXNAME) {
-		if ((buflen -= 4) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 4))
 			goto out_resource;
-		WRITE32(statfs.f_namelen);
+		write32(&ptr, statfs.f_namelen);
 	}
 	if (bmval0 & FATTR4_WORD0_MAXREAD) {
-		if ((buflen -= 8) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 8))
 			goto out_resource;
-		WRITE64((u64) svc_max_payload(rqstp));
+		write64(&ptr, (u64) svc_max_payload(rqstp));
 	}
 	if (bmval0 & FATTR4_WORD0_MAXWRITE) {
-		if ((buflen -= 8) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 8))
 			goto out_resource;
-		WRITE64((u64) svc_max_payload(rqstp));
+		write64(&ptr, (u64) svc_max_payload(rqstp));
 	}
 	if (bmval1 & FATTR4_WORD1_MODE) {
-		if ((buflen -= 4) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 4))
 			goto out_resource;
-		WRITE32(stat.mode & S_IALLUGO);
+		write32(&ptr, stat.mode & S_IALLUGO);
 	}
 	if (bmval1 & FATTR4_WORD1_NO_TRUNC) {
-		if ((buflen -= 4) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 4))
 			goto out_resource;
-		WRITE32(1);
+		write32(&ptr, 1);
 	}
 	if (bmval1 & FATTR4_WORD1_NUMLINKS) {
-		if ((buflen -= 4) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 4))
 			goto out_resource;
-		WRITE32(stat.nlink);
+		write32(&ptr, stat.nlink);
 	}
 	if (bmval1 & FATTR4_WORD1_OWNER) {
-		status = nfsd4_encode_user(rqstp, stat.uid, &p, &buflen);
+		status = nfsd4_encode_user(xdr, rqstp, stat.uid);
 		if (status)
 			goto out;
 	}
 	if (bmval1 & FATTR4_WORD1_OWNER_GROUP) {
-		status = nfsd4_encode_group(rqstp, stat.gid, &p, &buflen);
+		status = nfsd4_encode_group(xdr, rqstp, stat.gid);
 		if (status)
 			goto out;
 	}
 	if (bmval1 & FATTR4_WORD1_RAWDEV) {
-		if ((buflen -= 8) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 8))
 			goto out_resource;
-		WRITE32((u32) MAJOR(stat.rdev));
-		WRITE32((u32) MINOR(stat.rdev));
+		write32(&ptr, (u32) MAJOR(stat.rdev));
+		write32(&ptr, (u32) MINOR(stat.rdev));
 	}
 	if (bmval1 & FATTR4_WORD1_SPACE_AVAIL) {
-		if ((buflen -= 8) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 8))
 			goto out_resource;
 		dummy64 = (u64)statfs.f_bavail * (u64)statfs.f_bsize;
-		WRITE64(dummy64);
+		write64(&ptr, dummy64);
 	}
 	if (bmval1 & FATTR4_WORD1_SPACE_FREE) {
-		if ((buflen -= 8) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 8))
 			goto out_resource;
 		dummy64 = (u64)statfs.f_bfree * (u64)statfs.f_bsize;
-		WRITE64(dummy64);
+		write64(&ptr, dummy64);
 	}
 	if (bmval1 & FATTR4_WORD1_SPACE_TOTAL) {
-		if ((buflen -= 8) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 8))
 			goto out_resource;
 		dummy64 = (u64)statfs.f_blocks * (u64)statfs.f_bsize;
-		WRITE64(dummy64);
+		write64(&ptr, dummy64);
 	}
 	if (bmval1 & FATTR4_WORD1_SPACE_USED) {
-		if ((buflen -= 8) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 8))
 			goto out_resource;
 		dummy64 = (u64)stat.blocks << 9;
-		WRITE64(dummy64);
+		write64(&ptr, dummy64);
 	}
 	if (bmval1 & FATTR4_WORD1_TIME_ACCESS) {
-		if ((buflen -= 12) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 12))
 			goto out_resource;
-		WRITE32(0);
-		WRITE32(stat.atime.tv_sec);
-		WRITE32(stat.atime.tv_nsec);
+		write32(&ptr, 0);
+		write32(&ptr, stat.atime.tv_sec);
+		write32(&ptr, stat.atime.tv_nsec);
 	}
 	if (bmval1 & FATTR4_WORD1_TIME_DELTA) {
-		if ((buflen -= 12) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 12))
 			goto out_resource;
-		WRITE32(0);
-		WRITE32(1);
-		WRITE32(0);
+		write32(&ptr, 0);
+		write32(&ptr, 1);
+		write32(&ptr, 0);
 	}
 	if (bmval1 & FATTR4_WORD1_TIME_METADATA) {
-		if ((buflen -= 12) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 12))
 			goto out_resource;
-		WRITE32(0);
-		WRITE32(stat.ctime.tv_sec);
-		WRITE32(stat.ctime.tv_nsec);
+		write32(&ptr, 0);
+		write32(&ptr, stat.ctime.tv_sec);
+		write32(&ptr, stat.ctime.tv_nsec);
 	}
 	if (bmval1 & FATTR4_WORD1_TIME_MODIFY) {
-		if ((buflen -= 12) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 12))
 			goto out_resource;
-		WRITE32(0);
-		WRITE32(stat.mtime.tv_sec);
-		WRITE32(stat.mtime.tv_nsec);
+		write32(&ptr, 0);
+		write32(&ptr, stat.mtime.tv_sec);
+		write32(&ptr, stat.mtime.tv_nsec);
 	}
 	if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) {
-		if ((buflen -= 8) < 0)
+		if (!svcxdr_reserve_space(xdr, &ptr, 8))
                 	goto out_resource;
 		/*
 		 * Get parent's attributes if not ignoring crossmount
@@ -2414,30 +2615,43 @@ out_acl:
 		if (ignore_crossmnt == 0 &&
 		    dentry == exp->ex_path.mnt->mnt_root)
 			get_parent_attributes(exp, &stat);
-		WRITE64(stat.ino);
+		write64(&ptr, stat.ino);
 	}
 	if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) {
-		WRITE32(3);
-		WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD0);
-		WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD1);
-		WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD2);
+		write32(&ptr, 3);
+		write32(&ptr, NFSD_SUPPATTR_EXCLCREAT_WORD0);
+		write32(&ptr, NFSD_SUPPATTR_EXCLCREAT_WORD1);
+		write32(&ptr, NFSD_SUPPATTR_EXCLCREAT_WORD2);
 	}
 
-	*attrlenp = htonl((char *)p - (char *)attrlenp - 4);
-	*buffer = p;
+	attrlen = svcxdr_byte_offset(&attrlenp, &xdr->ptr) - 4;
+	write32(&attrlenp, attrlen);
 	status = nfs_ok;
-
 out:
 	kfree(acl);
 	if (fhp == &tempfh)
 		fh_put(&tempfh);
 	return status;
+out_reset:
+	svcxdr_reset(xdr, &start);
+	goto out;
 out_nfserr:
 	status = nfserrno(err);
-	goto out;
+	goto out_reset;
 out_resource:
 	status = nfserr_resource;
-	goto out;
+	goto out_reset;
+}
+
+__be32 nfsd4_encode_fattr_to_buffer(__be32 **p, int count, struct svc_fh *fh, struct svc_export *exp, struct dentry *dentry, u32 *bmval, struct svc_rqst *rqstp, int ignore_crossmnt)
+{
+	struct svcxdr_stream xdr;
+	__be32 nfserr;
+
+	svcxdr_stream_init_from_buffer(&xdr, *p, count);
+	nfserr = nfsd4_encode_fattr(&xdr, fh, exp, dentry, bmval, rqstp, ignore_crossmnt);
+	svcxdr_stream_update_buffer(&xdr, p);
+	return nfserr;
 }
 
 static inline int attributes_need_mount(u32 *bmval)
@@ -2503,7 +2717,7 @@ nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
 
 	}
 out_encode:
-	nfserr = nfsd4_encode_fattr(NULL, exp, dentry, p, buflen, cd->rd_bmval,
+	nfserr = nfsd4_encode_fattr_to_buffer(p, buflen, NULL, exp, dentry, cd->rd_bmval,
 					cd->rd_rqstp, ignore_crossmnt);
 out_put:
 	dput(dentry);
@@ -2676,15 +2890,18 @@ static __be32
 nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_getattr *getattr)
 {
 	struct svc_fh *fhp = getattr->ga_fhp;
-	int buflen;
+	struct svcxdr_stream xdr;
 
 	if (nfserr)
 		return nfserr;
 
-	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,
+	nfserr = svcxdr_stream_init_from_resp(&xdr, resp);
+	if (nfserr)
+		return nfserr;
+	nfserr = nfsd4_encode_fattr(&xdr, fhp, fhp->fh_export, fhp->fh_dentry,
+				    getattr->ga_bmval,
 				    resp->rqstp, 0);
+	svcxdr_stream_update_resp(&xdr, resp);
 	return nfserr;
 }
 
@@ -3629,6 +3846,11 @@ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_comp
 	return !nfsd4_decode_compound(args);
 }
 
+static bool in_same_page(void *p, void *q)
+{
+	return ((unsigned long)p & PAGE_MASK) == ((unsigned long)q & PAGE_MASK);
+}
+
 int
 nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compoundres *resp)
 {
@@ -3636,19 +3858,22 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
 	 * All that remains is to write the tag and operation count...
 	 */
 	struct nfsd4_compound_state *cs = &resp->cstate;
-	struct kvec *iov;
+	struct kvec *iov = NULL;
 	p = resp->tagp;
 	*p++ = htonl(resp->taglen);
 	memcpy(p, resp->tag, resp->taglen);
 	p += XDR_QUADLEN(resp->taglen);
 	*p++ = htonl(resp->opcnt);
 
-	if (rqstp->rq_res.page_len) 
+	/* Hacky: detect whether resp->p is in head or tail, fix up
+	 * length accordingly: */
+	if (in_same_page(resp->p, rqstp->rq_res.tail[0].iov_base))
 		iov = &rqstp->rq_res.tail[0];
-	else
+	else if (in_same_page(resp->p, rqstp->rq_res.head[0].iov_base))
 		iov = &rqstp->rq_res.head[0];
-	iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base;
-	BUG_ON(iov->iov_len > PAGE_SIZE);
+	if (iov)
+		iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base;
+	BUG_ON(iov && iov->iov_len > PAGE_SIZE);
 	if (nfsd4_has_session(cs)) {
 		if (cs->status != nfserr_replay_cache) {
 			nfsd4_store_cache_entry(resp);
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 546f898..4d9c82b 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -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);
-- 
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


[Index of Archives]     [Linux Filesystem Development]     [Linux USB Development]     [Linux Media Development]     [Video for Linux]     [Linux NILFS]     [Linux Audio Users]     [Yosemite Info]     [Linux SCSI]

  Powered by Linux