SUNRPC/xprtrdma: Fix reconnection locking
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Mon, 26 Jul 2021 12:03:12 +0000 (08:03 -0400)
committerAnna Schumaker <Anna.Schumaker@Netapp.com>
Mon, 9 Aug 2021 20:57:05 +0000 (16:57 -0400)
The xprtrdma client code currently relies on the task that initiated the
connect to hold the XPRT_LOCK for the duration of the connection
attempt. If the task is woken early, due to some other event, then that
lock could get released early.
Avoid races by using the same mechanism that the socket code uses of
transferring lock ownership to the RDMA connect worker itself. That
frees us to call rpcrdma_xprt_disconnect() directly since we're now
guaranteed exclusion w.r.t. other callers.

Fixes: 4cf44be6f1e8 ("xprtrdma: Fix recursion into rpcrdma_xprt_disconnect()")
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
net/sunrpc/xprt.c
net/sunrpc/xprtrdma/transport.c

index aae5a328b15b34dd8cb0a9fe6ddcdd42617caf26..b88ac8132054c4c4207317885334b0fe6c6744bc 100644 (file)
@@ -877,6 +877,7 @@ out:
        spin_unlock(&xprt->transport_lock);
        return ret;
 }
+EXPORT_SYMBOL_GPL(xprt_lock_connect);
 
 void xprt_unlock_connect(struct rpc_xprt *xprt, void *cookie)
 {
@@ -893,6 +894,7 @@ out:
        spin_unlock(&xprt->transport_lock);
        wake_up_bit(&xprt->state, XPRT_LOCKED);
 }
+EXPORT_SYMBOL_GPL(xprt_unlock_connect);
 
 /**
  * xprt_connect - schedule a transport connect operation
index a463400ed5a37a1d603aa45a10db1ed621fceff2..16e5696314a4f31cba124612f800d7e5b1daf00c 100644 (file)
@@ -250,12 +250,9 @@ xprt_rdma_connect_worker(struct work_struct *work)
                                           xprt->stat.connect_start;
                xprt_set_connected(xprt);
                rc = -EAGAIN;
-       } else {
-               /* Force a call to xprt_rdma_close to clean up */
-               spin_lock(&xprt->transport_lock);
-               set_bit(XPRT_CLOSE_WAIT, &xprt->state);
-               spin_unlock(&xprt->transport_lock);
-       }
+       } else
+               rpcrdma_xprt_disconnect(r_xprt);
+       xprt_unlock_connect(xprt, r_xprt);
        xprt_wake_pending_tasks(xprt, rc);
 }
 
@@ -489,6 +486,8 @@ xprt_rdma_connect(struct rpc_xprt *xprt, struct rpc_task *task)
        struct rpcrdma_ep *ep = r_xprt->rx_ep;
        unsigned long delay;
 
+       WARN_ON_ONCE(!xprt_lock_connect(xprt, task, r_xprt));
+
        delay = 0;
        if (ep && ep->re_connect_status != 0) {
                delay = xprt_reconnect_delay(xprt);