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

Commit 512570b1 authored by Chuck Lever's avatar Chuck Lever Committed by Greg Kroah-Hartman
Browse files

nfsd: Fix NFSv4 READ on RDMA when using readv

commit 412055398b9e67e07347a936fc4a6adddabe9cf4 upstream.

svcrdma expects that the payload falls precisely into the xdr_buf
page vector. This does not seem to be the case for
nfsd4_encode_readv().

This code is called only when fops->splice_read is missing or when
RQ_SPLICE_OK is clear, so it's not a noticeable problem in many
common cases.

Add new transport method: ->xpo_read_payload so that when a READ
payload does not fit exactly in rq_res's page vector, the XDR
encoder can inform the RPC transport exactly where that payload is,
without the payload's XDR pad.

That way, when a Write chunk is present, the transport knows what
byte range in the Reply message is supposed to be matched with the
chunk.

Note that the Linux NFS server implementation of NFS/RDMA can
currently handle only one Write chunk per RPC-over-RDMA message.
This simplifies the implementation of this fix.

Fixes: b0420980 ("nfsd4: allow exotic read compounds")
Buglink: https://bugzilla.kernel.org/show_bug.cgi?id=198053


Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
Cc: Timo Rothenpieler <timo@rothenpieler.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent df6aeb52
Loading
Loading
Loading
Loading
+11 −9
Original line number Diff line number Diff line
@@ -3530,17 +3530,17 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
	u32 zzz = 0;
	int pad;

	/*
	 * svcrdma requires every READ payload to start somewhere
	 * in xdr->pages.
	 */
	if (xdr->iov == xdr->buf->head) {
		xdr->iov = NULL;
		xdr->end = xdr->p;
	}

	len = maxcount;
	v = 0;

	thislen = min_t(long, len, ((void *)xdr->end - (void *)xdr->p));
	p = xdr_reserve_space(xdr, (thislen+3)&~3);
	WARN_ON_ONCE(!p);
	resp->rqstp->rq_vec[v].iov_base = p;
	resp->rqstp->rq_vec[v].iov_len = thislen;
	v++;
	len -= thislen;

	while (len) {
		thislen = min_t(long, len, PAGE_SIZE);
		p = xdr_reserve_space(xdr, (thislen+3)&~3);
@@ -3559,6 +3559,8 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
	read->rd_length = maxcount;
	if (nfserr)
		return nfserr;
	if (svc_encode_read_payload(resp->rqstp, starting_len + 8, maxcount))
		return nfserr_io;
	xdr_truncate_encode(xdr, starting_len + 8 + ((maxcount+3)&~3));

	tmp = htonl(eof);
+3 −0
Original line number Diff line number Diff line
@@ -517,6 +517,9 @@ void svc_wake_up(struct svc_serv *);
void		   svc_reserve(struct svc_rqst *rqstp, int space);
struct svc_pool *  svc_pool_for_cpu(struct svc_serv *serv, int cpu);
char *		   svc_print_addr(struct svc_rqst *, char *, size_t);
int		   svc_encode_read_payload(struct svc_rqst *rqstp,
					   unsigned int offset,
					   unsigned int length);
unsigned int	   svc_fill_write_vector(struct svc_rqst *rqstp,
					 struct page **pages,
					 struct kvec *first, size_t total);
+7 −1
Original line number Diff line number Diff line
@@ -137,6 +137,8 @@ struct svc_rdma_recv_ctxt {
	unsigned int		rc_page_count;
	unsigned int		rc_hdr_count;
	u32			rc_inv_rkey;
	unsigned int		rc_read_payload_offset;
	unsigned int		rc_read_payload_length;
	struct page		*rc_pages[RPCSVC_MAXPAGES];
};

@@ -171,7 +173,9 @@ extern int svc_rdma_recv_read_chunk(struct svcxprt_rdma *rdma,
				    struct svc_rqst *rqstp,
				    struct svc_rdma_recv_ctxt *head, __be32 *p);
extern int svc_rdma_send_write_chunk(struct svcxprt_rdma *rdma,
				     __be32 *wr_ch, struct xdr_buf *xdr);
				     __be32 *wr_ch, struct xdr_buf *xdr,
				     unsigned int offset,
				     unsigned long length);
extern int svc_rdma_send_reply_chunk(struct svcxprt_rdma *rdma,
				     __be32 *rp_ch, bool writelist,
				     struct xdr_buf *xdr);
@@ -190,6 +194,8 @@ extern int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma,
				  struct svc_rdma_send_ctxt *ctxt,
				  struct xdr_buf *xdr, __be32 *wr_lst);
extern int svc_rdma_sendto(struct svc_rqst *);
extern int svc_rdma_read_payload(struct svc_rqst *rqstp, unsigned int offset,
				 unsigned int length);

/* svc_rdma_transport.c */
extern int svc_rdma_create_listen(struct svc_serv *, int, struct sockaddr *);
+2 −0
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@ struct svc_xprt_ops {
	int		(*xpo_has_wspace)(struct svc_xprt *);
	int		(*xpo_recvfrom)(struct svc_rqst *);
	int		(*xpo_sendto)(struct svc_rqst *);
	int		(*xpo_read_payload)(struct svc_rqst *, unsigned int,
					    unsigned int);
	void		(*xpo_release_rqst)(struct svc_rqst *);
	void		(*xpo_detach)(struct svc_xprt *);
	void		(*xpo_free)(struct svc_xprt *);
+16 −0
Original line number Diff line number Diff line
@@ -1634,6 +1634,22 @@ u32 svc_max_payload(const struct svc_rqst *rqstp)
}
EXPORT_SYMBOL_GPL(svc_max_payload);

/**
 * svc_encode_read_payload - mark a range of bytes as a READ payload
 * @rqstp: svc_rqst to operate on
 * @offset: payload's byte offset in rqstp->rq_res
 * @length: size of payload, in bytes
 *
 * Returns zero on success, or a negative errno if a permanent
 * error occurred.
 */
int svc_encode_read_payload(struct svc_rqst *rqstp, unsigned int offset,
			    unsigned int length)
{
	return rqstp->rq_xprt->xpt_ops->xpo_read_payload(rqstp, offset, length);
}
EXPORT_SYMBOL_GPL(svc_encode_read_payload);

/**
 * svc_fill_write_vector - Construct data argument for VFS write call
 * @rqstp: svc_rqst to operate on
Loading