| From 48600899cfe2709e9576c19f16bb66974ba8d9bf Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Mon, 14 Mar 2022 21:02:10 -0400 |
| Subject: SUNRPC: Fix socket waits for write buffer space |
| |
| From: Trond Myklebust <trond.myklebust@hammerspace.com> |
| |
| [ 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: Chuck Lever <chuck.lever@oracle.com> |
| Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| net/sunrpc/xprtsock.c | 54 +++++++++++++++++++++++++++++++------------ |
| 1 file changed, 39 insertions(+), 15 deletions(-) |
| |
| diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c |
| index 37d961c1a5c9..aaba1d8cf552 100644 |
| --- a/net/sunrpc/xprtsock.c |
| +++ b/net/sunrpc/xprtsock.c |
| @@ -763,12 +763,12 @@ xs_stream_start_connect(struct sock_xprt *transport) |
| /** |
| * 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; |
| |
| @@ -779,25 +779,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; |
| +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; |
| |
| - rcu_read_lock(); |
| - wq = rcu_dereference(sk->sk_wq); |
| - set_bit(SOCKWQ_ASYNC_NOSPACE, &wq->flags); |
| - rcu_read_unlock(); |
| + lock_sock(sk); |
| + if (!sock_writeable(sk)) |
| + ret = xs_nospace(req, transport); |
| + release_sock(sk); |
| + return ret; |
| +} |
| |
| - sk->sk_write_space(sk); |
| - } |
| +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; |
| } |
| |
| @@ -887,7 +911,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", |
| @@ -963,7 +987,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: |
| @@ -1083,7 +1107,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: |
| -- |
| 2.35.1 |
| |