diff options
author | Benjamin Coddington <bcodding@redhat.com> | 2019-10-02 10:40:55 -0400 |
---|---|---|
committer | Anna Schumaker <Anna.Schumaker@Netapp.com> | 2019-10-10 16:14:28 -0400 |
commit | af84537dbd1b39505d1f3d8023029b4a59666513 (patch) | |
tree | 0c176e56553e0ac8c56b28794b6343128df56d6e /net/sunrpc/xprtsock.c | |
parent | 1047ec868332034d1fbcb2fae19fe6d4cb869ff2 (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.c | 17 |
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) | |||
2476 | static void xs_wake_error(struct sock_xprt *transport) | 2478 | static 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); |
2493 | out: | 2492 | out: |