Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2825a7f9 authored by J. Bruce Fields's avatar J. Bruce Fields
Browse files

nfsd4: allow encoding across page boundaries



After this we can handle for example getattr of very large ACLs.

Read, readdir, readlink are still special cases with their own limits.

Also we can't handle a new operation starting close to the end of a
page.

Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent a8095f7e
Loading
Loading
Loading
Loading
+4 −0
Original line number Original line Diff line number Diff line
@@ -1264,6 +1264,10 @@ static void svcxdr_init_encode(struct svc_rqst *rqstp,
	xdr->end = head->iov_base + PAGE_SIZE - 2 * RPC_MAX_AUTH_SIZE;
	xdr->end = head->iov_base + PAGE_SIZE - 2 * RPC_MAX_AUTH_SIZE;
	/* Tail and page_len should be zero at this point: */
	/* Tail and page_len should be zero at this point: */
	buf->len = buf->head[0].iov_len;
	buf->len = buf->head[0].iov_len;
	xdr->scratch.iov_len = 0;
	xdr->page_ptr = buf->pages;
	buf->buflen = PAGE_SIZE * (1 + rqstp->rq_page_end - buf->pages)
		- 2 * RPC_MAX_AUTH_SIZE;
}
}


/*
/*
+43 −17
Original line number Original line Diff line number Diff line
@@ -1624,6 +1624,7 @@ static int nfsd4_max_reply(u32 opnum)
		 * the head and tail in another page:
		 * the head and tail in another page:
		 */
		 */
		return 2 * PAGE_SIZE;
		return 2 * PAGE_SIZE;
	case OP_GETATTR:
	case OP_READ:
	case OP_READ:
		return INT_MAX;
		return INT_MAX;
	default:
	default:
@@ -2560,21 +2561,31 @@ out_resource:
	goto out;
	goto out;
}
}


static void svcxdr_init_encode_from_buffer(struct xdr_stream *xdr,
				struct xdr_buf *buf, __be32 *p, int bytes)
{
	xdr->scratch.iov_len = 0;
	memset(buf, 0, sizeof(struct xdr_buf));
	buf->head[0].iov_base = p;
	buf->head[0].iov_len = 0;
	buf->len = 0;
	xdr->buf = buf;
	xdr->iov = buf->head;
	xdr->p = p;
	xdr->end = (void *)p + bytes;
	buf->buflen = bytes;
}

__be32 nfsd4_encode_fattr_to_buf(__be32 **p, int words,
__be32 nfsd4_encode_fattr_to_buf(__be32 **p, int words,
			struct svc_fh *fhp, struct svc_export *exp,
			struct svc_fh *fhp, struct svc_export *exp,
			struct dentry *dentry, u32 *bmval,
			struct dentry *dentry, u32 *bmval,
			struct svc_rqst *rqstp, int ignore_crossmnt)
			struct svc_rqst *rqstp, int ignore_crossmnt)
{
{
	struct xdr_buf dummy = {
	struct xdr_buf dummy;
			.head[0] = {
				.iov_base = *p,
			},
			.buflen = words << 2,
		};
	struct xdr_stream xdr;
	struct xdr_stream xdr;
	__be32 ret;
	__be32 ret;


	xdr_init_encode(&xdr, &dummy, NULL);
	svcxdr_init_encode_from_buffer(&xdr, &dummy, *p, words << 2);
	ret = nfsd4_encode_fattr(&xdr, fhp, exp, dentry, bmval, rqstp,
	ret = nfsd4_encode_fattr(&xdr, fhp, exp, dentry, bmval, rqstp,
							ignore_crossmnt);
							ignore_crossmnt);
	*p = xdr.p;
	*p = xdr.p;
@@ -3064,8 +3075,6 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,


	if (nfserr)
	if (nfserr)
		return nfserr;
		return nfserr;
	if (resp->xdr.buf->page_len)
		return nfserr_resource;


	p = xdr_reserve_space(xdr, 8); /* eof flag and byte count */
	p = xdr_reserve_space(xdr, 8); /* eof flag and byte count */
	if (!p)
	if (!p)
@@ -3075,6 +3084,9 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
	if (xdr->end - xdr->p < 1)
	if (xdr->end - xdr->p < 1)
		return nfserr_resource;
		return nfserr_resource;


	if (resp->xdr.buf->page_len)
		return nfserr_resource;

	maxcount = svc_max_payload(resp->rqstp);
	maxcount = svc_max_payload(resp->rqstp);
	if (maxcount > read->rd_length)
	if (maxcount > read->rd_length)
		maxcount = read->rd_length;
		maxcount = read->rd_length;
@@ -3119,6 +3131,8 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
				- (char *)resp->xdr.buf->head[0].iov_base);
				- (char *)resp->xdr.buf->head[0].iov_base);
	resp->xdr.buf->page_len = maxcount;
	resp->xdr.buf->page_len = maxcount;
	xdr->buf->len += maxcount;
	xdr->buf->len += maxcount;
	xdr->page_ptr += v;
	xdr->buf->buflen = maxcount + PAGE_SIZE - 2 * RPC_MAX_AUTH_SIZE;
	xdr->iov = xdr->buf->tail;
	xdr->iov = xdr->buf->tail;


	/* Use rest of head for padding and remaining ops: */
	/* Use rest of head for padding and remaining ops: */
@@ -3145,6 +3159,11 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd


	if (nfserr)
	if (nfserr)
		return nfserr;
		return nfserr;

	p = xdr_reserve_space(xdr, 4);
	if (!p)
		return nfserr_resource;

	if (resp->xdr.buf->page_len)
	if (resp->xdr.buf->page_len)
		return nfserr_resource;
		return nfserr_resource;
	if (!*resp->rqstp->rq_next_page)
	if (!*resp->rqstp->rq_next_page)
@@ -3154,10 +3173,6 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd


	maxcount = PAGE_SIZE;
	maxcount = PAGE_SIZE;


	p = xdr_reserve_space(xdr, 4);
	if (!p)
		return nfserr_resource;

	if (xdr->end - xdr->p < 1)
	if (xdr->end - xdr->p < 1)
		return nfserr_resource;
		return nfserr_resource;


@@ -3180,6 +3195,8 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd
				- (char *)resp->xdr.buf->head[0].iov_base;
				- (char *)resp->xdr.buf->head[0].iov_base;
	resp->xdr.buf->page_len = maxcount;
	resp->xdr.buf->page_len = maxcount;
	xdr->buf->len += maxcount;
	xdr->buf->len += maxcount;
	xdr->page_ptr += 1;
	xdr->buf->buflen -= PAGE_SIZE;
	xdr->iov = xdr->buf->tail;
	xdr->iov = xdr->buf->tail;


	/* Use rest of head for padding and remaining ops: */
	/* Use rest of head for padding and remaining ops: */
@@ -3206,15 +3223,16 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4


	if (nfserr)
	if (nfserr)
		return nfserr;
		return nfserr;
	if (resp->xdr.buf->page_len)
		return nfserr_resource;
	if (!*resp->rqstp->rq_next_page)
		return nfserr_resource;


	p = xdr_reserve_space(xdr, NFS4_VERIFIER_SIZE);
	p = xdr_reserve_space(xdr, NFS4_VERIFIER_SIZE);
	if (!p)
	if (!p)
		return nfserr_resource;
		return nfserr_resource;


	if (resp->xdr.buf->page_len)
		return nfserr_resource;
	if (!*resp->rqstp->rq_next_page)
		return nfserr_resource;

	/* XXX: Following NFSv3, we ignore the READDIR verifier for now. */
	/* XXX: Following NFSv3, we ignore the READDIR verifier for now. */
	WRITE32(0);
	WRITE32(0);
	WRITE32(0);
	WRITE32(0);
@@ -3266,6 +3284,10 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4


	xdr->iov = xdr->buf->tail;
	xdr->iov = xdr->buf->tail;


	xdr->page_ptr++;
	xdr->buf->buflen -= PAGE_SIZE;
	xdr->iov = xdr->buf->tail;

	/* Use rest of head for padding and remaining ops: */
	/* Use rest of head for padding and remaining ops: */
	resp->xdr.buf->tail[0].iov_base = tailbase;
	resp->xdr.buf->tail[0].iov_base = tailbase;
	resp->xdr.buf->tail[0].iov_len = 0;
	resp->xdr.buf->tail[0].iov_len = 0;
@@ -3800,6 +3822,8 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
	       !nfsd4_enc_ops[op->opnum]);
	       !nfsd4_enc_ops[op->opnum]);
	encoder = nfsd4_enc_ops[op->opnum];
	encoder = nfsd4_enc_ops[op->opnum];
	op->status = encoder(resp, op->status, &op->u);
	op->status = encoder(resp, op->status, &op->u);
	xdr_commit_encode(xdr);

	/* nfsd4_check_resp_size guarantees enough room for error status */
	/* nfsd4_check_resp_size guarantees enough room for error status */
	if (!op->status) {
	if (!op->status) {
		int space_needed = 0;
		int space_needed = 0;
@@ -3919,6 +3943,8 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
	WARN_ON_ONCE(buf->len != buf->head[0].iov_len + buf->page_len +
	WARN_ON_ONCE(buf->len != buf->head[0].iov_len + buf->page_len +
				 buf->tail[0].iov_len);
				 buf->tail[0].iov_len);


	rqstp->rq_next_page = resp->xdr.page_ptr + 1;

	p = resp->tagp;
	p = resp->tagp;
	*p++ = htonl(resp->taglen);
	*p++ = htonl(resp->taglen);
	memcpy(p, resp->tag, resp->taglen);
	memcpy(p, resp->tag, resp->taglen);
+1 −0
Original line number Original line Diff line number Diff line
@@ -244,6 +244,7 @@ struct svc_rqst {
	struct page *		rq_pages[RPCSVC_MAXPAGES];
	struct page *		rq_pages[RPCSVC_MAXPAGES];
	struct page *		*rq_respages;	/* points into rq_pages */
	struct page *		*rq_respages;	/* points into rq_pages */
	struct page *		*rq_next_page; /* next reply page to use */
	struct page *		*rq_next_page; /* next reply page to use */
	struct page *		*rq_page_end;  /* one past the last page */


	struct kvec		rq_vec[RPCSVC_MAXPAGES]; /* generally useful.. */
	struct kvec		rq_vec[RPCSVC_MAXPAGES]; /* generally useful.. */


+1 −0
Original line number Original line Diff line number Diff line
@@ -215,6 +215,7 @@ typedef int (*kxdrdproc_t)(void *rqstp, struct xdr_stream *xdr, void *obj);


extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p);
extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p);
extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes);
extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes);
extern void xdr_commit_encode(struct xdr_stream *xdr);
extern void xdr_truncate_encode(struct xdr_stream *xdr, size_t len);
extern void xdr_truncate_encode(struct xdr_stream *xdr, size_t len);
extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages,
extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages,
		unsigned int base, unsigned int len);
		unsigned int base, unsigned int len);
+1 −0
Original line number Original line Diff line number Diff line
@@ -597,6 +597,7 @@ static int svc_alloc_arg(struct svc_rqst *rqstp)
			}
			}
			rqstp->rq_pages[i] = p;
			rqstp->rq_pages[i] = p;
		}
		}
	rqstp->rq_page_end = &rqstp->rq_pages[i];
	rqstp->rq_pages[i++] = NULL; /* this might be seen in nfs_read_actor */
	rqstp->rq_pages[i++] = NULL; /* this might be seen in nfs_read_actor */


	/* Make arg->head point to first page and arg->pages point to rest */
	/* Make arg->head point to first page and arg->pages point to rest */
Loading