Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

svcrdma: Properly compute .len and .buflen for received RPC Calls

When an RPC-over-RDMA request is received, the Receive buffer
contains a Transport Header possibly followed by an RPC message.

Even though rq_arg.head[0] (as passed to NFSD) does not contain the
Transport Header header, currently rq_arg.len includes the size of
the Transport Header.

That violates the intent of the xdr_buf API contract. .buflen should
include everything, but .len should be exactly the length of the RPC
message in the buffer.

The rq_arg fields are summed together at the end of
svc_rdma_recvfrom to obtain the correct return value. rq_arg.len
really ought to contain the correct number of bytes already, but it
currently doesn't due to the above misbehavior.

Let's instead ensure that .buflen includes the length of the
transport header, and that .len is always equal to head.iov_len +
.page_len + tail.iov_len .

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>

authored by

Chuck Lever and committed by
J. Bruce Fields
71641d99 cafc7398

+5 -11
+4 -10
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
··· 143 143 put_page(rqstp->rq_pages[sge_no]); 144 144 rqstp->rq_pages[sge_no] = page; 145 145 bc -= min_t(u32, bc, ctxt->sge[sge_no].length); 146 - rqstp->rq_arg.buflen += ctxt->sge[sge_no].length; 147 146 sge_no++; 148 147 } 149 148 rqstp->rq_respages = &rqstp->rq_pages[sge_no]; ··· 337 338 rq_arg->head[0].iov_base = p; 338 339 hdr_len = (unsigned long)p - (unsigned long)rdma_argp; 339 340 rq_arg->head[0].iov_len -= hdr_len; 341 + rq_arg->len -= hdr_len; 340 342 dprintk("svcrdma: received %s request for XID 0x%08x, hdr_len=%u\n", 341 343 proc, be32_to_cpup(rdma_argp), hdr_len); 342 344 return hdr_len; ··· 564 564 goto out_readchunk; 565 565 566 566 complete: 567 - ret = rqstp->rq_arg.head[0].iov_len 568 - + rqstp->rq_arg.page_len 569 - + rqstp->rq_arg.tail[0].iov_len; 570 567 svc_rdma_put_context(ctxt, 0); 571 - dprintk("svcrdma: ret=%d, rq_arg.len=%u, " 572 - "rq_arg.head[0].iov_base=%p, rq_arg.head[0].iov_len=%zd\n", 573 - ret, rqstp->rq_arg.len, 574 - rqstp->rq_arg.head[0].iov_base, 575 - rqstp->rq_arg.head[0].iov_len); 568 + dprintk("svcrdma: recvfrom: xprt=%p, rqstp=%p, rq_arg.len=%u\n", 569 + rdma_xprt, rqstp, rqstp->rq_arg.len); 576 570 rqstp->rq_prot = IPPROTO_MAX; 577 571 svc_xprt_copy_addrs(rqstp, xprt); 578 - return ret; 572 + return rqstp->rq_arg.len; 579 573 580 574 out_readchunk: 581 575 ret = svc_rdma_recv_read_chunk(rdma_xprt, rqstp, ctxt, p);
+1 -1
net/sunrpc/xprtrdma/svc_rdma_rw.c
··· 848 848 head->arg.len += info->ri_chunklen; 849 849 head->arg.buflen += info->ri_chunklen; 850 850 851 - if (head->arg.len <= head->sge[0].length) { 851 + if (head->arg.buflen <= head->sge[0].length) { 852 852 /* Transport header and RPC message fit entirely 853 853 * in page where head iovec resides. 854 854 */