diff mbox series

[RFC,1/2] udp: introduce udp specific skb destructor

Message ID f159776210ceca3d89ae6de629ba4d4c7d51c882.1707138546.git.asml.silence@gmail.com (mailing list archive)
State RFC
Delegated to: Netdev Maintainers
Headers show
Series optimise UDP skb completion wakeups | expand

Checks

Context Check Description
netdev/series_format warning Target tree name not specified in the subject
netdev/tree_selection success Guessed tree name to be net-next, async
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 3598 this patch: 3598
netdev/build_tools success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers warning 1 maintainers not CCed: willemdebruijn.kernel@gmail.com
netdev/build_clang success Errors and warnings before: 1125 this patch: 1125
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 3812 this patch: 3812
netdev/checkpatch warning WARNING: waitqueue_active without comment
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline fail Was 0 now: 1

Commit Message

Pavel Begunkov Feb. 7, 2024, 2:23 p.m. UTC
Introduce a UDP specific skb destructor, udp_wfree(). We'll need it in
the next patch, which wires SOCK_NOSPACE support for udp sockets. We
can't reuse sock_wfree() there as is because SOCK_NOSPACE also requires
support from the poll callback.

Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
 drivers/net/veth.c | 10 +++++++---
 include/net/sock.h |  1 +
 include/net/udp.h  |  1 +
 net/core/sock.c    | 10 +++++++---
 net/ipv4/udp.c     | 41 ++++++++++++++++++++++++++++++++++++++++-
 net/ipv6/udp.c     |  5 ++++-
 6 files changed, 60 insertions(+), 8 deletions(-)
diff mbox series

Patch

diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 578e36ea1589..84ed58b957e5 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -19,6 +19,7 @@ 
 #include <net/dst.h>
 #include <net/xfrm.h>
 #include <net/xdp.h>
+#include <net/udp.h>
 #include <linux/veth.h>
 #include <linux/module.h>
 #include <linux/bpf.h>
@@ -335,9 +336,12 @@  static bool veth_skb_is_eligible_for_gro(const struct net_device *dev,
 					 const struct net_device *rcv,
 					 const struct sk_buff *skb)
 {
-	return !(dev->features & NETIF_F_ALL_TSO) ||
-		(skb->destructor == sock_wfree &&
-		 rcv->features & (NETIF_F_GRO_FRAGLIST | NETIF_F_GRO_UDP_FWD));
+	if (skb->destructor == sock_wfree ||
+	    (IS_ENABLED(CONFIG_INET) && skb->destructor == udp_wfree))
+		return rcv->features & (NETIF_F_GRO_FRAGLIST |
+					NETIF_F_GRO_UDP_FWD);
+
+	return !(dev->features & NETIF_F_ALL_TSO);
 }
 
 static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
diff --git a/include/net/sock.h b/include/net/sock.h
index a7f815c7cfdf..bd58901ff87a 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -1811,6 +1811,7 @@  static inline bool sock_allow_reclassification(const struct sock *csk)
 struct sock *sk_alloc(struct net *net, int family, gfp_t priority,
 		      struct proto *prot, int kern);
 void sk_free(struct sock *sk);
+void __sk_free(struct sock *sk);
 void sk_destruct(struct sock *sk);
 struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority);
 void sk_free_unlock_clone(struct sock *sk);
diff --git a/include/net/udp.h b/include/net/udp.h
index 488a6d2babcc..bdb3dabf789d 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -288,6 +288,7 @@  int udp_pre_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len);
 int __udp_disconnect(struct sock *sk, int flags);
 int udp_disconnect(struct sock *sk, int flags);
 __poll_t udp_poll(struct file *file, struct socket *sock, poll_table *wait);
+void udp_wfree(struct sk_buff *skb);
 struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
 				       netdev_features_t features,
 				       bool is_ipv6);
diff --git a/net/core/sock.c b/net/core/sock.c
index 158dbdebce6a..6172686635a8 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -140,6 +140,7 @@ 
 #include <trace/events/sock.h>
 
 #include <net/tcp.h>
+#include <net/udp.h>
 #include <net/busy_poll.h>
 #include <net/phonet/phonet.h>
 
@@ -2221,7 +2222,7 @@  void sk_destruct(struct sock *sk)
 		__sk_destruct(&sk->sk_rcu);
 }
 
-static void __sk_free(struct sock *sk)
+void __sk_free(struct sock *sk)
 {
 	if (likely(sk->sk_net_refcnt))
 		sock_inuse_add(sock_net(sk), -1);
@@ -2231,6 +2232,7 @@  static void __sk_free(struct sock *sk)
 	else
 		sk_destruct(sk);
 }
+EXPORT_SYMBOL(__sk_free);
 
 void sk_free(struct sock *sk)
 {
@@ -2531,8 +2533,10 @@  static bool can_skb_orphan_partial(const struct sk_buff *skb)
 	if (skb->decrypted)
 		return false;
 #endif
-	return (skb->destructor == sock_wfree ||
-		(IS_ENABLED(CONFIG_INET) && skb->destructor == tcp_wfree));
+	if (skb->destructor == sock_wfree)
+		return true;
+	return IS_ENABLED(CONFIG_INET) &&
+		(skb->destructor == tcp_wfree || skb->destructor == udp_wfree);
 }
 
 /* This helper is used by netem, as it can hold packets in its
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 148ffb007969..90ff77ab78f9 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -796,6 +796,42 @@  int udp_err(struct sk_buff *skb, u32 info)
 	return __udp4_lib_err(skb, info, dev_net(skb->dev)->ipv4.udp_table);
 }
 
+static inline bool __udp_wfree(struct sk_buff *skb)
+{
+	struct socket_wq *wq;
+	struct sock *sk = skb->sk;
+	bool free;
+
+	free = refcount_sub_and_test(skb->truesize, &sk->sk_wmem_alloc);
+	/* a full barrier is required before waitqueue_active() */
+	smp_mb__after_atomic();
+
+	if (!sock_writeable(sk))
+		goto out;
+
+	wq = rcu_dereference(sk->sk_wq);
+	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);
+out:
+	return free;
+}
+
+void udp_wfree(struct sk_buff *skb)
+{
+	bool free;
+
+	rcu_read_lock();
+	free = __udp_wfree(skb);
+	rcu_read_unlock();
+
+	if (unlikely(free))
+		__sk_free(skb->sk);
+}
+EXPORT_SYMBOL_GPL(udp_wfree);
+
 /*
  * Throw away all pending data and cancel the corking. Socket is locked.
  */
@@ -989,6 +1025,7 @@  int udp_push_pending_frames(struct sock *sk)
 	if (!skb)
 		goto out;
 
+	skb->destructor = udp_wfree;
 	err = udp_send_skb(skb, fl4, &inet->cork.base);
 
 out:
@@ -1246,8 +1283,10 @@  int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
 				  sizeof(struct udphdr), &ipc, &rt,
 				  &cork, msg->msg_flags);
 		err = PTR_ERR(skb);
-		if (!IS_ERR_OR_NULL(skb))
+		if (!IS_ERR_OR_NULL(skb)) {
+			skb->destructor = udp_wfree;
 			err = udp_send_skb(skb, fl4, &cork);
+		}
 		goto out;
 	}
 
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 3f2249b4cd5f..d0c74a8f0914 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -1309,6 +1309,7 @@  static int udp_v6_push_pending_frames(struct sock *sk)
 	if (!skb)
 		goto out;
 
+	skb->destructor = udp_wfree;
 	err = udp_v6_send_skb(skb, &inet_sk(sk)->cork.fl.u.ip6,
 			      &inet_sk(sk)->cork.base);
 out:
@@ -1576,8 +1577,10 @@  int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
 				   (struct rt6_info *)dst,
 				   msg->msg_flags, &cork);
 		err = PTR_ERR(skb);
-		if (!IS_ERR_OR_NULL(skb))
+		if (!IS_ERR_OR_NULL(skb)) {
+			skb->destructor = udp_wfree;
 			err = udp_v6_send_skb(skb, fl6, &cork.base);
+		}
 		/* ip6_make_skb steals dst reference */
 		goto out_no_dst;
 	}