diff options
author | Trond Myklebust <trond.myklebust@primarydata.com> | 2016-01-06 08:57:06 -0500 |
---|---|---|
committer | Trond Myklebust <trond.myklebust@primarydata.com> | 2016-01-06 12:22:25 -0500 |
commit | 13331a551ab4df87f7a027d2cab392da96aba1de (patch) | |
tree | f6af42644253993712cd87b467355b570bc6646b /net | |
parent | 0b161e6330e27c191a0ff0d44082ff7832a8c8a1 (diff) |
SUNRPC: Fixup socket wait for memory
We're seeing hangs in the NFS client code, with loops of the form:
RPC: 30317 xmit incomplete (267368 left of 524448)
RPC: 30317 call_status (status -11)
RPC: 30317 call_transmit (status 0)
RPC: 30317 xprt_prepare_transmit
RPC: 30317 xprt_transmit(524448)
RPC: xs_tcp_send_request(267368) = -11
RPC: 30317 xmit incomplete (267368 left of 524448)
RPC: 30317 call_status (status -11)
RPC: 30317 call_transmit (status 0)
RPC: 30317 xprt_prepare_transmit
RPC: 30317 xprt_transmit(524448)
Turns out commit ceb5d58b2170 ("net: fix sock_wake_async() rcu protection")
moved SOCKWQ_ASYNC_NOSPACE out of sock->flags and into sk->sk_wq->flags,
however it never tried to fix up the code in net/sunrpc.
The new idiom is to use the flags in the RCU protected struct socket_wq.
While we're at it, clear out the now redundant places where we set/clear
SOCKWQ_ASYNC_NOSPACE and SOCK_NOSPACE. In principle, sk_stream_wait_memory()
is supposed to set these for us, so we only need to clear them in the
particular case of our ->write_space() callback.
Fixes: ceb5d58b2170 ("net: fix sock_wake_async() rcu protection")
Cc: Eric Dumazet <edumazet@google.com>
Cc: stable@vger.kernel.org # 4.4
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/sunrpc/xprtsock.c | 49 |
1 files changed, 21 insertions, 28 deletions
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 2ffaf6a79499..027c9ef8a263 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c | |||
@@ -398,7 +398,6 @@ static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen, | |||
398 | if (unlikely(!sock)) | 398 | if (unlikely(!sock)) |
399 | return -ENOTSOCK; | 399 | return -ENOTSOCK; |
400 | 400 | ||
401 | clear_bit(SOCKWQ_ASYNC_NOSPACE, &sock->flags); | ||
402 | if (base != 0) { | 401 | if (base != 0) { |
403 | addr = NULL; | 402 | addr = NULL; |
404 | addrlen = 0; | 403 | addrlen = 0; |
@@ -442,7 +441,6 @@ static void xs_nospace_callback(struct rpc_task *task) | |||
442 | struct sock_xprt *transport = container_of(task->tk_rqstp->rq_xprt, struct sock_xprt, xprt); | 441 | struct sock_xprt *transport = container_of(task->tk_rqstp->rq_xprt, struct sock_xprt, xprt); |
443 | 442 | ||
444 | transport->inet->sk_write_pending--; | 443 | transport->inet->sk_write_pending--; |
445 | clear_bit(SOCKWQ_ASYNC_NOSPACE, &transport->sock->flags); | ||
446 | } | 444 | } |
447 | 445 | ||
448 | /** | 446 | /** |
@@ -467,20 +465,11 @@ static int xs_nospace(struct rpc_task *task) | |||
467 | 465 | ||
468 | /* Don't race with disconnect */ | 466 | /* Don't race with disconnect */ |
469 | if (xprt_connected(xprt)) { | 467 | if (xprt_connected(xprt)) { |
470 | if (test_bit(SOCKWQ_ASYNC_NOSPACE, &transport->sock->flags)) { | 468 | /* wait for more buffer space */ |
471 | /* | 469 | sk->sk_write_pending++; |
472 | * Notify TCP that we're limited by the application | 470 | xprt_wait_for_buffer_space(task, xs_nospace_callback); |
473 | * window size | 471 | } else |
474 | */ | ||
475 | set_bit(SOCK_NOSPACE, &transport->sock->flags); | ||
476 | sk->sk_write_pending++; | ||
477 | /* ...and wait for more buffer space */ | ||
478 | xprt_wait_for_buffer_space(task, xs_nospace_callback); | ||
479 | } | ||
480 | } else { | ||
481 | clear_bit(SOCKWQ_ASYNC_NOSPACE, &transport->sock->flags); | ||
482 | ret = -ENOTCONN; | 472 | ret = -ENOTCONN; |
483 | } | ||
484 | 473 | ||
485 | spin_unlock_bh(&xprt->transport_lock); | 474 | spin_unlock_bh(&xprt->transport_lock); |
486 | 475 | ||
@@ -616,9 +605,6 @@ process_status: | |||
616 | case -EAGAIN: | 605 | case -EAGAIN: |
617 | status = xs_nospace(task); | 606 | status = xs_nospace(task); |
618 | break; | 607 | break; |
619 | default: | ||
620 | dprintk("RPC: sendmsg returned unrecognized error %d\n", | ||
621 | -status); | ||
622 | case -ENETUNREACH: | 608 | case -ENETUNREACH: |
623 | case -ENOBUFS: | 609 | case -ENOBUFS: |
624 | case -EPIPE: | 610 | case -EPIPE: |
@@ -626,7 +612,10 @@ process_status: | |||
626 | case -EPERM: | 612 | case -EPERM: |
627 | /* When the server has died, an ICMP port unreachable message | 613 | /* When the server has died, an ICMP port unreachable message |
628 | * prompts ECONNREFUSED. */ | 614 | * prompts ECONNREFUSED. */ |
629 | clear_bit(SOCKWQ_ASYNC_NOSPACE, &transport->sock->flags); | 615 | break; |
616 | default: | ||
617 | dprintk("RPC: sendmsg returned unrecognized error %d\n", | ||
618 | -status); | ||
630 | } | 619 | } |
631 | 620 | ||
632 | return status; | 621 | return status; |
@@ -706,16 +695,16 @@ static int xs_tcp_send_request(struct rpc_task *task) | |||
706 | case -EAGAIN: | 695 | case -EAGAIN: |
707 | status = xs_nospace(task); | 696 | status = xs_nospace(task); |
708 | break; | 697 | break; |
709 | default: | ||
710 | dprintk("RPC: sendmsg returned unrecognized error %d\n", | ||
711 | -status); | ||
712 | case -ECONNRESET: | 698 | case -ECONNRESET: |
713 | case -ECONNREFUSED: | 699 | case -ECONNREFUSED: |
714 | case -ENOTCONN: | 700 | case -ENOTCONN: |
715 | case -EADDRINUSE: | 701 | case -EADDRINUSE: |
716 | case -ENOBUFS: | 702 | case -ENOBUFS: |
717 | case -EPIPE: | 703 | case -EPIPE: |
718 | clear_bit(SOCKWQ_ASYNC_NOSPACE, &transport->sock->flags); | 704 | break; |
705 | default: | ||
706 | dprintk("RPC: sendmsg returned unrecognized error %d\n", | ||
707 | -status); | ||
719 | } | 708 | } |
720 | 709 | ||
721 | return status; | 710 | return status; |
@@ -1609,19 +1598,23 @@ static void xs_tcp_state_change(struct sock *sk) | |||
1609 | 1598 | ||
1610 | static void xs_write_space(struct sock *sk) | 1599 | static void xs_write_space(struct sock *sk) |
1611 | { | 1600 | { |
1612 | struct socket *sock; | 1601 | struct socket_wq *wq; |
1613 | struct rpc_xprt *xprt; | 1602 | struct rpc_xprt *xprt; |
1614 | 1603 | ||
1615 | if (unlikely(!(sock = sk->sk_socket))) | 1604 | if (!sk->sk_socket) |
1616 | return; | 1605 | return; |
1617 | clear_bit(SOCK_NOSPACE, &sock->flags); | 1606 | clear_bit(SOCK_NOSPACE, &sk->sk_socket->flags); |
1618 | 1607 | ||
1619 | if (unlikely(!(xprt = xprt_from_sock(sk)))) | 1608 | if (unlikely(!(xprt = xprt_from_sock(sk)))) |
1620 | return; | 1609 | return; |
1621 | if (test_and_clear_bit(SOCKWQ_ASYNC_NOSPACE, &sock->flags) == 0) | 1610 | rcu_read_lock(); |
1622 | return; | 1611 | wq = rcu_dereference(sk->sk_wq); |
1612 | if (!wq || test_and_clear_bit(SOCKWQ_ASYNC_NOSPACE, &wq->flags) == 0) | ||
1613 | goto out; | ||
1623 | 1614 | ||
1624 | xprt_write_space(xprt); | 1615 | xprt_write_space(xprt); |
1616 | out: | ||
1617 | rcu_read_unlock(); | ||
1625 | } | 1618 | } |
1626 | 1619 | ||
1627 | /** | 1620 | /** |