xprtrdma: Check inline size before providing a Write chunk
authorChuck Lever <chuck.lever@oracle.com>
Mon, 11 Feb 2019 16:23:49 +0000 (11:23 -0500)
committerAnna Schumaker <Anna.Schumaker@Netapp.com>
Wed, 13 Feb 2019 15:19:06 +0000 (10:19 -0500)
In very rare cases, an NFS READ operation might predict that the
non-payload part of the RPC Call is large. For instance, an
NFSv4 COMPOUND with a large GETATTR result, in combination with a
large Kerberos credential, could push the non-payload part to be
several kilobytes.

If the non-payload part is larger than the connection's inline
threshold, the client is required to provision a Reply chunk. The
current Linux client does not check for this case. There are two
obvious ways to handle it:

a. Provision a Write chunk for the payload and a Reply chunk for
   the non-payload part

b. Provision a Reply chunk for the whole RPC Reply

Some testing at a recent NFS bake-a-thon showed that servers can
mostly handle a. but there are some corner cases that do not work
yet. b. already works (it has to, to handle krb5i/p), but could be
somewhat less efficient. However, I expect this scenario to be very
rare -- no-one has reported a problem yet.

So I'm going to implement b. Sometime later I will provide some
patches to help make b. a little more efficient by more carefully
choosing the Reply chunk's segment sizes to ensure the payload is
optimally aligned.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
net/sunrpc/xprtrdma/rpc_rdma.c

index d18614e02b4e8d5e31e00795397b64cc7bfc0463..7774aee7c01387d7df74c91faa27543ffc8a337c 100644 (file)
@@ -164,6 +164,21 @@ static bool rpcrdma_results_inline(struct rpcrdma_xprt *r_xprt,
        return rqst->rq_rcv_buf.buflen <= ia->ri_max_inline_read;
 }
 
+/* The client is required to provide a Reply chunk if the maximum
+ * size of the non-payload part of the RPC Reply is larger than
+ * the inline threshold.
+ */
+static bool
+rpcrdma_nonpayload_inline(const struct rpcrdma_xprt *r_xprt,
+                         const struct rpc_rqst *rqst)
+{
+       const struct xdr_buf *buf = &rqst->rq_rcv_buf;
+       const struct rpcrdma_ia *ia = &r_xprt->rx_ia;
+
+       return buf->head[0].iov_len + buf->tail[0].iov_len <
+               ia->ri_max_inline_read;
+}
+
 /* Split @vec on page boundaries into SGEs. FMR registers pages, not
  * a byte range. Other modes coalesce these SGEs into a single MR
  * when they can.
@@ -762,7 +777,8 @@ rpcrdma_marshal_req(struct rpcrdma_xprt *r_xprt, struct rpc_rqst *rqst)
         */
        if (rpcrdma_results_inline(r_xprt, rqst))
                wtype = rpcrdma_noch;
-       else if (ddp_allowed && rqst->rq_rcv_buf.flags & XDRBUF_READ)
+       else if ((ddp_allowed && rqst->rq_rcv_buf.flags & XDRBUF_READ) &&
+                rpcrdma_nonpayload_inline(r_xprt, rqst))
                wtype = rpcrdma_writech;
        else
                wtype = rpcrdma_replych;