]> git.baikalelectronics.ru Git - kernel.git/commitdiff
SUNRPC: Fix disconnection races
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Mon, 17 Dec 2018 18:34:59 +0000 (13:34 -0500)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Tue, 18 Dec 2018 16:03:57 +0000 (11:03 -0500)
When the socket is closed, we need to call xprt_disconnect_done() in order
to clean up the XPRT_WRITE_SPACE flag, and wake up the sleeping tasks.

However, we also want to ensure that we don't wake them up before the socket
is closed, since that would cause thundering herd issues with everyone
piling up to retransmit before the TCP shutdown dance has completed.
Only the task that holds XPRT_LOCKED needs to wake up early in order to
allow the close to complete.

Reported-by: Dave Wysochanski <dwysocha@redhat.com>
Reported-by: Scott Mayhew <smayhew@redhat.com>
Cc: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Tested-by: Chuck Lever <chuck.lever@oracle.com>
net/sunrpc/clnt.c
net/sunrpc/xprt.c
net/sunrpc/xprtsock.c

index c6782aa475257bb510402a2172c8d4f55706a79a..24cbddc44c884ce6fa9a4ba51a7e85c30953edd2 100644 (file)
@@ -1952,6 +1952,7 @@ call_connect_status(struct rpc_task *task)
                /* retry with existing socket, after a delay */
                rpc_delay(task, 3*HZ);
                /* fall through */
+       case -ENOTCONN:
        case -EAGAIN:
                /* Check for timeouts before looping back to call_bind */
        case -ETIMEDOUT:
index ce927002862a675a9f1169d12fbeb6999984a1c6..3fb001dff670ebe05f009d4bf4d0b693e4ec0792 100644 (file)
@@ -680,7 +680,9 @@ void xprt_force_disconnect(struct rpc_xprt *xprt)
        /* Try to schedule an autoclose RPC call */
        if (test_and_set_bit(XPRT_LOCKED, &xprt->state) == 0)
                queue_work(xprtiod_workqueue, &xprt->task_cleanup);
-       xprt_wake_pending_tasks(xprt, -EAGAIN);
+       else if (xprt->snd_task)
+               rpc_wake_up_queued_task_set_status(&xprt->pending,
+                               xprt->snd_task, -ENOTCONN);
        spin_unlock_bh(&xprt->transport_lock);
 }
 EXPORT_SYMBOL_GPL(xprt_force_disconnect);
@@ -852,6 +854,7 @@ static void xprt_connect_status(struct rpc_task *task)
        case -ENETUNREACH:
        case -EHOSTUNREACH:
        case -EPIPE:
+       case -ENOTCONN:
        case -EAGAIN:
                dprintk("RPC: %5u xprt_connect_status: retrying\n", task->tk_pid);
                break;
index 8a5e823e0b339b1998ff21b9cac814de213a2b23..4c471b4235ba5585148ea7876778d34268229eaa 100644 (file)
@@ -1217,6 +1217,8 @@ static void xs_reset_transport(struct sock_xprt *transport)
 
        trace_rpc_socket_close(xprt, sock);
        sock_release(sock);
+
+       xprt_disconnect_done(xprt);
 }
 
 /**
@@ -1237,8 +1239,6 @@ static void xs_close(struct rpc_xprt *xprt)
 
        xs_reset_transport(transport);
        xprt->reestablish_timeout = 0;
-
-       xprt_disconnect_done(xprt);
 }
 
 static void xs_inject_disconnect(struct rpc_xprt *xprt)
@@ -1489,8 +1489,6 @@ static void xs_tcp_state_change(struct sock *sk)
                                        &transport->sock_state))
                        xprt_clear_connecting(xprt);
                clear_bit(XPRT_CLOSING, &xprt->state);
-               if (sk->sk_err)
-                       xprt_wake_pending_tasks(xprt, -sk->sk_err);
                /* Trigger the socket release */
                xs_tcp_force_close(xprt);
        }