From 1ec4c359dec1edfc1e97f94be29e6bff3934ac4d Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Wed, 7 Nov 2018 12:38:28 +0100 Subject: [PATCH] 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() Change-Id: Ie2faefd68ef38dd40494046b9db481044b7c5b3a CRs-Fixed: 2351793 Signed-off-by: Paolo Abeni Signed-off-by: David S. Miller Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git Git-commit: 60fb9567bf30937e6bedfa939d7c8fd4ee6a1b1c Signed-off-by: Sean Tranchetti --- 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(-) diff --git a/include/linux/udp.h b/include/linux/udp.h index ca840345571b..4f9fb6bcea49 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -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 layers set + * this + */ /* * Following member retains the information to create a UDP header * when the socket is uncorked. diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h index b95a6927c718..dddfa7a32b61 100644 --- a/include/net/udp_tunnel.h +++ b/include/net/udp_tunnel.h @@ -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(); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 1980c2ca66ae..55dfc75ad253 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -115,6 +115,7 @@ #include "udp_impl.h" #include #include +#include struct udp_table udp_table __read_mostly; EXPORT_SYMBOL(udp_table); @@ -2386,11 +2387,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_key_false(&udp_encap_needed) && up->encap_type) { - void (*encap_destroy)(struct sock *sk); - encap_destroy = ACCESS_ONCE(up->encap_destroy); - if (encap_destroy) - encap_destroy(sk); + if (static_key_false(&udp_encap_needed)) { + if (up->encap_type) { + void (*encap_destroy)(struct sock *sk); + encap_destroy = ACCESS_ONCE(up->encap_destroy); + if (encap_destroy) + encap_destroy(sk); + } + if (up->encap_enabled) + static_key_disable(&udp_encap_needed); } } @@ -2435,7 +2440,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; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 543e557b468a..22682cde24f4 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1460,11 +1460,15 @@ void udpv6_destroy_sock(struct sock *sk) udp_v6_flush_pending_frames(sk); release_sock(sk); - if (static_key_false(&udpv6_encap_needed) && up->encap_type) { - void (*encap_destroy)(struct sock *sk); - encap_destroy = ACCESS_ONCE(up->encap_destroy); - if (encap_destroy) - encap_destroy(sk); + if (static_key_false(&udpv6_encap_needed)) { + if (up->encap_type) { + void (*encap_destroy)(struct sock *sk); + encap_destroy = ACCESS_ONCE(up->encap_destroy); + if (encap_destroy) + encap_destroy(sk); + } + if (up->encap_enabled) + static_key_disable(&udpv6_encap_needed); } inet6_destroy_sock(sk);