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

Commit 1b8a6d1b authored by Trond Myklebust's avatar Trond Myklebust Committed by Greg Kroah-Hartman
Browse files

SUNRPC: Fix socket waits for write buffer space



[ Upstream commit 7496b59f588dd52886fdbac7633608097543a0a5 ]

The socket layer requires that we use the socket lock to protect changes
to the sock->sk_write_pending field and others.

Reported-by: default avatarChuck Lever <chuck.lever@oracle.com>
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent e19c3149
Loading
Loading
Loading
Loading
+39 −15
Original line number Diff line number Diff line
@@ -872,12 +872,12 @@ static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen,
/**
 * xs_nospace - handle transmit was incomplete
 * @req: pointer to RPC request
 * @transport: pointer to struct sock_xprt
 *
 */
static int xs_nospace(struct rpc_rqst *req)
static int xs_nospace(struct rpc_rqst *req, struct sock_xprt *transport)
{
	struct rpc_xprt *xprt = req->rq_xprt;
	struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
	struct rpc_xprt *xprt = &transport->xprt;
	struct sock *sk = transport->inet;
	int ret = -EAGAIN;

@@ -891,25 +891,49 @@ static int xs_nospace(struct rpc_rqst *req)

	/* Don't race with disconnect */
	if (xprt_connected(xprt)) {
		struct socket_wq *wq;

		rcu_read_lock();
		wq = rcu_dereference(sk->sk_wq);
		set_bit(SOCKWQ_ASYNC_NOSPACE, &wq->flags);
		rcu_read_unlock();

		/* wait for more buffer space */
		set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
		sk->sk_write_pending++;
		xprt_wait_for_buffer_space(xprt);
	} else
		ret = -ENOTCONN;

	spin_unlock(&xprt->transport_lock);
	return ret;
}

	/* Race breaker in case memory is freed before above code is called */
	if (ret == -EAGAIN) {
		struct socket_wq *wq;

		rcu_read_lock();
		wq = rcu_dereference(sk->sk_wq);
		set_bit(SOCKWQ_ASYNC_NOSPACE, &wq->flags);
		rcu_read_unlock();
static int xs_sock_nospace(struct rpc_rqst *req)
{
	struct sock_xprt *transport =
		container_of(req->rq_xprt, struct sock_xprt, xprt);
	struct sock *sk = transport->inet;
	int ret = -EAGAIN;

		sk->sk_write_space(sk);
	lock_sock(sk);
	if (!sock_writeable(sk))
		ret = xs_nospace(req, transport);
	release_sock(sk);
	return ret;
}

static int xs_stream_nospace(struct rpc_rqst *req)
{
	struct sock_xprt *transport =
		container_of(req->rq_xprt, struct sock_xprt, xprt);
	struct sock *sk = transport->inet;
	int ret = -EAGAIN;

	lock_sock(sk);
	if (!sk_stream_memory_free(sk))
		ret = xs_nospace(req, transport);
	release_sock(sk);
	return ret;
}

@@ -996,7 +1020,7 @@ static int xs_local_send_request(struct rpc_rqst *req)
	case -ENOBUFS:
		break;
	case -EAGAIN:
		status = xs_nospace(req);
		status = xs_stream_nospace(req);
		break;
	default:
		dprintk("RPC:       sendmsg returned unrecognized error %d\n",
@@ -1068,7 +1092,7 @@ static int xs_udp_send_request(struct rpc_rqst *req)
		/* Should we call xs_close() here? */
		break;
	case -EAGAIN:
		status = xs_nospace(req);
		status = xs_sock_nospace(req);
		break;
	case -ENETUNREACH:
	case -ENOBUFS:
@@ -1181,7 +1205,7 @@ static int xs_tcp_send_request(struct rpc_rqst *req)
		/* Should we call xs_close() here? */
		break;
	case -EAGAIN:
		status = xs_nospace(req);
		status = xs_stream_nospace(req);
		break;
	case -ECONNRESET:
	case -ECONNREFUSED: