udp: intoduce udp_encap_needed static_key

Most machines dont use UDP encapsulation (L2TP)

Adds a static_key so that udp_queue_rcv_skb() doesnt have to perform a
test if L2TP never setup the encap_rcv on a socket.

Idea of this patch came after Simon Horman proposal to add a hook on TCP
as well.

If static_key is not yet enabled, the fast path does a single JMP .

When static_key is enabled, JMP destination is patched to reach the real
encap_type/encap_rcv logic, possibly adding cache misses.

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Simon Horman <horms@verge.net.au>
Cc: dev@openvswitch.org
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Eric Dumazet 2012-04-11 23:05:28 +00:00 committed by David S. Miller
parent 77577bf932
commit 447167bf56
3 changed files with 13 additions and 1 deletions

View File

@ -267,4 +267,5 @@ extern void udp_init(void);
extern int udp4_ufo_send_check(struct sk_buff *skb); extern int udp4_ufo_send_check(struct sk_buff *skb);
extern struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, extern struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
netdev_features_t features); netdev_features_t features);
extern void udp_encap_enable(void);
#endif /* _UDP_H */ #endif /* _UDP_H */

View File

@ -107,6 +107,7 @@
#include <net/checksum.h> #include <net/checksum.h>
#include <net/xfrm.h> #include <net/xfrm.h>
#include <trace/events/udp.h> #include <trace/events/udp.h>
#include <linux/static_key.h>
#include "udp_impl.h" #include "udp_impl.h"
struct udp_table udp_table __read_mostly; struct udp_table udp_table __read_mostly;
@ -1379,6 +1380,14 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
} }
static struct static_key udp_encap_needed __read_mostly;
void udp_encap_enable(void)
{
if (!static_key_enabled(&udp_encap_needed))
static_key_slow_inc(&udp_encap_needed);
}
EXPORT_SYMBOL(udp_encap_enable);
/* returns: /* returns:
* -1: error * -1: error
* 0: success * 0: success
@ -1400,7 +1409,7 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
goto drop; goto drop;
nf_reset(skb); nf_reset(skb);
if (up->encap_type) { if (static_key_false(&udp_encap_needed) && up->encap_type) {
int (*encap_rcv)(struct sock *sk, struct sk_buff *skb); int (*encap_rcv)(struct sock *sk, struct sk_buff *skb);
/* /*
@ -1760,6 +1769,7 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
/* FALLTHROUGH */ /* FALLTHROUGH */
case UDP_ENCAP_L2TPINUDP: case UDP_ENCAP_L2TPINUDP:
up->encap_type = val; up->encap_type = val;
udp_encap_enable();
break; break;
default: default:
err = -ENOPROTOOPT; err = -ENOPROTOOPT;

View File

@ -1424,6 +1424,7 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
/* Mark socket as an encapsulation socket. See net/ipv4/udp.c */ /* Mark socket as an encapsulation socket. See net/ipv4/udp.c */
udp_sk(sk)->encap_type = UDP_ENCAP_L2TPINUDP; udp_sk(sk)->encap_type = UDP_ENCAP_L2TPINUDP;
udp_sk(sk)->encap_rcv = l2tp_udp_encap_recv; udp_sk(sk)->encap_rcv = l2tp_udp_encap_recv;
udp_encap_enable();
} }
sk->sk_user_data = tunnel; sk->sk_user_data = tunnel;