Commit cf570a93 authored by Chuck Lever's avatar Chuck Lever Committed by J. Bruce Fields

svcrdma: Do not write xdr_buf::tail in a Write chunk

When the Linux NFS server writes an odd-length data item into a
Write chunk, it finishes with XDR pad bytes. If the data item is
smaller than the Write chunk, the pad bytes are written at the end
of the data item, but still inside the chunk (ie, in the
application's buffer). Since this is direct data placement, that
exposes the pad bytes.

XDR pad bytes are inserted in order to preserve the XDR alignment
of the next XDR data item in an XDR stream. But Write chunks do not
appear in the payload XDR stream, and only one data item is allowed
in each chunk. Thus XDR padding is not needed in a Write chunk.

With NFSv4, the Linux NFS server places the results of any
operations that follow an NFSv4 READ or READLINK in the xdr_buf's
tail. Those results also should never be sent as a part of a Write
chunk. The current logic in send_write_chunks() appears to assume
that the xdr_buf's tail contains only pad bytes (ie, NFSv3).

The server should write only the contents of the xdr_buf's page list
in a Write chunk. If there's more than an XDR pad in the tail, that
needs to go inline or in the Reply chunk.

BugLink: https://bugzilla.linux-nfs.org/show_bug.cgi?id=294Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent 08ae4e7f
...@@ -50,6 +50,11 @@ ...@@ -50,6 +50,11 @@
#define RPCDBG_FACILITY RPCDBG_SVCXPRT #define RPCDBG_FACILITY RPCDBG_SVCXPRT
static u32 xdr_padsize(u32 len)
{
return (len & 3) ? (4 - (len & 3)) : 0;
}
int svc_rdma_map_xdr(struct svcxprt_rdma *xprt, int svc_rdma_map_xdr(struct svcxprt_rdma *xprt,
struct xdr_buf *xdr, struct xdr_buf *xdr,
struct svc_rdma_req_map *vec) struct svc_rdma_req_map *vec)
...@@ -308,7 +313,7 @@ static int send_write_chunks(struct svcxprt_rdma *xprt, ...@@ -308,7 +313,7 @@ static int send_write_chunks(struct svcxprt_rdma *xprt,
struct svc_rqst *rqstp, struct svc_rqst *rqstp,
struct svc_rdma_req_map *vec) struct svc_rdma_req_map *vec)
{ {
u32 xfer_len = rqstp->rq_res.page_len + rqstp->rq_res.tail[0].iov_len; u32 xfer_len = rqstp->rq_res.page_len;
int write_len; int write_len;
u32 xdr_off; u32 xdr_off;
int chunk_off; int chunk_off;
...@@ -357,7 +362,7 @@ static int send_write_chunks(struct svcxprt_rdma *xprt, ...@@ -357,7 +362,7 @@ static int send_write_chunks(struct svcxprt_rdma *xprt,
/* Update the req with the number of chunks actually used */ /* Update the req with the number of chunks actually used */
svc_rdma_xdr_encode_write_list(rdma_resp, chunk_no); svc_rdma_xdr_encode_write_list(rdma_resp, chunk_no);
return rqstp->rq_res.page_len + rqstp->rq_res.tail[0].iov_len; return rqstp->rq_res.page_len;
out_err: out_err:
pr_err("svcrdma: failed to send write chunks, rc=%d\n", ret); pr_err("svcrdma: failed to send write chunks, rc=%d\n", ret);
...@@ -612,7 +617,7 @@ int svc_rdma_sendto(struct svc_rqst *rqstp) ...@@ -612,7 +617,7 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
ret = send_write_chunks(rdma, wr_ary, rdma_resp, rqstp, vec); ret = send_write_chunks(rdma, wr_ary, rdma_resp, rqstp, vec);
if (ret < 0) if (ret < 0)
goto err1; goto err1;
inline_bytes -= ret; inline_bytes -= ret + xdr_padsize(ret);
} }
/* Send any reply-list data and update resp reply-list */ /* Send any reply-list data and update resp reply-list */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment