]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
xprtrdma: Fix cwnd update ordering
authorChuck Lever <chuck.lever@oracle.com>
Mon, 19 Apr 2021 18:02:41 +0000 (14:02 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 19 May 2021 08:13:04 +0000 (10:13 +0200)
[ Upstream commit 35d8b10a25884050bb3b0149b62c3818ec59f77c ]

After a reconnect, the reply handler is opening the cwnd (and thus
enabling more RPC Calls to be sent) /before/ rpcrdma_post_recvs()
can post enough Receive WRs to receive their replies. This causes an
RNR and the new connection is lost immediately.

The race is most clearly exposed when KASAN and disconnect injection
are enabled. This slows down rpcrdma_rep_create() enough to allow
the send side to post a bunch of RPC Calls before the Receive
completion handler can invoke ib_post_recv().

Fixes: 2ae50ad68cd7 ("xprtrdma: Close window between waking RPC senders and posting Receives")
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
net/sunrpc/xprtrdma/rpc_rdma.c
net/sunrpc/xprtrdma/verbs.c
net/sunrpc/xprtrdma/xprt_rdma.h

index c48536f2121fb223683b41cec7095fa9b8d6f1c9..ca267a855a12c25f248b37b8a8e6f6a5307230e5 100644 (file)
@@ -1467,9 +1467,10 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep)
                credits = 1;    /* don't deadlock */
        else if (credits > r_xprt->rx_ep->re_max_requests)
                credits = r_xprt->rx_ep->re_max_requests;
+       rpcrdma_post_recvs(r_xprt, credits + (buf->rb_bc_srv_max_requests << 1),
+                          false);
        if (buf->rb_credits != credits)
                rpcrdma_update_cwnd(r_xprt, credits);
-       rpcrdma_post_recvs(r_xprt, false);
 
        req = rpcr_to_rdmar(rqst);
        if (req->rl_reply) {
index ad6e2e4994ce8edd52b920976a27f3bc8ffc0d2c..04325f0267c1c8ef0aa3bb78764f2273dd7a9305 100644 (file)
@@ -535,7 +535,7 @@ int rpcrdma_xprt_connect(struct rpcrdma_xprt *r_xprt)
         * outstanding Receives.
         */
        rpcrdma_ep_get(ep);
-       rpcrdma_post_recvs(r_xprt, true);
+       rpcrdma_post_recvs(r_xprt, 1, true);
 
        rc = rdma_connect(ep->re_id, &ep->re_remote_cma);
        if (rc)
@@ -1377,21 +1377,21 @@ int rpcrdma_post_sends(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
 /**
  * rpcrdma_post_recvs - Refill the Receive Queue
  * @r_xprt: controlling transport instance
- * @temp: mark Receive buffers to be deleted after use
+ * @needed: current credit grant
+ * @temp: mark Receive buffers to be deleted after one use
  *
  */
-void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, bool temp)
+void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, int needed, bool temp)
 {
        struct rpcrdma_buffer *buf = &r_xprt->rx_buf;
        struct rpcrdma_ep *ep = r_xprt->rx_ep;
        struct ib_recv_wr *wr, *bad_wr;
        struct rpcrdma_rep *rep;
-       int needed, count, rc;
+       int count, rc;
 
        rc = 0;
        count = 0;
 
-       needed = buf->rb_credits + (buf->rb_bc_srv_max_requests << 1);
        if (likely(ep->re_receive_count > needed))
                goto out;
        needed -= ep->re_receive_count;
index 43974ef39a5055f9c56b736cc5c5da20d36a4c55..3cacc6f4c5271fd0a06edffaa765f23a932d934f 100644 (file)
@@ -452,7 +452,7 @@ int rpcrdma_xprt_connect(struct rpcrdma_xprt *r_xprt);
 void rpcrdma_xprt_disconnect(struct rpcrdma_xprt *r_xprt);
 
 int rpcrdma_post_sends(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req);
-void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, bool temp);
+void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, int needed, bool temp);
 
 /*
  * Buffer calls - xprtrdma/verbs.c