Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+21 −28
Original line number Diff line number Diff line
@@ -398,7 +398,6 @@ static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen,
	if (unlikely(!sock))
		return -ENOTSOCK;

	clear_bit(SOCKWQ_ASYNC_NOSPACE, &sock->flags);
	if (base != 0) {
		addr = NULL;
		addrlen = 0;
@@ -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);

	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)

	/* Don't race with disconnect */
	if (xprt_connected(xprt)) {
		if (test_bit(SOCKWQ_ASYNC_NOSPACE, &transport->sock->flags)) {
			/*
			 * Notify TCP that we're limited by the application
			 * window size
			 */
			set_bit(SOCK_NOSPACE, &transport->sock->flags);
		/* wait for more buffer space */
		sk->sk_write_pending++;
			/* ...and wait for more buffer space */
		xprt_wait_for_buffer_space(task, xs_nospace_callback);
		}
	} else {
		clear_bit(SOCKWQ_ASYNC_NOSPACE, &transport->sock->flags);
	} else
		ret = -ENOTCONN;
	}

	spin_unlock_bh(&xprt->transport_lock);

@@ -616,9 +605,6 @@ static int xs_udp_send_request(struct rpc_task *task)
	case -EAGAIN:
		status = xs_nospace(task);
		break;
	default:
		dprintk("RPC:       sendmsg returned unrecognized error %d\n",
			-status);
	case -ENETUNREACH:
	case -ENOBUFS:
	case -EPIPE:
@@ -626,7 +612,10 @@ static int xs_udp_send_request(struct rpc_task *task)
	case -EPERM:
		/* When the server has died, an ICMP port unreachable message
		 * prompts ECONNREFUSED. */
		clear_bit(SOCKWQ_ASYNC_NOSPACE, &transport->sock->flags);
		break;
	default:
		dprintk("RPC:       sendmsg returned unrecognized error %d\n",
			-status);
	}

	return status;
@@ -706,16 +695,16 @@ static int xs_tcp_send_request(struct rpc_task *task)
	case -EAGAIN:
		status = xs_nospace(task);
		break;
	default:
		dprintk("RPC:       sendmsg returned unrecognized error %d\n",
			-status);
	case -ECONNRESET:
	case -ECONNREFUSED:
	case -ENOTCONN:
	case -EADDRINUSE:
	case -ENOBUFS:
	case -EPIPE:
		clear_bit(SOCKWQ_ASYNC_NOSPACE, &transport->sock->flags);
		break;
	default:
		dprintk("RPC:       sendmsg returned unrecognized error %d\n",
			-status);
	}

	return status;
@@ -1609,19 +1598,23 @@ static void xs_tcp_state_change(struct sock *sk)

static void xs_write_space(struct sock *sk)
{
	struct socket *sock;
	struct socket_wq *wq;
	struct rpc_xprt *xprt;

	if (unlikely(!(sock = sk->sk_socket)))
	if (!sk->sk_socket)
		return;
	clear_bit(SOCK_NOSPACE, &sock->flags);
	clear_bit(SOCK_NOSPACE, &sk->sk_socket->flags);

	if (unlikely(!(xprt = xprt_from_sock(sk))))
		return;
	if (test_and_clear_bit(SOCKWQ_ASYNC_NOSPACE, &sock->flags) == 0)
		return;
	rcu_read_lock();
	wq = rcu_dereference(sk->sk_wq);
	if (!wq || test_and_clear_bit(SOCKWQ_ASYNC_NOSPACE, &wq->flags) == 0)
		goto out;

	xprt_write_space(xprt);
out:
	rcu_read_unlock();
}

/**