Commit 13331a55 authored by Trond Myklebust's avatar Trond Myklebust

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 ceb5d58b ("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: ceb5d58b ("net: fix sock_wake_async() rcu protection")
Cc: Eric Dumazet <edumazet@google.com>
Cc: stable@vger.kernel.org # 4.4
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@primarydata.com>
parent 0b161e63
...@@ -398,7 +398,6 @@ static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen, ...@@ -398,7 +398,6 @@ static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen,
if (unlikely(!sock)) if (unlikely(!sock))
return -ENOTSOCK; return -ENOTSOCK;
clear_bit(SOCKWQ_ASYNC_NOSPACE, &sock->flags);
if (base != 0) { if (base != 0) {
addr = NULL; addr = NULL;
addrlen = 0; addrlen = 0;
...@@ -442,7 +441,6 @@ static void xs_nospace_callback(struct rpc_task *task) ...@@ -442,7 +441,6 @@ static void xs_nospace_callback(struct rpc_task *task)
struct sock_xprt *transport = container_of(task->tk_rqstp->rq_xprt, struct sock_xprt, xprt); struct sock_xprt *transport = container_of(task->tk_rqstp->rq_xprt, struct sock_xprt, xprt);
transport->inet->sk_write_pending--; transport->inet->sk_write_pending--;
clear_bit(SOCKWQ_ASYNC_NOSPACE, &transport->sock->flags);
} }
/** /**
...@@ -467,20 +465,11 @@ static int xs_nospace(struct rpc_task *task) ...@@ -467,20 +465,11 @@ static int xs_nospace(struct rpc_task *task)
/* Don't race with disconnect */ /* Don't race with disconnect */
if (xprt_connected(xprt)) { if (xprt_connected(xprt)) {
if (test_bit(SOCKWQ_ASYNC_NOSPACE, &transport->sock->flags)) { /* wait for more buffer space */
/*
* Notify TCP that we're limited by the application
* window size
*/
set_bit(SOCK_NOSPACE, &transport->sock->flags);
sk->sk_write_pending++; sk->sk_write_pending++;
/* ...and wait for more buffer space */
xprt_wait_for_buffer_space(task, xs_nospace_callback); xprt_wait_for_buffer_space(task, xs_nospace_callback);
} } else
} else {
clear_bit(SOCKWQ_ASYNC_NOSPACE, &transport->sock->flags);
ret = -ENOTCONN; ret = -ENOTCONN;
}
spin_unlock_bh(&xprt->transport_lock); spin_unlock_bh(&xprt->transport_lock);
...@@ -616,9 +605,6 @@ static int xs_udp_send_request(struct rpc_task *task) ...@@ -616,9 +605,6 @@ static int xs_udp_send_request(struct rpc_task *task)
case -EAGAIN: case -EAGAIN:
status = xs_nospace(task); status = xs_nospace(task);
break; break;
default:
dprintk("RPC: sendmsg returned unrecognized error %d\n",
-status);
case -ENETUNREACH: case -ENETUNREACH:
case -ENOBUFS: case -ENOBUFS:
case -EPIPE: case -EPIPE:
...@@ -626,7 +612,10 @@ static int xs_udp_send_request(struct rpc_task *task) ...@@ -626,7 +612,10 @@ static int xs_udp_send_request(struct rpc_task *task)
case -EPERM: case -EPERM:
/* When the server has died, an ICMP port unreachable message /* When the server has died, an ICMP port unreachable message
* prompts ECONNREFUSED. */ * prompts ECONNREFUSED. */
clear_bit(SOCKWQ_ASYNC_NOSPACE, &transport->sock->flags); break;
default:
dprintk("RPC: sendmsg returned unrecognized error %d\n",
-status);
} }
return status; return status;
...@@ -706,16 +695,16 @@ static int xs_tcp_send_request(struct rpc_task *task) ...@@ -706,16 +695,16 @@ static int xs_tcp_send_request(struct rpc_task *task)
case -EAGAIN: case -EAGAIN:
status = xs_nospace(task); status = xs_nospace(task);
break; break;
default:
dprintk("RPC: sendmsg returned unrecognized error %d\n",
-status);
case -ECONNRESET: case -ECONNRESET:
case -ECONNREFUSED: case -ECONNREFUSED:
case -ENOTCONN: case -ENOTCONN:
case -EADDRINUSE: case -EADDRINUSE:
case -ENOBUFS: case -ENOBUFS:
case -EPIPE: case -EPIPE:
clear_bit(SOCKWQ_ASYNC_NOSPACE, &transport->sock->flags); break;
default:
dprintk("RPC: sendmsg returned unrecognized error %d\n",
-status);
} }
return status; return status;
...@@ -1609,19 +1598,23 @@ static void xs_tcp_state_change(struct sock *sk) ...@@ -1609,19 +1598,23 @@ static void xs_tcp_state_change(struct sock *sk)
static void xs_write_space(struct sock *sk) static void xs_write_space(struct sock *sk)
{ {
struct socket *sock; struct socket_wq *wq;
struct rpc_xprt *xprt; struct rpc_xprt *xprt;
if (unlikely(!(sock = sk->sk_socket))) if (!sk->sk_socket)
return; return;
clear_bit(SOCK_NOSPACE, &sock->flags); clear_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
if (unlikely(!(xprt = xprt_from_sock(sk)))) if (unlikely(!(xprt = xprt_from_sock(sk))))
return; return;
if (test_and_clear_bit(SOCKWQ_ASYNC_NOSPACE, &sock->flags) == 0) rcu_read_lock();
return; wq = rcu_dereference(sk->sk_wq);
if (!wq || test_and_clear_bit(SOCKWQ_ASYNC_NOSPACE, &wq->flags) == 0)
goto out;
xprt_write_space(xprt); xprt_write_space(xprt);
out:
rcu_read_unlock();
} }
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment