net: add sk_set_nospace() and sk_clear_nospace()

SOCK_NOSPACE lives in sk->sk_socket->flags and is manipulated from
about thirty places in the tree, all of them open coding the
sk->sk_socket dereference, some with a NULL check, some without,
and some reaching the struct socket by yet another path.

Add sk_set_nospace() and sk_clear_nospace() helpers and convert the
core networking setters and clearers to them; the following patches
convert the remaining subsystem callers so that
"git grep _bit(SOCK_NOSPACE" only reports the two helpers and the
remaining test_bit() sites.

Callers that had no NULL check are all called from user context with a
socket attached, so folding the check into the helpers only makes them
more robust.

No functional change intended.

This is a preparation patch: a following one gives TCP a cheaper
private copy of this bit, and needs a single choke point to keep it
in sync.

Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
Signed-off-by: Eric Dumazet <edumazet@kernel.org>
Link: https://patch.msgid.link/20260929071743.23624-3-edumazet@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Eric Dumazet 2026-09-29 07:17:36 +00:00 committed by Jakub Kicinski
parent e234d76255
commit 6ba48a271b
10 changed files with 52 additions and 17 deletions

View File

@ -1129,6 +1129,8 @@ static inline void sk_forward_alloc_add(struct sock *sk, int val)
}
void sk_stream_write_space(struct sock *sk);
void sk_set_nospace(struct sock *sk);
void sk_clear_nospace(struct sock *sk);
/* OOB backlog add */
static inline void __sk_add_backlog(struct sock *sk, struct sk_buff *skb)

View File

@ -2957,6 +2957,39 @@ void sock_kzfree_s(struct sock *sk, void *mem, int size)
}
EXPORT_SYMBOL(sock_kzfree_s);
/**
* sk_set_nospace - tell the transport a writer is waiting for space
* @sk: socket
*
* Must be called before the final check of the available send space,
* so that the transport can not miss the request and forget to call
* sk->sk_write_space() once space is available again.
*/
void sk_set_nospace(struct sock *sk)
{
struct socket *sock = sk->sk_socket;
if (sock)
set_bit(SOCK_NOSPACE, &sock->flags);
}
EXPORT_SYMBOL(sk_set_nospace);
/**
* sk_clear_nospace - tell the transport no writer is waiting for space
* @sk: socket
*
* Called from ->sk_write_space() handlers, once send space has been
* made available to writers.
*/
void sk_clear_nospace(struct sock *sk)
{
struct socket *sock = sk->sk_socket;
if (sock)
clear_bit(SOCK_NOSPACE, &sock->flags);
}
EXPORT_SYMBOL(sk_clear_nospace);
/* It is almost wait_for_tcp_memory minus release_sock/lock_sock.
I think, these locks should be removed for datagram sockets.
*/
@ -2970,7 +3003,7 @@ static long sock_wait_for_wmem(struct sock *sk, long timeo)
break;
if (signal_pending(current))
break;
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
sk_set_nospace(sk);
prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
if (refcount_read(&sk->sk_wmem_alloc) < READ_ONCE(sk->sk_sndbuf))
break;
@ -3011,7 +3044,7 @@ struct sk_buff *sock_alloc_send_pskb(struct sock *sk, unsigned long header_len,
break;
sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
sk_set_nospace(sk);
err = -EAGAIN;
if (!timeo)
goto failure;

View File

@ -37,7 +37,7 @@ void sk_stream_write_space(struct sock *sk)
struct socket_wq *wq;
if (__sk_stream_is_writeable(sk, 1) && sock) {
clear_bit(SOCK_NOSPACE, &sock->flags);
sk_clear_nospace(sk);
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
@ -143,7 +143,7 @@ int sk_stream_wait_memory(struct sock *sk, long *timeo_p)
if (sk_stream_memory_free(sk) && !vm_wait)
break;
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
sk_set_nospace(sk);
sk->sk_write_pending++;
ret = sk_wait_event(sk, &current_timeo, READ_ONCE(sk->sk_err) ||
(READ_ONCE(sk->sk_shutdown) & SEND_SHUTDOWN) ||
@ -177,7 +177,7 @@ int sk_stream_wait_memory(struct sock *sk, long *timeo_p)
* When TCP receives ACK packets that make room, tcp_check_space()
* only calls tcp_new_space() if SOCK_NOSPACE is set.
*/
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
sk_set_nospace(sk);
err = -EAGAIN;
goto out;
do_interrupted:

View File

@ -607,7 +607,7 @@ __poll_t tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
mask |= EPOLLOUT | EPOLLWRNORM;
} else { /* send SIGIO later */
sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
sk_set_nospace(sk);
/* Race breaker. If space is freed after
* wspace test but before the flags are set,
@ -1397,7 +1397,7 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size)
continue;
wait_for_space:
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
sk_set_nospace(sk);
tcp_remove_empty_skb(sk);
if (copied)
tcp_push(sk, flags & ~MSG_MORE, mss_now,

View File

@ -600,7 +600,7 @@ static int tcp_bpf_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
goto out_err;
continue;
wait_for_sndbuf:
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
sk_set_nospace(sk);
wait_for_memory:
err = sk_stream_wait_memory(sk, &timeo);
if (err) {

View File

@ -735,7 +735,7 @@ static void kcm_tx_work(struct work_struct *w)
/* Primarily for SOCK_SEQPACKET sockets */
if (likely(sk->sk_socket) &&
test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) {
clear_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
sk_clear_nospace(sk);
sk->sk_write_space(sk);
}
@ -779,7 +779,7 @@ static int kcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
/* Call the sk_stream functions to manage the sndbuf mem. */
if (!sk_stream_memory_free(sk)) {
kcm_push(kcm);
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
sk_set_nospace(sk);
err = sk_stream_wait_memory(sk, &timeo);
if (err)
goto out_error;

View File

@ -2106,7 +2106,7 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
continue;
wait_for_memory:
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
sk_set_nospace(sk);
__mptcp_push_pending(sk, msg->msg_flags);
ret = sk_stream_wait_memory(sk, &timeo);
if (ret)
@ -4471,7 +4471,7 @@ static __poll_t mptcp_check_writeable(struct mptcp_sock *msk)
if (__mptcp_stream_is_writeable(sk, 1))
return EPOLLOUT | EPOLLWRNORM;
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
sk_set_nospace(sk);
smp_mb__after_atomic(); /* NOSPACE is changed by mptcp_write_space() */
if (__mptcp_stream_is_writeable(sk, 1))
return EPOLLOUT | EPOLLWRNORM;

View File

@ -2925,7 +2925,7 @@ __poll_t smc_poll(struct file *file, struct socket *sock,
mask |= EPOLLOUT | EPOLLWRNORM;
} else {
sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
sk_set_nospace(sk);
if (sk->sk_state != SMC_INIT) {
/* Race breaker the same way as tcp_poll(). */

View File

@ -48,7 +48,7 @@ static void smc_tx_write_space(struct sock *sk)
if (atomic_read(&smc->conn.sndbuf_space) && sock) {
if (test_bit(SOCK_NOSPACE, &sock->flags))
SMC_STAT_RMB_TX_FULL(smc, !smc->conn.lnk);
clear_bit(SOCK_NOSPACE, &sock->flags);
sk_clear_nospace(sk);
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
if (skwq_has_sleeper(wq))
@ -100,7 +100,7 @@ static int smc_tx_wait(struct smc_sock *smc, int flags)
}
if (!timeo) {
/* ensure EPOLLOUT is subsequently generated */
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
sk_set_nospace(sk);
rc = -EAGAIN;
break;
}
@ -111,7 +111,7 @@ static int smc_tx_wait(struct smc_sock *smc, int flags)
sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk);
if (atomic_read(&conn->sndbuf_space) && !conn->urg_tx_pend)
break; /* at least 1 byte of free & no urgent data */
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
sk_set_nospace(sk);
sk_wait_event(sk, &timeo,
READ_ONCE(sk->sk_err) ||
(READ_ONCE(sk->sk_shutdown) & SEND_SHUTDOWN) ||

View File

@ -966,7 +966,7 @@ static int tls_sw_sendmsg_locked(struct sock *sk, struct msghdr *msg,
continue;
wait_for_sndbuf:
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
sk_set_nospace(sk);
wait_for_memory:
ret = sk_stream_wait_memory(sk, &timeo);
if (ret) {