Skip to content

Commit

Permalink
SUNRPC: Fix disconnection races
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Trond Myklebust committed Dec 18, 2018
1 parent 7566ec3 commit 0445f92
Show file tree
Hide file tree
Showing 3 changed files with 7 additions and 5 deletions.
1 change: 1 addition & 0 deletions net/sunrpc/clnt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
5 changes: 4 additions & 1 deletion net/sunrpc/xprt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 2 additions & 4 deletions net/sunrpc/xprtsock.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand All @@ -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)
Expand Down Expand Up @@ -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);
}
Expand Down

0 comments on commit 0445f92

Please sign in to comment.