Skip to content

Commit 2a2de41

Browse files
Paolo AbeniNipaLocal
Paolo Abeni
authored and
NipaLocal
committed
udp_tunnel: create a fastpath GRO lookup.
Most UDP tunnels bind a socket to a local port, with ANY address, no peer and no interface index specified. Additionally it's quite common to have a single tunnel device per namespace. Track in each namespace the UDP tunnel socket respecting the above. When only a single one is present, store a reference in the netns. When such reference is not NULL, UDP tunnel GRO lookup just need to match the incoming packet destination port vs the socket local port. The tunnel socket never sets the reuse[port] flag[s]. When bound to no address and interface, no other socket can exist in the same netns matching the specified local port. Matching packets with non-local destination addresses will be aggregated, and eventually segmented as needed - no behavior changes intended. Restrict the optimization to kernel sockets only: it covers all the relevant use-cases, and user-space owned sockets could be disconnected and rebound after setup_udp_tunnel_sock(), breaking the uniqueness assumption Note that the UDP tunnel socket reference is stored into struct netns_ipv4 for both IPv4 and IPv6 tunnels. That is intentional to keep all the fastpath-related netns fields in the same struct and allow cacheline-based optimization. Currently both the IPv4 and IPv6 socket pointer share the same cacheline as the `udp_table` field. Signed-off-by: Paolo Abeni <[email protected]> Reviewed-by: Willem de Bruijn <[email protected]> Signed-off-by: NipaLocal <nipa@local>
1 parent 8fd5ee4 commit 2a2de41

File tree

9 files changed

+109
-1
lines changed

9 files changed

+109
-1
lines changed

include/linux/udp.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,13 @@ struct udp_sock {
101101

102102
/* Cache friendly copy of sk->sk_peek_off >= 0 */
103103
bool peeking_with_offset;
104+
105+
/*
106+
* Accounting for the tunnel GRO fastpath.
107+
* Unprotected by compilers guard, as it uses space available in
108+
* the last UDP socket cacheline.
109+
*/
110+
struct hlist_node tunnel_list;
104111
};
105112

106113
#define udp_test_bit(nr, sk) \
@@ -219,4 +226,13 @@ static inline void udp_allow_gso(struct sock *sk)
219226

220227
#define IS_UDPLITE(__sk) (__sk->sk_protocol == IPPROTO_UDPLITE)
221228

229+
static inline struct sock *udp_tunnel_sk(const struct net *net, bool is_ipv6)
230+
{
231+
#if IS_ENABLED(CONFIG_NET_UDP_TUNNEL)
232+
return rcu_dereference(net->ipv4.udp_tunnel_gro[is_ipv6].sk);
233+
#else
234+
return NULL;
235+
#endif
236+
}
237+
222238
#endif /* _LINUX_UDP_H */

include/net/netns/ipv4.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ struct sysctl_fib_multipath_hash_seed {
4747
};
4848
#endif
4949

50+
struct udp_tunnel_gro {
51+
struct sock __rcu *sk;
52+
struct hlist_head list;
53+
};
54+
5055
struct netns_ipv4 {
5156
/* Cacheline organization can be found documented in
5257
* Documentation/networking/net_cachelines/netns_ipv4_sysctl.rst.
@@ -85,6 +90,11 @@ struct netns_ipv4 {
8590
struct inet_timewait_death_row tcp_death_row;
8691
struct udp_table *udp_table;
8792

93+
#if IS_ENABLED(CONFIG_NET_UDP_TUNNEL)
94+
/* Not in a pernet subsys because need to be available at GRO stage */
95+
struct udp_tunnel_gro udp_tunnel_gro[2];
96+
#endif
97+
8898
#ifdef CONFIG_SYSCTL
8999
struct ctl_table_header *forw_hdr;
90100
struct ctl_table_header *frags_hdr;
@@ -277,4 +287,5 @@ struct netns_ipv4 {
277287
struct hlist_head *inet_addr_lst;
278288
struct delayed_work addr_chk_work;
279289
};
290+
280291
#endif

include/net/udp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ static inline void udp_lib_init_sock(struct sock *sk)
290290
struct udp_sock *up = udp_sk(sk);
291291

292292
skb_queue_head_init(&up->reader_queue);
293+
INIT_HLIST_NODE(&up->tunnel_list);
293294
up->forward_threshold = sk->sk_rcvbuf >> 2;
294295
set_bit(SOCK_CUSTOM_SOCKOPT, &sk->sk_socket->flags);
295296
}

include/net/udp_tunnel.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,18 @@ static inline int udp_tunnel_handle_offloads(struct sk_buff *skb, bool udp_csum)
191191
}
192192
#endif
193193

194+
#if IS_ENABLED(CONFIG_NET_UDP_TUNNEL)
195+
void udp_tunnel_update_gro_lookup(struct net *net, struct sock *sk, bool add);
196+
#else
197+
static inline void udp_tunnel_update_gro_lookup(struct net *net,
198+
struct sock *sk, bool add) {}
199+
#endif
200+
201+
static inline void udp_tunnel_cleanup_gro(struct sock *sk)
202+
{
203+
udp_tunnel_update_gro_lookup(sock_net(sk), sk, false);
204+
}
205+
194206
static inline void udp_tunnel_encap_enable(struct sock *sk)
195207
{
196208
if (udp_test_and_set_bit(ENCAP_ENABLED, sk))

net/ipv4/udp.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2897,8 +2897,10 @@ void udp_destroy_sock(struct sock *sk)
28972897
if (encap_destroy)
28982898
encap_destroy(sk);
28992899
}
2900-
if (udp_test_bit(ENCAP_ENABLED, sk))
2900+
if (udp_test_bit(ENCAP_ENABLED, sk)) {
29012901
static_branch_dec(&udp_encap_needed_key);
2902+
udp_tunnel_cleanup_gro(sk);
2903+
}
29022904
}
29032905
}
29042906

@@ -3810,6 +3812,15 @@ static void __net_init udp_set_table(struct net *net)
38103812

38113813
static int __net_init udp_pernet_init(struct net *net)
38123814
{
3815+
#if IS_ENABLED(CONFIG_NET_UDP_TUNNEL)
3816+
int i;
3817+
3818+
/* No tunnel is configured */
3819+
for (i = 0; i < ARRAY_SIZE(net->ipv4.udp_tunnel_gro); ++i) {
3820+
INIT_HLIST_HEAD(&net->ipv4.udp_tunnel_gro[i].list);
3821+
RCU_INIT_POINTER(net->ipv4.udp_tunnel_gro[i].sk, NULL);
3822+
}
3823+
#endif
38133824
udp_sysctl_init(net);
38143825
udp_set_table(net);
38153826

net/ipv4/udp_offload.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,38 @@
1212
#include <net/udp.h>
1313
#include <net/protocol.h>
1414
#include <net/inet_common.h>
15+
#include <net/udp_tunnel.h>
16+
17+
#if IS_ENABLED(CONFIG_NET_UDP_TUNNEL)
18+
static DEFINE_SPINLOCK(udp_tunnel_gro_lock);
19+
20+
void udp_tunnel_update_gro_lookup(struct net *net, struct sock *sk, bool add)
21+
{
22+
bool is_ipv6 = sk->sk_family == AF_INET6;
23+
struct udp_sock *tup, *up = udp_sk(sk);
24+
struct udp_tunnel_gro *udp_tunnel_gro;
25+
26+
spin_lock(&udp_tunnel_gro_lock);
27+
udp_tunnel_gro = &net->ipv4.udp_tunnel_gro[is_ipv6];
28+
if (add)
29+
hlist_add_head(&up->tunnel_list, &udp_tunnel_gro->list);
30+
else if (up->tunnel_list.pprev)
31+
hlist_del_init(&up->tunnel_list);
32+
33+
if (udp_tunnel_gro->list.first &&
34+
!udp_tunnel_gro->list.first->next) {
35+
tup = hlist_entry(udp_tunnel_gro->list.first, struct udp_sock,
36+
tunnel_list);
37+
38+
rcu_assign_pointer(udp_tunnel_gro->sk, (struct sock *)tup);
39+
} else {
40+
RCU_INIT_POINTER(udp_tunnel_gro->sk, NULL);
41+
}
42+
43+
spin_unlock(&udp_tunnel_gro_lock);
44+
}
45+
EXPORT_SYMBOL_GPL(udp_tunnel_update_gro_lookup);
46+
#endif
1547

1648
static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
1749
netdev_features_t features,
@@ -635,8 +667,13 @@ static struct sock *udp4_gro_lookup_skb(struct sk_buff *skb, __be16 sport,
635667
{
636668
const struct iphdr *iph = skb_gro_network_header(skb);
637669
struct net *net = dev_net_rcu(skb->dev);
670+
struct sock *sk;
638671
int iif, sdif;
639672

673+
sk = udp_tunnel_sk(net, false);
674+
if (sk && dport == htons(sk->sk_num))
675+
return sk;
676+
640677
inet_get_iif_sdif(skb, &iif, &sdif);
641678

642679
return __udp4_lib_lookup(net, iph->saddr, sport,

net/ipv4/udp_tunnel_core.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ int udp_sock_create4(struct net *net, struct udp_port_cfg *cfg,
5858
}
5959
EXPORT_SYMBOL(udp_sock_create4);
6060

61+
static bool sk_saddr_any(struct sock *sk)
62+
{
63+
#if IS_ENABLED(CONFIG_IPV6)
64+
return ipv6_addr_any(&sk->sk_v6_rcv_saddr);
65+
#else
66+
return !sk->sk_rcv_saddr;
67+
#endif
68+
}
69+
6170
void setup_udp_tunnel_sock(struct net *net, struct socket *sock,
6271
struct udp_tunnel_sock_cfg *cfg)
6372
{
@@ -80,6 +89,10 @@ void setup_udp_tunnel_sock(struct net *net, struct socket *sock,
8089
udp_sk(sk)->gro_complete = cfg->gro_complete;
8190

8291
udp_tunnel_encap_enable(sk);
92+
93+
if (!sk->sk_dport && !sk->sk_bound_dev_if && sk_saddr_any(sk) &&
94+
sk->sk_kern_sock)
95+
udp_tunnel_update_gro_lookup(net, sk, true);
8396
}
8497
EXPORT_SYMBOL_GPL(setup_udp_tunnel_sock);
8598

net/ipv6/udp.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include <net/tcp_states.h>
4747
#include <net/ip6_checksum.h>
4848
#include <net/ip6_tunnel.h>
49+
#include <net/udp_tunnel.h>
4950
#include <net/xfrm.h>
5051
#include <net/inet_hashtables.h>
5152
#include <net/inet6_hashtables.h>
@@ -1825,6 +1826,7 @@ void udpv6_destroy_sock(struct sock *sk)
18251826
if (udp_test_bit(ENCAP_ENABLED, sk)) {
18261827
static_branch_dec(&udpv6_encap_needed_key);
18271828
udp_encap_disable();
1829+
udp_tunnel_cleanup_gro(sk);
18281830
}
18291831
}
18301832
}

net/ipv6/udp_offload.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,13 @@ static struct sock *udp6_gro_lookup_skb(struct sk_buff *skb, __be16 sport,
118118
{
119119
const struct ipv6hdr *iph = skb_gro_network_header(skb);
120120
struct net *net = dev_net_rcu(skb->dev);
121+
struct sock *sk;
121122
int iif, sdif;
122123

124+
sk = udp_tunnel_sk(net, true);
125+
if (sk && dport == htons(sk->sk_num))
126+
return sk;
127+
123128
inet6_get_iif_sdif(skb, &iif, &sdif);
124129

125130
return __udp6_lib_lookup(net, &iph->saddr, sport,

0 commit comments

Comments
 (0)