udp: implement GRO support
Subsystems (8)
Paolo Abeni
maintainercover udp: implement GRO support
This series implements GRO support for UDP sockets, as the RX counterpart of commit bec1f6f69736 ("udp: generate gso with UDP_SEGMENT").
The core functionality is implemented by the second patch, introducing a new sockopt to enable UDP_GRO, while patch 3 implements support for passing the segment size to the user space via a new cmsg.
UDP GRO performs a socket lookup for each ingress packets and aggregate datagram directed to UDP GRO enabled sockets with constant l4 tuple.
UDP GRO packets can land on non GRO-enabled sockets, e.g. due to iptables NAT rules, and that could potentially confuse existing applications.
The solution adopted here is to de-segment the GRO packet before enqueuing as needed. Since we must cope with packet reinsertion after de-segmentation, the relevant code is factored-out in ipv4 and ipv6 specific helpers and exposed to UDP usage.
While the current code can probably be improved, this safeguard ,implemented in the patches 4-7, allows future enachements to enable UDP GSO offload on more virtual devices eventually even on forwarded packets.
The last 4 for patches implement some performance and functional self-tests, re-using the existing udpgso infrastructure. The problematic scenario described above is explicitly tested.
This revision of the series try to address the feedback provided by Willem and Subash on previous iteration.
Paolo Abeni (10):
udp: implement complete book-keeping for encap_needed
udp: implement GRO for plain UDP sockets.
udp: add support for UDP_GRO cmsg
ip: factor out protocol delivery helper
ipv6: factor out protocol delivery helper
udp: cope with UDP GRO packet misdirection
selftests: add GRO support to udp bench rx program
selftests: add dummy xdp test helper
selftests: add some benchmark for UDP GRO
selftests: add functionals test for UDP GRO
include/linux/udp.h | 25 ++- include/net/ip.h | 1 + include/net/ipv6.h | 2 + include/net/udp.h | 45 ++++- include/net/udp_tunnel.h | 6 + include/uapi/linux/udp.h | 1 + net/ipv4/ip_input.c | 73 ++++---- net/ipv4/udp.c | 54 +++++- net/ipv4/udp_offload.c | 109 +++++++++--- net/ipv6/ip6_input.c | 28 ++-- net/ipv6/udp.c | 41 ++++- net/ipv6/udp_offload.c | 6 +- tools/testing/selftests/bpf/Makefile | 3 +- tools/testing/selftests/bpf/xdp_dummy.c | 13 ++ tools/testing/selftests/net/Makefile | 1 + tools/testing/selftests/net/udpgro.sh | 148 +++++++++++++++++ tools/testing/selftests/net/udpgro_bench.sh | 95 +++++++++++ tools/testing/selftests/net/udpgso_bench.sh | 2 +- tools/testing/selftests/net/udpgso_bench_rx.c | 156 ++++++++++++++++-- tools/testing/selftests/net/udpgso_bench_tx.c | 22 ++- 20 files changed, 708 insertions(+), 123 deletions(-) create mode 100644 tools/testing/selftests/bpf/xdp_dummy.c create mode 100755 tools/testing/selftests/net/udpgro.sh create mode 100755 tools/testing/selftests/net/udpgro_bench.sh
Signature · 1 line
2.17.2
Paolo Abeni
maintainerPATCH 1/10 udp: implement complete book-keeping for encap_needed
The *encap_needed static keys are enabled by UDP tunnels and several UDP encapsulations type, but they are never turned off. This can cause unneeded overall performance degradation for systems where such features are used transiently.
This patch introduces complete book-keeping for such keys, decreasing the usage at socket destruction time, if needed, and avoiding that the same socket could increase the key usage multiple times.
rfc v3 -> v1:
- add socket lock around udp_tunnel_encap_enable()
rfc v2 -> rfc v3:
- use udp_tunnel_encap_enable() in setsockopt()
--- include/linux/udp.h | 7 ++++++- include/net/udp_tunnel.h | 6 ++++++ net/ipv4/udp.c | 19 +++++++++++++------ net/ipv6/udp.c | 14 +++++++++----- 4 files changed, 34 insertions(+), 12 deletions(-)
include/linux/udp.h +6 -1
@@ -49,7 +49,12 @@ struct udp_sock {unsigned int corkflag;/* Cork is required */ __u8 encap_type;/* Is this an Encapsulation socket? */ unsigned char no_check6_tx:1,/* Send zero UDP6 checksums on TX? */ - no_check6_rx:1;/* Allow zero UDP6 checksums on RX? */ + no_check6_rx:1,/* Allow zero UDP6 checksums on RX? */ + encap_enabled:1; /* This socket enabled encap +* processing; UDP tunnels and +* different encapsulation layer set +* this +*/ /* * Following member retains the information to create a UDP header * when the socket is uncorked.
include/net/udp_tunnel.h +6 -0
@@ -165,6 +165,12 @@ static inline int udp_tunnel_handle_offloads(struct sk_buff *skb, bool udp_csum)static inline void udp_tunnel_encap_enable (struct socket *sock) { +struct udp_sock *up =udp_sk (sock->sk); + +if (up->encap_enabled) +return ; + + up->encap_enabled =1 ;#if IS_ENABLED (CONFIG_IPV6 )if (sock->sk->sk_family ==PF_INET6 ) ipv6_stub->udpv6_encap_enable ();
net/ipv4/udp.c +13 -6
@@ -115,6 +115,7 @@#include "udp_impl.h" #include <net/sock_reuseport.h> #include <net/addrconf.h> +#include <net/udp_tunnel.h> struct udp_table udp_table__read_mostly ;EXPORT_SYMBOL (udp_table);
@@ -2398,11 +2399,15 @@ void udp_destroy_sock(struct sock *sk)bool slow =lock_sock_fast (sk);udp_flush_pending_frames (sk);unlock_sock_fast (sk, slow); -if (static_branch_unlikely (&udp_encap_needed_key) && up->encap_type) { -void (*encap_destroy)(struct sock *sk); - encap_destroy =READ_ONCE (up->encap_destroy); -if (encap_destroy) -encap_destroy (sk); +if (static_branch_unlikely (&udp_encap_needed_key)) { +if (up->encap_type) { +void (*encap_destroy)(struct sock *sk); + encap_destroy =READ_ONCE (up->encap_destroy); +if (encap_destroy) +encap_destroy (sk); + } +if (up->encap_enabled) +static_branch_disable (&udp_encap_needed_key); } }
@@ -2447,7 +2452,9 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,/* FALLTHROUGH */ case UDP_ENCAP_L2TPINUDP : up->encap_type = val; -udp_encap_enable (); +lock_sock (sk); +udp_tunnel_encap_enable (sk->sk_socket); +release_sock (sk);break ;default : err = -ENOPROTOOPT ;
net/ipv6/udp.c +9 -5
@@ -1458,11 +1458,15 @@ void udpv6_destroy_sock(struct sock *sk)udp_v6_flush_pending_frames (sk);release_sock (sk); -if (static_branch_unlikely (&udpv6_encap_needed_key) && up->encap_type) { -void (*encap_destroy)(struct sock *sk); - encap_destroy =READ_ONCE (up->encap_destroy); -if (encap_destroy) -encap_destroy (sk); +if (static_branch_unlikely (&udpv6_encap_needed_key)) { +if (up->encap_type) { +void (*encap_destroy)(struct sock *sk); + encap_destroy =READ_ONCE (up->encap_destroy); +if (encap_destroy) +encap_destroy (sk); + } +if (up->encap_enabled) +static_branch_disable (&udpv6_encap_needed_key); }inet6_destroy_sock (sk);
Signature · 1 line
2.17.2
Paolo Abeni
maintainerPATCH 2/10 udp: implement GRO for plain UDP sockets.
In reply to Paolo AbeniThis is the RX counterpart of commit bec1f6f69736 ("udp: generate gso with UDP_SEGMENT"). When UDP_GRO is enabled, such socket is also eligible for GRO in the rx path: UDP segments directed to such socket are assembled into a larger GSO_UDP_L4 packet.
The core UDP GRO support is enabled with setsockopt(UDP_GRO).
Initial benchmark numbers:
Before:
udp rx: 1079 MB/s 769065 calls/s
After:
udp rx: 1466 MB/s 24877 calls/s
This change introduces a side effect in respect to UDP tunnels:
after a UDP tunnel creation, now the kernel performs a lookup per ingress UDP packet, while before such lookup happened only if the ingress packet carried a valid internal header csum.
rfc v2 -> rfc v3:
- fixed typos in macro name and comments
- really enforce UDP_GRO_CNT_MAX, instead of UDP_GRO_CNT_MAX + 1
- acquire socket lock in UDP_GRO setsockopt
rfc v1 -> rfc v2:
- use a new option to enable UDP GRO
- use static keys to protect the UDP GRO socket lookup
--
Note: I opted for acquiring the socket lock only for the newly introduced setsockopt instead for every value, despite the previous conversation on this topic, to avoid introducing somewhat larger and unrelated changes.
--- include/linux/udp.h | 3 +- include/uapi/linux/udp.h | 1 + net/ipv4/udp.c | 8 +++ net/ipv4/udp_offload.c | 109 +++++++++++++++++++++++++++++++-------- net/ipv6/udp_offload.c | 6 +-- 5 files changed, 99 insertions(+), 28 deletions(-)
include/linux/udp.h +2 -1
@@ -50,11 +50,12 @@ struct udp_sock {__u8 encap_type;/* Is this an Encapsulation socket? */ unsigned char no_check6_tx:1,/* Send zero UDP6 checksums on TX? */ no_check6_rx:1,/* Allow zero UDP6 checksums on RX? */ - encap_enabled:1; /* This socket enabled encap + encap_enabled:1, /* This socket enabled encap* processing; UDP tunnels and * different encapsulation layer set * this */ + gro_enabled:1; /* Can accept GRO packets *//* * Following member retains the information to create a UDP header * when the socket is uncorked.
include/uapi/linux/udp.h +1 -0
@@ -33,6 +33,7 @@ struct udphdr {#define UDP_NO_CHECK6_TX 101/* Disable sending checksum for UDP6X */ #define UDP_NO_CHECK6_RX 102/* Disable accpeting checksum for UDP6 */ #define UDP_SEGMENT 103/* Set GSO segmentation size */ +#define UDP_GRO 104/* This socket can receive UDP GRO packets */ /* UDP encapsulation types */ #define UDP_ENCAP_ESPINUDP_NON_IKE 1/* draft-ietf-ipsec-nat-t-ike-00/01 */
net/ipv4/udp.c +8 -0
@@ -2476,6 +2476,14 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, up->gso_size = val;break ; +case UDP_GRO : +lock_sock (sk); +if (valbool) +udp_tunnel_encap_enable (sk->sk_socket); + up->gro_enabled = valbool; +release_sock (sk); +break ; +/* * UDP-Lite's partial checksum coverage (RFC 3828). */
net/ipv4/udp_offload.c +87 -22
158 lines not on this page · raw message
net/ipv6/udp_offload.c +1 -5
@@ -147,13 +147,9 @@ static int udp6_gro_complete(struct sk_buff *skb, int nhoff)const struct ipv6hdr *ipv6h =ipv6_hdr (skb);struct udphdr *uh = (struct udphdr *)(skb->data + nhoff); -if (uh->check) { -skb_shinfo (skb)->gso_type |=SKB_GSO_UDP_TUNNEL_CSUM ; +if (uh->check) uh->check = ~udp_v6_check (skb->len - nhoff, &ipv6h->saddr, &ipv6h->daddr,0 ); - }else { -skb_shinfo (skb)->gso_type |=SKB_GSO_UDP_TUNNEL ; - }return udp_gro_complete (skb, nhoff, udp6_lib_lookup_skb); }
Signature · 1 line
2.17.2
Paolo Abeni
maintainerPATCH 3/10 udp: add support for UDP_GRO cmsg
In reply to Paolo AbeniWhen UDP GRO is enabled, the UDP_GRO cmsg will carry the ingress datagram size. User-space can use such info to compute the original packets layout.
--- include/linux/udp.h | 11 +++++++++++ net/ipv4/udp.c | 4 ++++ net/ipv6/udp.c | 3 +++ 3 files changed, 18 insertions(+)
include/linux/udp.h +11 -0
@@ -121,6 +121,17 @@ static inline bool udp_get_no_check6_rx(struct sock *sk)return udp_sk (sk)->no_check6_rx; } +static inline void udp_cmsg_recv (struct msghdr *msg,struct sock *sk, +struct sk_buff *skb) +{ +int gso_size; + +if (skb_shinfo (skb)->gso_type &SKB_GSO_UDP_L4 ) { + gso_size =skb_shinfo (skb)->gso_size; +put_cmsg (msg,SOL_UDP ,UDP_GRO ,sizeof (gso_size), &gso_size); + } +} +#define udp_portaddr_for_each_entry (__sk, list) \ hlist_for_each_entry(__sk, list, __sk_common.skc_portaddr_node)
net/ipv4/udp.c +4 -0
@@ -1714,6 +1714,10 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock,memset (sin->sin_zero,0 ,sizeof (sin->sin_zero)); *addr_len =sizeof (*sin); } + +if (udp_sk (sk)->gro_enabled) +udp_cmsg_recv (msg, sk, skb); +if (inet->cmsg_flags)ip_cmsg_recv_offset (msg, sk, skb,sizeof (struct udphdr ), off);
net/ipv6/udp.c +3 -0
@@ -421,6 +421,9 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, *addr_len =sizeof (*sin6); } +if (udp_sk (sk)->gro_enabled) +udp_cmsg_recv (msg, sk, skb); +if (np->rxopt.all)ip6_datagram_recv_common_ctl (sk, msg, skb);
Signature · 1 line
2.17.2
Paolo Abeni
maintainerPATCH 4/10 ip: factor out protocol delivery helper
In reply to Paolo AbeniSo that we can re-use it at the UDP level in a later patch
rfc v3 -> v1
- add the helper declaration into the ip header
--- include/net/ip.h | 1 + net/ipv4/ip_input.c | 73 ++++++++++++++++++++++----------------------- 2 files changed, 37 insertions(+), 37 deletions(-)
include/net/ip.h +1 -0
@@ -155,6 +155,7 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,void ip_list_rcv (struct list_head *head,struct packet_type *pt,struct net_device *orig_dev);int ip_local_deliver (struct sk_buff *skb); +void ip_protocol_deliver_rcu (struct net *net,struct sk_buff *skb,int proto);int ip_mr_input (struct sk_buff *skb);int ip_output (struct net *net,struct sock *sk,struct sk_buff *skb);int ip_mc_output (struct net *net,struct sock *sk,struct sk_buff *skb);
net/ipv4/ip_input.c +36 -37
87 lines not on this page · raw message
Signature · 1 line
2.17.2
Paolo Abeni
maintainerPATCH 5/10 ipv6: factor out protocol delivery helper
In reply to Paolo AbeniSo that we can re-use it at the UDP level in the next patch
rfc v3 -> v1:
- add the helper declaration into the ipv6 header
--- include/net/ipv6.h | 2 ++ net/ipv6/ip6_input.c | 28 ++++++++++++++++------------ 2 files changed, 18 insertions(+), 12 deletions(-)
include/net/ipv6.h +2 -0
@@ -975,6 +975,8 @@ int ip6_output(struct net *net, struct sock *sk, struct sk_buff *skb);int ip6_forward (struct sk_buff *skb);int ip6_input (struct sk_buff *skb);int ip6_mc_input (struct sk_buff *skb); +void ip6_protocol_deliver_rcu (struct net *net,struct sk_buff *skb,int nexthdr, +bool have_final);int __ip6_local_out (struct net *net,struct sock *sk,struct sk_buff *skb);int ip6_local_out (struct net *net,struct sock *sk,struct sk_buff *skb);
net/ipv6/ip6_input.c +16 -12
@@ -319,28 +319,26 @@ void ipv6_list_rcv(struct list_head *head, struct packet_type *pt,/* * Deliver the packet to the host */ - - -static int ip6_input_finish (struct net *net,struct sock *sk,struct sk_buff *skb) +void ip6_protocol_deliver_rcu (struct net *net,struct sk_buff *skb,int nexthdr, +bool have_final) {const struct inet6_protocol *ipprot;struct inet6_dev *idev;unsigned int nhoff; -int nexthdr;bool raw; -bool have_final = false;/* * Parse extension headers */ -rcu_read_lock (); resubmit: idev =ip6_dst_idev (skb_dst (skb)); -if (!pskb_pull (skb,skb_transport_offset (skb))) - goto discard; nhoff =IP6CB (skb)->nhoff; - nexthdr =skb_network_header (skb)[nhoff]; +if (!have_final) { +if (!pskb_pull (skb,skb_transport_offset (skb))) + goto discard; + nexthdr =skb_network_header (skb)[nhoff]; + } resubmit_final: raw =raw6_local_deliver (skb, nexthdr);
@@ -411,13 +409,19 @@ static int ip6_input_finish(struct net *net, struct sock *sk, struct sk_buff *skconsume_skb (skb); } } -rcu_read_unlock (); -return 0 ; +return ; discard:__IP6_INC_STATS (net, idev,IPSTATS_MIB_INDISCARDS ); -rcu_read_unlock ();kfree_skb (skb); +} + +static int ip6_input_finish (struct net *net,struct sock *sk,struct sk_buff *skb) +{ +rcu_read_lock (); +ip6_protocol_deliver_rcu (net, skb,0 , false); +rcu_read_unlock (); +return 0 ; }
Signature · 1 line
2.17.2
Paolo Abeni
maintainerPATCH 6/10 udp: cope with UDP GRO packet misdirection
In reply to Paolo AbeniIn some scenarios, the GRO engine can assemble an UDP GRO packet that ultimately lands on a non GRO-enabled socket.
This patch tries to address the issue explicitly checking for the UDP socket features before enqueuing the packet, and eventually segmenting the unexpected GRO packet, as needed.
We must also cope with re-insertion requests: after segmentation the UDP code calls the helper introduced by the previous patches, as needed.
Segmentation is performed by a common helper, which takes care of updating socket and protocol stats is case of failure.
rfc v3 -> v1
- fix compile issues with rxrpc
- when gso_segment returns NULL, treat is as an error
- added 'ipv4' argument to udp_rcv_segment()
rfc v2 -> rfc v3
- moved udp_rcv_segment() into net/udp.h, account errors to socket and ns, always return NULL or segs list
--- include/linux/udp.h | 6 ++++++ include/net/udp.h | 45 +++++++++++++++++++++++++++++++++++++-------- net/ipv4/udp.c | 23 ++++++++++++++++++++++- net/ipv6/udp.c | 24 +++++++++++++++++++++++- 4 files changed, 88 insertions(+), 10 deletions(-)
include/linux/udp.h +6 -0
@@ -132,6 +132,12 @@ static inline void udp_cmsg_recv(struct msghdr *msg, struct sock *sk, } } +static inline bool udp_unexpected_gso (struct sock *sk,struct sk_buff *skb) +{ +return !udp_sk (sk)->gro_enabled &&skb_is_gso (skb) && +skb_shinfo (skb)->gso_type &SKB_GSO_UDP_L4 ; +} +#define udp_portaddr_for_each_entry (__sk, list) \ hlist_for_each_entry(__sk, list, __sk_common.skc_portaddr_node)
include/net/udp.h +37 -8
58 lines not on this page · raw message
net/ipv4/udp.c +22 -1
@@ -1909,7 +1909,7 @@ EXPORT_SYMBOL(udp_encap_enable);* Note that in the success and error cases, the skb is assumed to * have either been requeued or freed. */ -static int udp_queue_rcv_skb (struct sock *sk,struct sk_buff *skb) +static int udp_queue_rcv_one_skb (struct sock *sk,struct sk_buff *skb) {struct udp_sock *up =udp_sk (sk);int is_udplite =IS_UDPLITE (sk);
@@ -2012,6 +2012,27 @@ static int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)return -1 ; } +static int udp_queue_rcv_skb (struct sock *sk,struct sk_buff *skb) +{ +struct sk_buff *next, *segs; +int ret; + +if (likely (!udp_unexpected_gso (sk, skb))) +return udp_queue_rcv_one_skb (sk, skb); + +BUILD_BUG_ON (sizeof (struct udp_skb_cb ) >SKB_SGO_CB_OFFSET ); +__skb_push (skb, -skb_mac_offset (skb)); + segs =udp_rcv_segment (sk, skb, true); +for (skb = segs; skb; skb = next) { + next = skb->next; +__skb_pull (skb,skb_transport_offset (skb)); + ret =udp_queue_rcv_one_skb (sk, skb); +if (ret >0 ) +ip_protocol_deliver_rcu (dev_net (skb->dev), skb, -ret); + } +return 0 ; +} +/* For TCP sockets, sk_rx_dst is protected by socket lock * For UDP, we use xchg() to guard against concurrent changes. */
net/ipv6/udp.c +23 -1
@@ -558,7 +558,7 @@ void udpv6_encap_enable(void) }EXPORT_SYMBOL (udpv6_encap_enable); -static int udpv6_queue_rcv_skb (struct sock *sk,struct sk_buff *skb) +static int udpv6_queue_rcv_one_skb (struct sock *sk,struct sk_buff *skb) {struct udp_sock *up =udp_sk (sk);int is_udplite =IS_UDPLITE (sk);
@@ -641,6 +641,28 @@ static int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)return -1 ; } +static int udpv6_queue_rcv_skb (struct sock *sk,struct sk_buff *skb) +{ +struct sk_buff *next, *segs; +int ret; + +if (likely (!udp_unexpected_gso (sk, skb))) +return udpv6_queue_rcv_one_skb (sk, skb); + +__skb_push (skb, -skb_mac_offset (skb)); + segs =udp_rcv_segment (sk, skb, false); +for (skb = segs; skb; skb = next) { + next = skb->next; +__skb_pull (skb,skb_transport_offset (skb)); + + ret =udpv6_queue_rcv_one_skb (sk, skb); +if (ret >0 ) +ip6_protocol_deliver_rcu (dev_net (skb->dev), skb, ret, + true); + } +return 0 ; +} + static bool __udp_v6_is_mcast_sock(struct net *net, struct sock *sk, __be16 loc_port, const struct in6_addr *loc_addr, __be16 rmt_port, const struct in6_addr *rmt_addr,
Signature · 1 line
2.17.2
Paolo Abeni
maintainerPATCH 7/10 selftests: add GRO support to udp bench rx program
In reply to Paolo AbeniAnd fix a couple of buglets (port option processing, clean termination on SIGINT). This is preparatory work for GRO tests.
rfc v2 -> rfc v3:
- use ETH_MAX_MTU
--- tools/testing/selftests/net/udpgso_bench_rx.c | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-)
tools/testing/selftests/net/udpgso_bench_rx.c +30 -7
@@ -31,9 +31,15 @@#include <sys/wait.h> #include <unistd.h> +#ifndef UDP_GRO +#define UDP_GRO 104 +#endif +static int cfg_port =8000 ;static bool cfg_tcp;static bool cfg_verify; +static bool cfg_read_all; +static bool cfg_gro_segment;static bool interrupted;static unsigned long packets, bytes;
@@ -63,6 +69,8 @@ static void do_poll(int fd) do { ret = poll(&pfd, 1, 10); + if (interrupted) + break; if (ret == -1) error(1, errno, "poll"); if (ret == 0)
@@ -70,7 +78,7 @@ static void do_poll(int fd)if (pfd.revents !=POLLIN )error (1 , errno,"poll: 0x%x expected 0x%x\n" , pfd.revents,POLLIN ); - }while (!ret && !interrupted); + }while (!ret); }static int do_socket (bool do_tcp)
@@ -102,6 +110,8 @@ static int do_socket(bool do_tcp)error (1 , errno,"listen" );do_poll (accept_fd); +if (interrupted) +exit (0 ); fd =accept (accept_fd,NULL ,NULL );if (fd ==-1 )
@@ -167,10 +177,10 @@ static void do_verify_udp(const char *data, int len)/* Flush all outstanding datagrams. Verify first few bytes of each. */ static void do_flush_udp (int fd) { -static char rbuf[ETH_DATA_LEN ]; +static char rbuf[ETH_MAX_MTU ];int ret, len, budget =256 ; - len = cfg_verify ?sizeof (rbuf) :0 ; + len = cfg_read_all ?sizeof (rbuf) :0 ;while (budget--) {/* MSG_TRUNC will make return value full datagram length */ ret =recv (fd, rbuf, len,MSG_TRUNC |MSG_DONTWAIT );
@@ -178,7 +188,7 @@ static void do_flush_udp(int fd)return ;if (ret ==-1 )error (1 , errno,"recv" ); -if (len) { +if (len && cfg_verify) {if (ret ==0 )error (1 , errno,"recv: 0 byte datagram\n" );
@@ -192,23 +202,30 @@ static void do_flush_udp(int fd)static void usage (const char *filepath) { -error (1 ,0 ,"Usage: %s [-tv] [-p port]" , filepath); +error (1 ,0 ,"Usage: %s [-Grtv] [-p port]" , filepath); }static void parse_opts (int argc,char **argv) {int c; -while ((c =getopt (argc, argv,"ptv" )) !=-1 ) { +while ((c =getopt (argc, argv,"Gp:rtv" )) !=-1 ) {switch (c) { +case 'G' : + cfg_gro_segment = true; +break ;case 'p' : - cfg_port =htons (strtoul (optarg,NULL ,0 )); + cfg_port =strtoul (optarg,NULL ,0 ); +break ; +case 'r' : + cfg_read_all = true;break ;case 't' : cfg_tcp = true;break ;case 'v' : cfg_verify = true; + cfg_read_all = true;break ; } }
@@ -227,6 +244,12 @@ static void do_recv(void) fd =do_socket (cfg_tcp); +if (cfg_gro_segment && !cfg_tcp) { +int val =1 ; +if (setsockopt (fd,IPPROTO_UDP ,UDP_GRO , &val,sizeof (val))) +error (1 , errno,"setsockopt UDP_GRO" ); + } + treport =gettimeofday_ms () +1000 ; do { do_poll(fd);
Signature · 1 line
2.17.2
Paolo Abeni
maintainerPATCH 8/10 selftests: add dummy xdp test helper
In reply to Paolo AbeniThis trivial XDP program does nothing, but will be used by the next patch to test the GRO path in a net namespace, leveraging the veth XDP implementation.
It's added here, despite its 'net' usage, to avoid the duplication of the llc-related makefile boilerplate.
rfc v3 -> v1:
- move the helper implementation into the bpf directory, don't touch udpgso_bench_rx
rfc v2 -> rfc v3:
- move 'x' option handling here
--- tools/testing/selftests/bpf/Makefile | 3 ++- tools/testing/selftests/bpf/xdp_dummy.c | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/xdp_dummy.c
tools/testing/selftests/bpf/Makefile +2 -1
@@ -37,7 +37,8 @@ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test test_lwt_seg6local.o sendmsg4_prog.o sendmsg6_prog.o test_lirc_mode2_kern.o \ get_cgroup_id_kern.o socket_cookie_prog.o test_select_reuseport_kern.o \ test_skb_cgroup_id_kern.o bpf_flow.o netcnt_prog.o \ - test_sk_lookup_kern.o test_xdp_vlan.o test_queue_map.o test_stack_map.o + test_sk_lookup_kern.o test_xdp_vlan.o test_queue_map.o test_stack_map.o \ + xdp_dummy.o # Order correspond to 'make run_tests' order TEST_PROGS := test_kmod.sh \
tools/testing/selftests/bpf/xdp_dummy.c new file +13 -0
@@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define KBUILD_MODNAME "xdp_dummy" +#include <linux/bpf.h> +#include "bpf_helpers.h" + +SEC("xdp_dummy") +int xdp_dummy_prog (struct xdp_md *ctx) +{ +return XDP_PASS ; +} + +char _license[] SEC("license") = "GPL";
Signature · 1 line
2.17.2
Paolo Abeni
maintainerPATCH 9/10 selftests: add some benchmark for UDP GRO
In reply to Paolo AbeniRun on top of veth pair, using a dummy XDP program to enable the GRO.
rfc v3 -> v1:
- use ip route to attach the xdp helper to the veth
--- tools/testing/selftests/net/Makefile | 1 + tools/testing/selftests/net/udpgro_bench.sh | 93 +++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100755 tools/testing/selftests/net/udpgro_bench.sh
tools/testing/selftests/net/Makefile +1 -0
@@ -7,6 +7,7 @@ CFLAGS += -I../../../../usr/include/ TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh netdevice.sh rtnetlink.sh TEST_PROGS += fib_tests.sh fib-onlink-tests.sh pmtu.sh udpgso.sh ip_defrag.sh TEST_PROGS += udpgso_bench.sh fib_rule_tests.sh msg_zerocopy.sh psock_snd.sh +TEST_PROGS += udpgro_bench.sh TEST_PROGS_EXTENDED := in_netns.sh TEST_GEN_FILES = socket TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy
tools/testing/selftests/net/udpgro_bench.sh new file +93 -0
93 lines not on this page · raw message
Signature · 1 line
2.17.2
Paolo Abeni
maintainerPATCH 10/10 selftests: add functionals test for UDP GRO
In reply to Paolo AbeniExtends the existing udp programs to allow checking for proper GRO aggregation/GSO size, and run the tests via a shell script, using a veth pair with XDP program attached to trigger the GRO code path.
rfc v3 -> v1:
- use ip route to attach the xdp helper to the veth
rfc v2 -> rfc v3:
- add missing test program options documentation
- fix sporatic test failures (receiver faster than sender)
--- tools/testing/selftests/net/Makefile | 2 +- tools/testing/selftests/net/udpgro.sh | 148 ++++++++++++++++++ tools/testing/selftests/net/udpgro_bench.sh | 8 +- tools/testing/selftests/net/udpgso_bench.sh | 2 +- tools/testing/selftests/net/udpgso_bench_rx.c | 123 +++++++++++++-- tools/testing/selftests/net/udpgso_bench_tx.c | 22 ++- 6 files changed, 282 insertions(+), 23 deletions(-) create mode 100755 tools/testing/selftests/net/udpgro.sh
tools/testing/selftests/net/Makefile +1 -1
@@ -7,7 +7,7 @@ CFLAGS += -I../../../../usr/include/ TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh netdevice.sh rtnetlink.sh TEST_PROGS += fib_tests.sh fib-onlink-tests.sh pmtu.sh udpgso.sh ip_defrag.sh TEST_PROGS += udpgso_bench.sh fib_rule_tests.sh msg_zerocopy.sh psock_snd.sh -TEST_PROGS += udpgro_bench.sh +TEST_PROGS += udpgro_bench.sh udpgro.sh TEST_PROGS_EXTENDED := in_netns.sh TEST_GEN_FILES = socket TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy
tools/testing/selftests/net/udpgro.sh new file +148 -0
148 lines not on this page · raw message
tools/testing/selftests/net/udpgro_bench.sh +5 -3
@@ -18,7 +18,9 @@ run_one() { # use 'rx' as separator between sender args and receiver args local -r all="$@" local -r tx_args=${all%rx*} - local -r rx_args=${all#*rx} + local rx_args=${all#*rx} + + [[ "${tx_args}" == *"-4"* ]] && rx_args="${rx_args} -4" ip netns add "${PEER_NS}" ip -netns "${PEER_NS}" link set lo up
@@ -51,10 +53,10 @@ run_udp() { local -r args=$@ echo "udp gso - over veth touching data" - run_in_netns ${args} -S rx + run_in_netns ${args} -S 0 rx echo "udp gso and gro - over veth touching data" - run_in_netns ${args} -S rx -G + run_in_netns ${args} -S 0 rx -G } run_tcp() {
tools/testing/selftests/net/udpgso_bench.sh +1 -1
@@ -34,7 +34,7 @@ run_udp() { run_in_netns ${args} echo "udp gso" - run_in_netns ${args} -S + run_in_netns ${args} -S 0 } run_tcp() {
tools/testing/selftests/net/udpgso_bench_rx.c +111 -12
214 lines not on this page · raw message
tools/testing/selftests/net/udpgso_bench_tx.c +16 -6
@@ -52,6 +52,8 @@ static bool cfg_segment;static bool cfg_sendmmsg;static bool cfg_tcp;static bool cfg_zerocopy; +static int cfg_msg_nr; +static uint16_t cfg_gso_size;static socklen_t cfg_alen;static struct sockaddr_storage cfg_dst_addr;
@@ -205,14 +207,14 @@ static void send_udp_segment_cmsg(struct cmsghdr *cm) cm->cmsg_level =SOL_UDP ; cm->cmsg_type =UDP_SEGMENT ; - cm->cmsg_len =CMSG_LEN (sizeof (cfg_mss)); + cm->cmsg_len =CMSG_LEN (sizeof (cfg_gso_size)); valp = (void *)CMSG_DATA (cm); - *valp = cfg_mss; + *valp = cfg_gso_size; }static int send_udp_segment (int fd,char *data) { -char control[CMSG_SPACE (sizeof (cfg_mss))] = {0 }; +char control[CMSG_SPACE (sizeof (cfg_gso_size))] = {0 };struct msghdr msg = {0 };struct iovec iov = {0 };int ret;
@@ -241,7 +243,7 @@ static int send_udp_segment(int fd, char *data)static void usage (const char *filepath) { -error (1 ,0 ,"Usage: %s [-46cmStuz] [-C cpu] [-D dst ip] [-l secs] [-p port] [-s sendsize]" , +error (1 ,0 ,"Usage: %s [-46cmtuz] [-C cpu] [-D dst ip] [-l secs] [-m messagenr] [-p port] [-s sendsize] [-S gsosize]" , filepath); }
@@ -250,7 +252,7 @@ static void parse_opts(int argc, char **argv)int max_len, hdrlen;int c; -while ((c =getopt (argc, argv,"46cC:D:l:mp:s:Stuz" )) !=-1 ) { +while ((c =getopt (argc, argv,"46cC:D:l:mM:p:s:S:tuz" )) !=-1 ) {switch (c) {case '4' :if (cfg_family !=PF_UNSPEC )
@@ -279,6 +281,9 @@ static void parse_opts(int argc, char **argv)case 'm' : cfg_sendmmsg = true;break ; +case 'M' : + cfg_msg_nr =strtoul (optarg,NULL ,10 ); +break ;case 'p' : cfg_port =strtoul (optarg,NULL ,0 );break ;
@@ -286,6 +291,7 @@ static void parse_opts(int argc, char **argv) cfg_payload_len =strtoul (optarg,NULL ,0 );break ;case 'S' : + cfg_gso_size =strtoul (optarg,NULL ,0 ); cfg_segment = true;break ;case 't' :
@@ -317,6 +323,8 @@ static void parse_opts(int argc, char **argv) cfg_mss =ETH_DATA_LEN - hdrlen; max_len =ETH_MAX_MTU - hdrlen; +if (!cfg_gso_size) + cfg_gso_size = cfg_mss;if (cfg_payload_len > max_len) error(1, 0, "payload length %u exceeds max %u",
@@ -392,10 +400,12 @@ int main(int argc, char **argv)else num_sends +=send_udp (fd, buf[i]); num_msgs++; -if (cfg_zerocopy && ((num_msgs &0xF ) ==0 ))flush_zerocopy (fd); +if (cfg_msg_nr && num_msgs >= cfg_msg_nr) +break ; + tnow =gettimeofday_ms ();if (tnow > treport) { fprintf(stderr,
Signature · 1 line
2.17.2
Quoted 65 lines · Paolo Abeni
This series implements GRO support for UDP sockets, as the RX counterpart of commit
bec1f6f69736("udp: generate gso withUDP_SEGMENT").
The core functionality is implemented by the second patch, introducing a new sockopt to enableUDP_GRO, while patch 3 implements support for passing the segment size to the user space via a new cmsg.
UDP GRO performs a socket lookup for each ingress packets and aggregate datagram directed to UDP GRO enabled sockets with constant l4 tuple.UDP GRO packets can land on non GRO-enabled sockets, e.g. due to iptables NAT rules, and that could potentially confuse existing applications.
The solution adopted here is to de-segment the GRO packet before enqueuing as needed. Since we must cope with packet reinsertion after de-segmentation, the relevant code is factored-out in ipv4 and ipv6 specific helpers and exposed to UDP usage.
While the current code can probably be improved, this safeguard ,implemented in the patches 4-7, allows future enachements to enable UDP GSO offload on more virtual devices eventually even on forwarded packets.
The last 4 for patches implement some performance and functional self-tests, re-using the existing udpgso infrastructure. The problematic scenario described above is explicitly tested.
This revision of the series try to address the feedback provided by Willem and Subash on previous iteration.
Paolo Abeni (10):
udp: implement complete book-keeping forencap_needed
udp: implement GRO for plain UDP sockets.
udp: add support forUDP_GROcmsg
ip: factor out protocol delivery helper
ipv6: factor out protocol delivery helper
udp: cope with UDP GRO packet misdirection
selftests: add GRO support to udp bench rx program
selftests: add dummy xdp test helper
selftests: add some benchmark for UDP GRO
selftests: add functionals test for UDP GROinclude/linux/udp.h | 25 ++- include/net/ip.h | 1 + include/net/ipv6.h | 2 + include/net/udp.h | 45 ++++- include/net/udp_tunnel.h | 6 + include/uapi/linux/udp.h | 1 + net/ipv4/ip_input.c | 73 ++++---- net/ipv4/udp.c | 54 +++++- net/ipv4/udp_offload.c | 109 +++++++++--- net/ipv6/ip6_input.c | 28 ++-- net/ipv6/udp.c | 41 ++++- net/ipv6/udp_offload.c | 6 +- tools/testing/selftests/bpf/Makefile | 3 +- tools/testing/selftests/bpf/xdp_dummy.c | 13 ++ tools/testing/selftests/net/Makefile | 1 + tools/testing/selftests/net/udpgro.sh | 148 +++++++++++++++++ tools/testing/selftests/net/udpgro_bench.sh | 95 +++++++++++ tools/testing/selftests/net/udpgso_bench.sh | 2 +- tools/testing/selftests/net/udpgso_bench_rx.c | 156 ++++++++++++++++-- tools/testing/selftests/net/udpgso_bench_tx.c | 22 ++- 20 files changed, 708 insertions(+), 123 deletions(-) create mode 100644 tools/testing/selftests/bpf/xdp_dummy.c create mode 100755 tools/testing/selftests/net/udpgro.sh create mode 100755 tools/testing/selftests/net/udpgro_bench.sh
Quoted 3 lines
--
2.17.2
For the series:
(Let me know if I need to Ack each patch directly)
Looks great. Thanks for addressing all comments, Paolo. I applied the series mbox from patchwork to the same commit as the previous RFCs to be able to incrementally review with git diff.
David Miller
maintainerFrom: Willem de Bruijn <[email protected]>
Date: Wed, 7 Nov 2018 13:56:52 -0600
Quoted 5 lines
For the series:
Acked-by Willem de Bruijn <[email protected]>(Let me know if I need to Ack each patch directly)
You do not.
This is why I like header postings so much, reviewers can just reply to it to ACK the entire series.
David Miller
maintainerFrom: Paolo Abeni <[email protected]>
Date: Wed, 7 Nov 2018 12:38:27 +0100
Quoted 20 lines
This series implements GRO support for UDP sockets, as the RX counterpart of commit
bec1f6f69736("udp: generate gso withUDP_SEGMENT").
The core functionality is implemented by the second patch, introducing a new sockopt to enableUDP_GRO, while patch 3 implements support for passing the segment size to the user space via a new cmsg.
UDP GRO performs a socket lookup for each ingress packets and aggregate datagram directed to UDP GRO enabled sockets with constant l4 tuple.UDP GRO packets can land on non GRO-enabled sockets, e.g. due to iptables NAT rules, and that could potentially confuse existing applications.
The solution adopted here is to de-segment the GRO packet before enqueuing as needed. Since we must cope with packet reinsertion after de-segmentation, the relevant code is factored-out in ipv4 and ipv6 specific helpers and exposed to UDP usage.
While the current code can probably be improved, this safeguard ,implemented in the patches 4-7, allows future enachements to enable UDP GSO offload on more virtual devices eventually even on forwarded packets.
Quoted 6 lines
The last 4 for patches implement some performance and functional self-tests, re-using the existing udpgso infrastructure. The problematic scenario described above is explicitly tested.
This revision of the series try to address the feedback provided by Willem and Subash on previous iteration.
Series applied.
Re: [PATCH net-next 05/10] ipv6: factor out protocol delivery helper
In reply to Paolo AbeniQuoted 15 lines · Paolo Abeni
So that we can re-use it at the UDP level in the next patch
rfc v3 -> v1:
- add the helper declaration into the ipv6 headerSigned-off-by Paolo Abeni <[email protected]>--- include/net/ipv6.h | 2 ++ net/ipv6/ip6_input.c | 28 ++++++++++++++++------------ 2 files changed, 18 insertions(+), 12 deletions(-)include/net/ipv6.h +0
-0
[...]
Quoted 19 lines
include/net/ipv6.h +2
-5@@ -319,28 +319,26 @@ void ipv6_list_rcv(struct list_head *head, struct packet_type *pt,/* * Deliver the packet to the host */ - - -static int ip6_input_finish (struct net *net,struct sock *sk,struct sk_buff *skb) +void ip6_protocol_deliver_rcu (struct net *net,struct sk_buff *skb,int nexthdr, +bool have_final) {const struct inet6_protocol *ipprot;struct inet6_dev *idev;unsigned int nhoff; -int nexthdr;bool raw; -bool have_final = false;/* * Parse extension headers
include/net/ipv6.h void ipv6_list_rcv(struct list_head *head, struct packet_type *pt, · line 337
Quoted 10 lines
include/net/ipv6.h +1
-4*/ -rcu_read_lock (); resubmit: idev =ip6_dst_idev (skb_dst (skb)); -if (!pskb_pull (skb,skb_transport_offset (skb))) - goto discard; nhoff =IP6CB (skb)->nhoff; - nexthdr =skb_network_header (skb)[nhoff]; +if (!have_final) {
Haven't you removed this variable above?
include/net/ipv6.h void ipv6_list_rcv(struct list_head *head, struct packet_type *pt, · line 340
Quoted 3 lines
include/net/ipv6.h +3
-0+if (!pskb_pull (skb,skb_transport_offset (skb))) + goto discard; + nexthdr =skb_network_header (skb)[nhoff];
And this?
include/net/ipv6.h void ipv6_list_rcv(struct list_head *head, struct packet_type *pt, · line 344
Quoted 4 lines
include/net/ipv6.h +1
-0+ } resubmit_final: raw =raw6_local_deliver (skb, nexthdr);
[...]
MBR, Sergei
Paolo Abeni
maintainerRe: [PATCH net-next 05/10] ipv6: factor out protocol delivery helper
Hi,
Quoted 51 lines · Sergei Shtylyov
Quoted 15 lines · Paolo Abeni
So that we can re-use it at the UDP level in the next patch
rfc v3 -> v1:
- add the helper declaration into the ipv6 headerSigned-off-by Paolo Abeni <[email protected]>--- include/net/ipv6.h | 2 ++ net/ipv6/ip6_input.c | 28 ++++++++++++++++------------ 2 files changed, 18 insertions(+), 12 deletions(-)include/net/ipv6.h +0
-0[...]
Quoted 19 lines
include/net/ipv6.h +2
-5@@ -319,28 +319,26 @@ void ipv6_list_rcv(struct list_head *head, struct packet_type *pt,/* * Deliver the packet to the host */ - - -static int ip6_input_finish (struct net *net,struct sock *sk,struct sk_buff *skb) +void ip6_protocol_deliver_rcu (struct net *net,struct sk_buff *skb,int nexthdr, +bool have_final) {const struct inet6_protocol *ipprot;struct inet6_dev *idev;unsigned int nhoff; -int nexthdr;bool raw; -bool have_final = false;/* * Parse extension headers include/net/ipv6.h void ipv6_list_rcv(struct list_head *head, struct packet_type *pt, · line 337
Quoted 10 lines
include/net/ipv6.h +1
-4*/ -rcu_read_lock (); resubmit: idev =ip6_dst_idev (skb_dst (skb)); -if (!pskb_pull (skb,skb_transport_offset (skb))) - goto discard; nhoff =IP6CB (skb)->nhoff; - nexthdr =skb_network_header (skb)[nhoff]; +if (!have_final) {Haven't you removed this variable above?
Quoted 5 lines
include/net/ipv6.h void ipv6_list_rcv(struct list_head *head, struct packet_type *pt, · line 340
Quoted 3 lines
include/net/ipv6.h +3
-0+if (!pskb_pull (skb,skb_transport_offset (skb))) + goto discard; + nexthdr =skb_network_header (skb)[nhoff];And this?
Thanks for reviewing.
Both local variables are now function arguments (see the function signature, far above in this chunk).
Cheers,
Paolo
Re: [PATCH net-next 05/10] ipv6: factor out protocol delivery helper
Quoted 55 lines · Paolo Abeni
Quoted 49 lines
Quoted 15 lines
So that we can re-use it at the UDP level in the next patch
rfc v3 -> v1:
- add the helper declaration into the ipv6 headerSigned-off-by Paolo Abeni <[email protected]>--- include/net/ipv6.h | 2 ++ net/ipv6/ip6_input.c | 28 ++++++++++++++++------------ 2 files changed, 18 insertions(+), 12 deletions(-)include/net/ipv6.h +0
-0[...]
Quoted 19 lines
include/net/ipv6.h +2
-5@@ -319,28 +319,26 @@ void ipv6_list_rcv(struct list_head *head, struct packet_type *pt,/* * Deliver the packet to the host */ - - -static int ip6_input_finish (struct net *net,struct sock *sk,struct sk_buff *skb) +void ip6_protocol_deliver_rcu (struct net *net,struct sk_buff *skb,int nexthdr, +bool have_final) {const struct inet6_protocol *ipprot;struct inet6_dev *idev;unsigned int nhoff; -int nexthdr;bool raw; -bool have_final = false;/* * Parse extension headers include/net/ipv6.h void ipv6_list_rcv(struct list_head *head, struct packet_type *pt, · line 337
Quoted 10 lines
include/net/ipv6.h +1
-4*/ -rcu_read_lock (); resubmit: idev =ip6_dst_idev (skb_dst (skb)); -if (!pskb_pull (skb,skb_transport_offset (skb))) - goto discard; nhoff =IP6CB (skb)->nhoff; - nexthdr =skb_network_header (skb)[nhoff]; +if (!have_final) {Haven't you removed this variable above?
Quoted 5 lines
include/net/ipv6.h void ipv6_list_rcv(struct list_head *head, struct packet_type *pt, · line 340
Quoted 3 lines
include/net/ipv6.h +3
-0+if (!pskb_pull (skb,skb_transport_offset (skb))) + goto discard; + nexthdr =skb_network_header (skb)[nhoff];And this?
Quoted 4 lines
Thanks for reviewing.
Both local variables are now function arguments (see the function signature, far above in this chunk).
Oops, my bad... I'm sorry. :-)
Quoted 3 lines
Cheers,
Paolo
MBR, Sergei
Re: [PATCH net-next 01/10] udp: implement complete book-keeping for encap_needed
In reply to Paolo AbeniQuoted 116 lines · Paolo Abeni
The
*encap_neededstatic keys are enabled by UDP tunnels and several UDP encapsulations type, but they are never turned off. This can cause unneeded overall performance degradation for systems where such features are used transiently.This patch introduces complete book-keeping for such keys, decreasing the usage at socket destruction time, if needed, and avoiding that the same socket could increase the key usage multiple times.
rfc v3 -> v1:
- add socket lock aroundudp_tunnel_encap_enable()rfc v2 -> rfc v3:
- useudp_tunnel_encap_enable()insetsockopt()Signed-off-by Paolo Abeni <[email protected]>--- include/linux/udp.h | 7 ++++++- include/net/udp_tunnel.h | 6 ++++++ net/ipv4/udp.c | 19 +++++++++++++------ net/ipv6/udp.c | 14 +++++++++----- 4 files changed, 34 insertions(+), 12 deletions(-)include/linux/udp.h +6
-1@@ -49,7 +49,12 @@ struct udp_sock {unsigned int corkflag;/* Cork is required */ __u8 encap_type;/* Is this an Encapsulation socket? */ unsigned char no_check6_tx:1,/* Send zero UDP6 checksums on TX? */ - no_check6_rx:1;/* Allow zero UDP6 checksums on RX? */ + no_check6_rx:1,/* Allow zero UDP6 checksums on RX? */ + encap_enabled:1; /* This socket enabled encap +* processing; UDP tunnels and +* different encapsulation layer set +* this +*/ /* * Following member retains the information to create a UDP header * when the socket is uncorked. include/net/udp_tunnel.h +6
-0@@ -165,6 +165,12 @@ static inline int udp_tunnel_handle_offloads(struct sk_buff *skb, bool udp_csum)static inline void udp_tunnel_encap_enable (struct socket *sock) { +struct udp_sock *up =udp_sk (sock->sk); + +if (up->encap_enabled) +return ; + + up->encap_enabled =1 ;#if IS_ENABLED (CONFIG_IPV6 )if (sock->sk->sk_family ==PF_INET6 ) ipv6_stub->udpv6_encap_enable ();net/ipv4/udp.c +13
-6@@ -115,6 +115,7 @@#include "udp_impl.h" #include <net/sock_reuseport.h> #include <net/addrconf.h> +#include <net/udp_tunnel.h> struct udp_table udp_table__read_mostly ;EXPORT_SYMBOL (udp_table);@@ -2398,11 +2399,15 @@ void udp_destroy_sock(struct sock *sk)bool slow =lock_sock_fast (sk);udp_flush_pending_frames (sk);unlock_sock_fast (sk, slow); -if (static_branch_unlikely (&udp_encap_needed_key) && up->encap_type) { -void (*encap_destroy)(struct sock *sk); - encap_destroy =READ_ONCE (up->encap_destroy); -if (encap_destroy) -encap_destroy (sk); +if (static_branch_unlikely (&udp_encap_needed_key)) { +if (up->encap_type) { +void (*encap_destroy)(struct sock *sk); + encap_destroy =READ_ONCE (up->encap_destroy); +if (encap_destroy) +encap_destroy (sk); + } +if (up->encap_enabled) +static_branch_disable (&udp_encap_needed_key); } }@@ -2447,7 +2452,9 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,/* FALLTHROUGH */ case UDP_ENCAP_L2TPINUDP : up->encap_type = val; -udp_encap_enable (); +lock_sock (sk); +udp_tunnel_encap_enable (sk->sk_socket); +release_sock (sk);break ;default : err = -ENOPROTOOPT ;net/ipv6/udp.c +0
-4@@ -1458,11 +1458,15 @@ void udpv6_destroy_sock(struct sock *sk)udp_v6_flush_pending_frames (sk);release_sock (sk); -if (static_branch_unlikely (&udpv6_encap_needed_key) && up->encap_type) { -void (*encap_destroy)(struct sock *sk); - encap_destroy =READ_ONCE (up->encap_destroy); -if (encap_destroy)
net/ipv6/udp.c void udpv6_destroy_sock(struct sock *sk) · line 1469
Quoted 10 lines
net/ipv6/udp.c +9
-1-encap_destroy (sk); +if (static_branch_unlikely (&udpv6_encap_needed_key)) { +if (up->encap_type) { +void (*encap_destroy)(struct sock *sk); + encap_destroy =READ_ONCE (up->encap_destroy); +if (encap_destroy) +encap_destroy (sk); + } +if (up->encap_enabled) +static_branch_disable (&udpv6_encap_needed_key);
Where is the opposite operation done ?
syzbot reported :
WARNING: CPU: 0 PID: 7564 at kernel/jump_label.c:188 static_key_disable_cpuslocked+0x2bb/0x310 kernel/jump_label.c:188 Kernel panic - not syncing: panic_on_warn set ... CPU: 0 PID: 7564 Comm: syz-executor2 Not tainted 4.20.0-rc1-next-20181109+ #110 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 Call Trace: __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0x244/0x39d lib/dump_stack.c:113 panic+0x2ad/0x55c kernel/panic.c:188 __warn.cold.8+0x20/0x45 kernel/panic.c:540 report_bug+0x254/0x2d0 lib/bug.c:186 fixup_bug arch/x86/kernel/traps.c:178 [inline] do_error_trap+0x11b/0x200 arch/x86/kernel/traps.c:271 do_invalid_op+0x36/0x40 arch/x86/kernel/traps.c:290 invalid_op+0x14/0x20 arch/x86/entry/entry_64.S:969 RIP: 0010:static_key_disable_cpuslocked+0x2bb/0x310 kernel/jump_label.c:188 Code: ff eb db e8 07 da e3 ff 48 89 da 48 c7 c6 00 bf 31 88 48 c7 c7 e0 bb 31 88 e8 71 0d ad ff 0f 0b e9 0e fe ff ff e8 e5 d9 e3 ff <0f> 0b e9 2b ff ff ff 4c 89 f7 e8 56 32 27 00 e9 5e fe ff ff 48 89 RSP: 0018:ffff8801ba2b7928 EFLAGS: 00010293 RAX: ffff8801bb558340 RBX: 00000000ffffffff RCX: ffffffff819bcdf5 RDX: 0000000000000000 RSI: ffffffff819bcecb RDI: 0000000000000005 RBP: ffff8801ba2b79f0 R08: ffff8801bb558340 R09: fffffbfff17885c4 R10: fffffbfff17885c4 R11: ffffffff8bc42e23 R12: 1ffff10037456f25 R13: 1ffff10037456f31 R14: dffffc0000000000 R15: ffff8801ba2b79c8 static_key_disable+0x1a/0x30 kernel/jump_label.c:202 udpv6_destroy_sock+0x1e3/0x220 net/ipv6/udp.c:1492 sk_common_release+0x6d/0x320 net/core/sock.c:3001 udp_lib_close+0x15/0x20 include/net/udp.h:206 inet_release+0x104/0x1f0 net/ipv4/af_inet.c:428 inet6_release+0x50/0x70 net/ipv6/af_inet6.c:458 __sock_release+0xd7/0x250 net/socket.c:580 sock_close+0x19/0x20 net/socket.c:1142 __fput+0x3bc/0xa70 fs/file_table.c:279 ____fput+0x15/0x20 fs/file_table.c:312 task_work_run+0x1e8/0x2a0 kernel/task_work.c:113 tracehook_notify_resume include/linux/tracehook.h:188 [inline] exit_to_usermode_loop+0x318/0x380 arch/x86/entry/common.c:166 prepare_exit_to_usermode arch/x86/entry/common.c:197 [inline] syscall_return_slowpath arch/x86/entry/common.c:268 [inline] do_syscall_64+0x6be/0x820 arch/x86/entry/common.c:293 entry_SYSCALL_64_after_hwframe+0x49/0xbe RIP: 0033:0x411021 Code: 75 14 b8 03 00 00 00 0f 05 48 3d 01 f0 ff ff 0f 83 34 19 00 00 c3 48 83 ec 08 e8 0a fc ff ff 48 89 04 24 b8 03 00 00 00 0f 05 <48> 8b 3c 24 48 89 c2 e8 53 fc ff ff 48 89 d0 48 83 c4 08 48 3d 01 RSP: 002b:00007ffeb03ec0a0 EFLAGS: 00000293 ORIG_RAX: 0000000000000003 RAX: 0000000000000000 RBX: 0000000000000004 RCX: 0000000000411021 RDX: 0000000000000001 RSI: 0000000000731ce8 RDI: 0000000000000003 RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000 R10: 00007ffeb03ebfd0 R11: 0000000000000293 R12: 0000000000000000 R13: 0000000000000001 R14: 0000000000000000 R15: 0000000000000002
Kernel Offset: disabled
Rebooting in 86400 seconds..