aboutsummaryrefslogtreecommitdiffstats
path: root/net/sunrpc/xprtsock.c
diff options
context:
space:
mode:
authorBenjamin Coddington <bcodding@redhat.com>2019-10-02 10:40:55 -0400
committerAnna Schumaker <Anna.Schumaker@Netapp.com>2019-10-10 16:14:28 -0400
commitaf84537dbd1b39505d1f3d8023029b4a59666513 (patch)
tree0c176e56553e0ac8c56b28794b6343128df56d6e /net/sunrpc/xprtsock.c
parent1047ec868332034d1fbcb2fae19fe6d4cb869ff2 (diff)
SUNRPC: fix race to sk_err after xs_error_report
Since commit 4f8943f80883 ("SUNRPC: Replace direct task wakeups from softirq context") there has been a race to the value of the sk_err if both XPRT_SOCK_WAKE_ERROR and XPRT_SOCK_WAKE_DISCONNECT are set. In that case, we may end up losing the sk_err value that existed when xs_error_report was called. Fix this by reverting to the previous behavior: instead of using SO_ERROR to retrieve the value at a later time (which might also return sk_err_soft), copy the sk_err value onto struct sock_xprt, and use that value to wake pending tasks. Signed-off-by: Benjamin Coddington <bcodding@redhat.com> Fixes: 4f8943f80883 ("SUNRPC: Replace direct task wakeups from softirq context") Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
Diffstat (limited to 'net/sunrpc/xprtsock.c')
-rw-r--r--net/sunrpc/xprtsock.c17
1 files changed, 8 insertions, 9 deletions
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 9ac88722fa83..70e52f567b2a 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -1249,19 +1249,21 @@ static void xs_error_report(struct sock *sk)
1249{ 1249{
1250 struct sock_xprt *transport; 1250 struct sock_xprt *transport;
1251 struct rpc_xprt *xprt; 1251 struct rpc_xprt *xprt;
1252 int err;
1253 1252
1254 read_lock_bh(&sk->sk_callback_lock); 1253 read_lock_bh(&sk->sk_callback_lock);
1255 if (!(xprt = xprt_from_sock(sk))) 1254 if (!(xprt = xprt_from_sock(sk)))
1256 goto out; 1255 goto out;
1257 1256
1258 transport = container_of(xprt, struct sock_xprt, xprt); 1257 transport = container_of(xprt, struct sock_xprt, xprt);
1259 err = -sk->sk_err; 1258 transport->xprt_err = -sk->sk_err;
1260 if (err == 0) 1259 if (transport->xprt_err == 0)
1261 goto out; 1260 goto out;
1262 dprintk("RPC: xs_error_report client %p, error=%d...\n", 1261 dprintk("RPC: xs_error_report client %p, error=%d...\n",
1263 xprt, -err); 1262 xprt, -transport->xprt_err);
1264 trace_rpc_socket_error(xprt, sk->sk_socket, err); 1263 trace_rpc_socket_error(xprt, sk->sk_socket, transport->xprt_err);
1264
1265 /* barrier ensures xprt_err is set before XPRT_SOCK_WAKE_ERROR */
1266 smp_mb__before_atomic();
1265 xs_run_error_worker(transport, XPRT_SOCK_WAKE_ERROR); 1267 xs_run_error_worker(transport, XPRT_SOCK_WAKE_ERROR);
1266 out: 1268 out:
1267 read_unlock_bh(&sk->sk_callback_lock); 1269 read_unlock_bh(&sk->sk_callback_lock);
@@ -2476,7 +2478,6 @@ static void xs_wake_write(struct sock_xprt *transport)
2476static void xs_wake_error(struct sock_xprt *transport) 2478static void xs_wake_error(struct sock_xprt *transport)
2477{ 2479{
2478 int sockerr; 2480 int sockerr;
2479 int sockerr_len = sizeof(sockerr);
2480 2481
2481 if (!test_bit(XPRT_SOCK_WAKE_ERROR, &transport->sock_state)) 2482 if (!test_bit(XPRT_SOCK_WAKE_ERROR, &transport->sock_state))
2482 return; 2483 return;
@@ -2485,9 +2486,7 @@ static void xs_wake_error(struct sock_xprt *transport)
2485 goto out; 2486 goto out;
2486 if (!test_and_clear_bit(XPRT_SOCK_WAKE_ERROR, &transport->sock_state)) 2487 if (!test_and_clear_bit(XPRT_SOCK_WAKE_ERROR, &transport->sock_state))
2487 goto out; 2488 goto out;
2488 if (kernel_getsockopt(transport->sock, SOL_SOCKET, SO_ERROR, 2489 sockerr = xchg(&transport->xprt_err, 0);
2489 (char *)&sockerr, &sockerr_len) != 0)
2490 goto out;
2491 if (sockerr < 0) 2490 if (sockerr < 0)
2492 xprt_wake_pending_tasks(&transport->xprt, sockerr); 2491 xprt_wake_pending_tasks(&transport->xprt, sockerr);
2493out: 2492out: