Commit 528b8493 authored by Anna Schumaker's avatar Anna Schumaker Committed by J. Bruce Fields
Browse files

NFSD: Add READ_PLUS data support



This patch adds READ_PLUS support for returning a single
NFS4_CONTENT_DATA segment to the client. This is basically the same as
the READ operation, only with the extra information about data segments.

Signed-off-by: default avatarAnna Schumaker <Anna.Schumaker@Netapp.com>
Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent cc028a10
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -2590,6 +2590,20 @@ static inline u32 nfsd4_read_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
	return (op_encode_hdr_size + 2 + XDR_QUADLEN(rlen)) * sizeof(__be32);
}

static inline u32 nfsd4_read_plus_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
{
	u32 maxcount = svc_max_payload(rqstp);
	u32 rlen = min(op->u.read.rd_length, maxcount);
	/*
	 * If we detect that the file changed during hole encoding, then we
	 * recover by encoding the remaining reply as data. This means we need
	 * to set aside enough room to encode two data segments.
	 */
	u32 seg_len = 2 * (1 + 2 + 1);

	return (op_encode_hdr_size + 2 + seg_len + XDR_QUADLEN(rlen)) * sizeof(__be32);
}

static inline u32 nfsd4_readdir_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
{
	u32 maxcount = 0, rlen = 0;
@@ -3162,6 +3176,13 @@ static const struct nfsd4_operation nfsd4_ops[] = {
		.op_name = "OP_COPY",
		.op_rsize_bop = nfsd4_copy_rsize,
	},
	[OP_READ_PLUS] = {
		.op_func = nfsd4_read,
		.op_release = nfsd4_read_release,
		.op_name = "OP_READ_PLUS",
		.op_rsize_bop = nfsd4_read_plus_rsize,
		.op_get_currentstateid = nfsd4_get_readstateid,
	},
	[OP_SEEK] = {
		.op_func = nfsd4_seek,
		.op_name = "OP_SEEK",
+84 −3
Original line number Diff line number Diff line
@@ -2173,7 +2173,7 @@ static const nfsd4_dec nfsd4_dec_ops[] = {
	[OP_LAYOUTSTATS]	= (nfsd4_dec)nfsd4_decode_notsupp,
	[OP_OFFLOAD_CANCEL]	= (nfsd4_dec)nfsd4_decode_offload_status,
	[OP_OFFLOAD_STATUS]	= (nfsd4_dec)nfsd4_decode_offload_status,
	[OP_READ_PLUS]		= (nfsd4_dec)nfsd4_decode_notsupp,
	[OP_READ_PLUS]		= (nfsd4_dec)nfsd4_decode_read,
	[OP_SEEK]		= (nfsd4_dec)nfsd4_decode_seek,
	[OP_WRITE_SAME]		= (nfsd4_dec)nfsd4_decode_notsupp,
	[OP_CLONE]		= (nfsd4_dec)nfsd4_decode_clone,
@@ -2261,7 +2261,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
		 */
		cachethis |= nfsd4_cache_this_op(op);

		if (op->opnum == OP_READ) {
		if (op->opnum == OP_READ || op->opnum == OP_READ_PLUS) {
			readcount++;
			readbytes += nfsd4_max_reply(argp->rqstp, op);
		} else
@@ -4597,6 +4597,87 @@ nfsd4_encode_offload_status(struct nfsd4_compoundres *resp, __be32 nfserr,
		return nfserr_resource;
	p = xdr_encode_hyper(p, os->count);
	*p++ = cpu_to_be32(0);
	return nfserr;
}

static __be32
nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp,
			    struct nfsd4_read *read,
			    unsigned long maxcount,  u32 *eof)
{
	struct xdr_stream *xdr = &resp->xdr;
	struct file *file = read->rd_nf->nf_file;
	int starting_len = xdr->buf->len;
	__be32 nfserr;
	__be32 *p, tmp;
	__be64 tmp64;

	maxcount = min_t(unsigned long, maxcount, (xdr->buf->buflen - xdr->buf->len));

	/* Content type, offset, byte count */
	p = xdr_reserve_space(xdr, 4 + 8 + 4);
	if (!p)
		return nfserr_resource;

	read->rd_vlen = xdr_reserve_space_vec(xdr, resp->rqstp->rq_vec, maxcount);
	if (read->rd_vlen < 0)
		return nfserr_resource;

	nfserr = nfsd_readv(resp->rqstp, read->rd_fhp, file, read->rd_offset,
			    resp->rqstp->rq_vec, read->rd_vlen, &maxcount, eof);
	if (nfserr)
		return nfserr;

	tmp = htonl(NFS4_CONTENT_DATA);
	write_bytes_to_xdr_buf(xdr->buf, starting_len,      &tmp,   4);
	tmp64 = cpu_to_be64(read->rd_offset);
	write_bytes_to_xdr_buf(xdr->buf, starting_len + 4,  &tmp64, 8);
	tmp = htonl(maxcount);
	write_bytes_to_xdr_buf(xdr->buf, starting_len + 12, &tmp,   4);
	return nfs_ok;
}

static __be32
nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr,
		       struct nfsd4_read *read)
{
	unsigned long maxcount;
	struct xdr_stream *xdr = &resp->xdr;
	struct file *file;
	int starting_len = xdr->buf->len;
	int segments = 0;
	__be32 *p, tmp;
	u32 eof;

	if (nfserr)
		return nfserr;
	file = read->rd_nf->nf_file;

	/* eof flag, segment count */
	p = xdr_reserve_space(xdr, 4 + 4);
	if (!p)
		return nfserr_resource;
	xdr_commit_encode(xdr);

	maxcount = svc_max_payload(resp->rqstp);
	maxcount = min_t(unsigned long, maxcount,
			 (xdr->buf->buflen - xdr->buf->len));
	maxcount = min_t(unsigned long, maxcount, read->rd_length);

	eof = read->rd_offset >= i_size_read(file_inode(file));
	if (!eof) {
		nfserr = nfsd4_encode_read_plus_data(resp, read, maxcount, &eof);
		segments++;
	}

	if (nfserr)
		xdr_truncate_encode(xdr, starting_len);
	else {
		tmp = htonl(eof);
		write_bytes_to_xdr_buf(xdr->buf, starting_len,     &tmp, 4);
		tmp = htonl(segments);
		write_bytes_to_xdr_buf(xdr->buf, starting_len + 4, &tmp, 4);
	}

	return nfserr;
}
@@ -4974,7 +5055,7 @@ static const nfsd4_enc nfsd4_enc_ops[] = {
	[OP_LAYOUTSTATS]	= (nfsd4_enc)nfsd4_encode_noop,
	[OP_OFFLOAD_CANCEL]	= (nfsd4_enc)nfsd4_encode_noop,
	[OP_OFFLOAD_STATUS]	= (nfsd4_enc)nfsd4_encode_offload_status,
	[OP_READ_PLUS]		= (nfsd4_enc)nfsd4_encode_noop,
	[OP_READ_PLUS]		= (nfsd4_enc)nfsd4_encode_read_plus,
	[OP_SEEK]		= (nfsd4_enc)nfsd4_encode_seek,
	[OP_WRITE_SAME]		= (nfsd4_enc)nfsd4_encode_noop,
	[OP_CLONE]		= (nfsd4_enc)nfsd4_encode_noop,