UDP sock_wfree optimisations
Subsystems (3)
cover UDP sock_wfree optimisations
The series is not UDP specific but that the main beneficiary. 2/3 saves one atomic in sock_wfree() and on top 3/3 removes an extra barrier. Tested with UDP over dummy netdev, 2038491 -> 2099071 req/s (or around +3%).
note: in regards to 1/3, there is a "Should agree with poll..." comment that I don't completely get, and there is no git history to explain it. Though I can't see how it could rely on having the second check without racing with tasks woken by wake_up*().
The series was split from a larger patchset, see
https://lore.kernel.org/netdev/[email protected]/
Pavel Begunkov (3):
sock: dedup sock_def_write_space wmem_alloc checks
sock: optimise UDP sock_wfree() refcounting
sock: optimise sock_def_write_space barriers
net/core/sock.c | 43 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-)
Signature · 1 line
2.36.0
PATCH 1/3 sock: dedup sock_def_write_space wmem_alloc checks
Except for minor rounding differences the first ->sk_wmem_alloc test in sock_def_write_space() is a hand coded version of sock_writeable(). Replace it with the helper, and also kill the following if duplicating the check.
--- net/core/sock.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
net/core/sock.c +2 -3
@@ -3194,15 +3194,14 @@ static void sock_def_write_space(struct sock *sk)/* Do not wake up a writer until he can make "significant" * progress. --DaveM */ -if ((refcount_read (&sk->sk_wmem_alloc) <<1 ) <=READ_ONCE (sk->sk_sndbuf)) { +if (sock_writeable (sk)) { wq =rcu_dereference (sk->sk_wq);if (skwq_has_sleeper (wq))wake_up_interruptible_sync_poll (&wq->wait,EPOLLOUT |EPOLLWRNORM |EPOLLWRBAND );/* Should agree with poll, otherwise some programs break */ -if (sock_writeable (sk)) -sk_wake_async (sk,SOCK_WAKE_SPACE ,POLL_OUT ); +sk_wake_async (sk,SOCK_WAKE_SPACE ,POLL_OUT ); }rcu_read_unlock ();
Signature · 1 line
2.36.0
PATCH 2/3 sock: optimise UDP sock_wfree() refcounting
In reply to Pavel BegunkovFor non SOCK_USE_WRITE_QUEUE sockets, sock_wfree() (atomically) puts ->sk_wmem_alloc twice. It's needed to keep the socket alive while calling ->sk_write_space() after the first put.
However, some sockets, such as UDP, are freed by RCU
(i.e. SOCK_RCU_FREE) and use already RCU-safe sock_def_write_space(). Carve a fast path for such sockets, put down all refs in one go before calling sock_def_write_space() but guard the socket from being freed by an RCU read section.
note: because TCP sockets are marked with SOCK_USE_WRITE_QUEUE it doesn't add extra checks in its path.
--- net/core/sock.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
net/core/sock.c +14 -0
@@ -146,6 +146,8 @@static DEFINE_MUTEX (proto_list_mutex);static LIST_HEAD (proto_list); +static void sock_def_write_space (struct sock *sk); +/** * sk_ns_capable - General socket capability test * @sk: Socket to use a capability on or through
@@ -2320,8 +2322,20 @@ void sock_wfree(struct sk_buff *skb) {struct sock *sk = skb->sk;unsigned int len = skb->truesize; +bool free;if (!sock_flag (sk,SOCK_USE_WRITE_QUEUE )) { +if (sock_flag (sk,SOCK_RCU_FREE ) && + sk->sk_write_space == sock_def_write_space) { +rcu_read_lock (); + free =refcount_sub_and_test (len, &sk->sk_wmem_alloc); +sock_def_write_space (sk); +rcu_read_unlock (); +if (unlikely (free)) +__sk_free (sk); +return ; + } +/* * Keep a reference on sk_wmem_alloc, this will be released * after sk_write_space() call
Signature · 1 line
2.36.0
PATCH 3/3 sock: optimise sock_def_write_space barriers
In reply to Pavel BegunkovNow we have a separate path for sock_def_write_space() and can go one step further. When it's called from sock_wfree() we know that there is a preceding atomic for putting down ->sk_wmem_alloc. We can use it to replace to replace smb_mb() with a less expensive smp_mb__after_atomic(). It also removes an extra RCU read lock/unlock as a small bonus.
--- net/core/sock.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-)
net/core/sock.c +25 -1
@@ -146,6 +146,7 @@static DEFINE_MUTEX (proto_list_mutex);static LIST_HEAD (proto_list); +static void sock_def_write_space_wfree (struct sock *sk);static void sock_def_write_space (struct sock *sk);/**
@@ -2329,7 +2330,7 @@ void sock_wfree(struct sk_buff *skb) sk->sk_write_space == sock_def_write_space) {rcu_read_lock (); free =refcount_sub_and_test (len, &sk->sk_wmem_alloc); -sock_def_write_space (sk); +sock_def_write_space_wfree (sk);rcu_read_unlock ();if (unlikely (free))__sk_free (sk);
@@ -3221,6 +3222,29 @@ static void sock_def_write_space(struct sock *sk)rcu_read_unlock (); } +/* An optimised version of sock_def_write_space(), should only be called +* for SOCK_RCU_FREE sockets under RCU read section and after putting +* ->sk_wmem_alloc. +*/ +static void sock_def_write_space_wfree (struct sock *sk) +{ +/* Do not wake up a writer until he can make "significant" +* progress. --DaveM +*/ +if (sock_writeable (sk)) { +struct socket_wq *wq =rcu_dereference (sk->sk_wq); + +/* rely on refcount_sub from sock_wfree() */ +smp_mb__after_atomic (); +if (wq &&waitqueue_active (&wq->wait)) +wake_up_interruptible_sync_poll (&wq->wait,EPOLLOUT | +EPOLLWRNORM |EPOLLWRBAND ); + +/* Should agree with poll, otherwise some programs break */ +sk_wake_async (sk,SOCK_WAKE_SPACE ,POLL_OUT ); + } +} +static void sock_def_destruct (struct sock *sk) { }
Signature · 1 line
2.36.0
This series was applied to netdev/net-next.git (master) by David S. Miller <[email protected]>:
Hello:
This series was applied to netdev/net-next.git (master) by David S. Miller <[email protected]>:
Quoted 10 lines · On Thu, 28 Apr 2022 11:58:16 +0100 you wrote:
The series is not UDP specific but that the main beneficiary. 2/3 saves one atomic in
sock_wfree()and on top 3/3 removes an extra barrier. Tested with UDP over dummy netdev, 2038491 -> 2099071 req/s (or around +3%).note: in regards to 1/3, there is a "Should agree with poll..." comment that I don't completely get, and there is no git history to explain it. Though I can't see how it could rely on having the second check without racing with tasks woken by wake_up*().
[...]
Here is the summary with links:
- [net-next,1/3] sock: dedup sock_def_write_space wmem_alloc checks
https://git.kernel.org/netdev/net-next/c/14bfee9b6270 - [net-next,2/3] sock: optimise UDP sock_wfree() refcounting https://git.kernel.org/netdev/net-next/c/052ada096842 - [net-next,3/3] sock: optimise sock_def_write_space barriers https://git.kernel.org/netdev/net-next/c/0a8afd9f026a
You are awesome, thank you!
Signature · 2 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html