diff options
author | Tom Herbert <tom@herbertland.com> | 2016-04-05 11:22:51 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-04-07 16:53:29 -0400 |
commit | a6024562ffd7e0f31bc6671817840ad1e91de7b4 (patch) | |
tree | 6d5ea1150eb18e8a3b0626913034b162cdf9d74d | |
parent | 63058308cd55182bbfd7a87970bd57883fcfbd2e (diff) |
udp: Add GRO functions to UDP socket
This patch adds GRO functions (gro_receive and gro_complete) to UDP
sockets. udp_gro_receive is changed to perform socket lookup on a
packet. If a socket is found the related GRO functions are called.
This features obsoletes using UDP offload infrastructure for GRO
(udp_offload). This has the advantage of not being limited to provide
offload on a per port basis, GRO is now applied to whatever individual
UDP sockets are bound to. This also allows the possbility of
"application defined GRO"-- that is we can attach something like
a BPF program to a UDP socket to perfrom GRO on an application
layer protocol.
Signed-off-by: Tom Herbert <tom@herbertland.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/udp.h | 8 | ||||
-rw-r--r-- | include/net/udp.h | 7 | ||||
-rw-r--r-- | net/ipv4/udp_offload.c | 52 | ||||
-rw-r--r-- | net/ipv6/Makefile | 5 | ||||
-rw-r--r-- | net/ipv6/af_inet6.c | 8 | ||||
-rw-r--r-- | net/ipv6/ip6_offload.c | 2 | ||||
-rw-r--r-- | net/ipv6/ip6_offload.h | 3 | ||||
-rw-r--r-- | net/ipv6/udp_offload.c | 11 |
8 files changed, 54 insertions, 42 deletions
diff --git a/include/linux/udp.h b/include/linux/udp.h index 32342754643a..d1fd8cd39478 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h | |||
@@ -71,6 +71,14 @@ struct udp_sock { | |||
71 | */ | 71 | */ |
72 | int (*encap_rcv)(struct sock *sk, struct sk_buff *skb); | 72 | int (*encap_rcv)(struct sock *sk, struct sk_buff *skb); |
73 | void (*encap_destroy)(struct sock *sk); | 73 | void (*encap_destroy)(struct sock *sk); |
74 | |||
75 | /* GRO functions for UDP socket */ | ||
76 | struct sk_buff ** (*gro_receive)(struct sock *sk, | ||
77 | struct sk_buff **head, | ||
78 | struct sk_buff *skb); | ||
79 | int (*gro_complete)(struct sock *sk, | ||
80 | struct sk_buff *skb, | ||
81 | int nhoff); | ||
74 | }; | 82 | }; |
75 | 83 | ||
76 | static inline struct udp_sock *udp_sk(const struct sock *sk) | 84 | static inline struct udp_sock *udp_sk(const struct sock *sk) |
diff --git a/include/net/udp.h b/include/net/udp.h index 3aa0b3ec1fb0..3c5a65e0946d 100644 --- a/include/net/udp.h +++ b/include/net/udp.h | |||
@@ -167,9 +167,12 @@ static inline void udp_csum_pull_header(struct sk_buff *skb) | |||
167 | UDP_SKB_CB(skb)->cscov -= sizeof(struct udphdr); | 167 | UDP_SKB_CB(skb)->cscov -= sizeof(struct udphdr); |
168 | } | 168 | } |
169 | 169 | ||
170 | typedef struct sock *(*udp_lookup_t)(struct sk_buff *skb, __be16 sport, | ||
171 | __be16 dport); | ||
172 | |||
170 | struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb, | 173 | struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb, |
171 | struct udphdr *uh); | 174 | struct udphdr *uh, udp_lookup_t lookup); |
172 | int udp_gro_complete(struct sk_buff *skb, int nhoff); | 175 | int udp_gro_complete(struct sk_buff *skb, int nhoff, udp_lookup_t lookup); |
173 | 176 | ||
174 | static inline struct udphdr *udp_gro_udphdr(struct sk_buff *skb) | 177 | static inline struct udphdr *udp_gro_udphdr(struct sk_buff *skb) |
175 | { | 178 | { |
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index 0ed2dafb7cc4..65c3fd34b363 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c | |||
@@ -179,6 +179,7 @@ out_unlock: | |||
179 | 179 | ||
180 | return segs; | 180 | return segs; |
181 | } | 181 | } |
182 | EXPORT_SYMBOL(skb_udp_tunnel_segment); | ||
182 | 183 | ||
183 | static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, | 184 | static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, |
184 | netdev_features_t features) | 185 | netdev_features_t features) |
@@ -304,13 +305,13 @@ unlock: | |||
304 | EXPORT_SYMBOL(udp_del_offload); | 305 | EXPORT_SYMBOL(udp_del_offload); |
305 | 306 | ||
306 | struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb, | 307 | struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb, |
307 | struct udphdr *uh) | 308 | struct udphdr *uh, udp_lookup_t lookup) |
308 | { | 309 | { |
309 | struct udp_offload_priv *uo_priv; | ||
310 | struct sk_buff *p, **pp = NULL; | 310 | struct sk_buff *p, **pp = NULL; |
311 | struct udphdr *uh2; | 311 | struct udphdr *uh2; |
312 | unsigned int off = skb_gro_offset(skb); | 312 | unsigned int off = skb_gro_offset(skb); |
313 | int flush = 1; | 313 | int flush = 1; |
314 | struct sock *sk; | ||
314 | 315 | ||
315 | if (NAPI_GRO_CB(skb)->encap_mark || | 316 | if (NAPI_GRO_CB(skb)->encap_mark || |
316 | (skb->ip_summed != CHECKSUM_PARTIAL && | 317 | (skb->ip_summed != CHECKSUM_PARTIAL && |
@@ -322,13 +323,11 @@ struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb, | |||
322 | NAPI_GRO_CB(skb)->encap_mark = 1; | 323 | NAPI_GRO_CB(skb)->encap_mark = 1; |
323 | 324 | ||
324 | rcu_read_lock(); | 325 | rcu_read_lock(); |
325 | uo_priv = rcu_dereference(udp_offload_base); | 326 | sk = (*lookup)(skb, uh->source, uh->dest); |
326 | for (; uo_priv != NULL; uo_priv = rcu_dereference(uo_priv->next)) { | 327 | |
327 | if (net_eq(read_pnet(&uo_priv->net), dev_net(skb->dev)) && | 328 | if (sk && udp_sk(sk)->gro_receive) |
328 | uo_priv->offload->port == uh->dest && | 329 | goto unflush; |
329 | uo_priv->offload->callbacks.gro_receive) | 330 | |
330 | goto unflush; | ||
331 | } | ||
332 | goto out_unlock; | 331 | goto out_unlock; |
333 | 332 | ||
334 | unflush: | 333 | unflush: |
@@ -352,9 +351,7 @@ unflush: | |||
352 | 351 | ||
353 | skb_gro_pull(skb, sizeof(struct udphdr)); /* pull encapsulating udp header */ | 352 | skb_gro_pull(skb, sizeof(struct udphdr)); /* pull encapsulating udp header */ |
354 | skb_gro_postpull_rcsum(skb, uh, sizeof(struct udphdr)); | 353 | skb_gro_postpull_rcsum(skb, uh, sizeof(struct udphdr)); |
355 | NAPI_GRO_CB(skb)->proto = uo_priv->offload->ipproto; | 354 | pp = udp_sk(sk)->gro_receive(sk, head, skb); |
356 | pp = uo_priv->offload->callbacks.gro_receive(head, skb, | ||
357 | uo_priv->offload); | ||
358 | 355 | ||
359 | out_unlock: | 356 | out_unlock: |
360 | rcu_read_unlock(); | 357 | rcu_read_unlock(); |
@@ -362,6 +359,7 @@ out: | |||
362 | NAPI_GRO_CB(skb)->flush |= flush; | 359 | NAPI_GRO_CB(skb)->flush |= flush; |
363 | return pp; | 360 | return pp; |
364 | } | 361 | } |
362 | EXPORT_SYMBOL(udp_gro_receive); | ||
365 | 363 | ||
366 | static struct sk_buff **udp4_gro_receive(struct sk_buff **head, | 364 | static struct sk_buff **udp4_gro_receive(struct sk_buff **head, |
367 | struct sk_buff *skb) | 365 | struct sk_buff *skb) |
@@ -383,39 +381,28 @@ static struct sk_buff **udp4_gro_receive(struct sk_buff **head, | |||
383 | inet_gro_compute_pseudo); | 381 | inet_gro_compute_pseudo); |
384 | skip: | 382 | skip: |
385 | NAPI_GRO_CB(skb)->is_ipv6 = 0; | 383 | NAPI_GRO_CB(skb)->is_ipv6 = 0; |
386 | return udp_gro_receive(head, skb, uh); | 384 | return udp_gro_receive(head, skb, uh, udp4_lib_lookup_skb); |
387 | 385 | ||
388 | flush: | 386 | flush: |
389 | NAPI_GRO_CB(skb)->flush = 1; | 387 | NAPI_GRO_CB(skb)->flush = 1; |
390 | return NULL; | 388 | return NULL; |
391 | } | 389 | } |
392 | 390 | ||
393 | int udp_gro_complete(struct sk_buff *skb, int nhoff) | 391 | int udp_gro_complete(struct sk_buff *skb, int nhoff, |
392 | udp_lookup_t lookup) | ||
394 | { | 393 | { |
395 | struct udp_offload_priv *uo_priv; | ||
396 | __be16 newlen = htons(skb->len - nhoff); | 394 | __be16 newlen = htons(skb->len - nhoff); |
397 | struct udphdr *uh = (struct udphdr *)(skb->data + nhoff); | 395 | struct udphdr *uh = (struct udphdr *)(skb->data + nhoff); |
398 | int err = -ENOSYS; | 396 | int err = -ENOSYS; |
397 | struct sock *sk; | ||
399 | 398 | ||
400 | uh->len = newlen; | 399 | uh->len = newlen; |
401 | 400 | ||
402 | rcu_read_lock(); | 401 | rcu_read_lock(); |
403 | 402 | sk = (*lookup)(skb, uh->source, uh->dest); | |
404 | uo_priv = rcu_dereference(udp_offload_base); | 403 | if (sk && udp_sk(sk)->gro_complete) |
405 | for (; uo_priv != NULL; uo_priv = rcu_dereference(uo_priv->next)) { | 404 | err = udp_sk(sk)->gro_complete(sk, skb, |
406 | if (net_eq(read_pnet(&uo_priv->net), dev_net(skb->dev)) && | 405 | nhoff + sizeof(struct udphdr)); |
407 | uo_priv->offload->port == uh->dest && | ||
408 | uo_priv->offload->callbacks.gro_complete) | ||
409 | break; | ||
410 | } | ||
411 | |||
412 | if (uo_priv) { | ||
413 | NAPI_GRO_CB(skb)->proto = uo_priv->offload->ipproto; | ||
414 | err = uo_priv->offload->callbacks.gro_complete(skb, | ||
415 | nhoff + sizeof(struct udphdr), | ||
416 | uo_priv->offload); | ||
417 | } | ||
418 | |||
419 | rcu_read_unlock(); | 406 | rcu_read_unlock(); |
420 | 407 | ||
421 | if (skb->remcsum_offload) | 408 | if (skb->remcsum_offload) |
@@ -426,6 +413,7 @@ int udp_gro_complete(struct sk_buff *skb, int nhoff) | |||
426 | 413 | ||
427 | return err; | 414 | return err; |
428 | } | 415 | } |
416 | EXPORT_SYMBOL(udp_gro_complete); | ||
429 | 417 | ||
430 | static int udp4_gro_complete(struct sk_buff *skb, int nhoff) | 418 | static int udp4_gro_complete(struct sk_buff *skb, int nhoff) |
431 | { | 419 | { |
@@ -440,7 +428,7 @@ static int udp4_gro_complete(struct sk_buff *skb, int nhoff) | |||
440 | skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL; | 428 | skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL; |
441 | } | 429 | } |
442 | 430 | ||
443 | return udp_gro_complete(skb, nhoff); | 431 | return udp_gro_complete(skb, nhoff, udp4_lib_lookup_skb); |
444 | } | 432 | } |
445 | 433 | ||
446 | static const struct net_offload udpv4_offload = { | 434 | static const struct net_offload udpv4_offload = { |
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index 2fbd90bf8d33..5e9d6bf4aaca 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile | |||
@@ -8,9 +8,10 @@ ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \ | |||
8 | addrlabel.o \ | 8 | addrlabel.o \ |
9 | route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \ | 9 | route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \ |
10 | raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \ | 10 | raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \ |
11 | exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o | 11 | exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o \ |
12 | udp_offload.o | ||
12 | 13 | ||
13 | ipv6-offload := ip6_offload.o tcpv6_offload.o udp_offload.o exthdrs_offload.o | 14 | ipv6-offload := ip6_offload.o tcpv6_offload.o exthdrs_offload.o |
14 | 15 | ||
15 | ipv6-$(CONFIG_SYSCTL) = sysctl_net_ipv6.o | 16 | ipv6-$(CONFIG_SYSCTL) = sysctl_net_ipv6.o |
16 | ipv6-$(CONFIG_IPV6_MROUTE) += ip6mr.o | 17 | ipv6-$(CONFIG_IPV6_MROUTE) += ip6mr.o |
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 2b78aad0d52f..bfa86f040c16 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c | |||
@@ -64,6 +64,8 @@ | |||
64 | #include <asm/uaccess.h> | 64 | #include <asm/uaccess.h> |
65 | #include <linux/mroute6.h> | 65 | #include <linux/mroute6.h> |
66 | 66 | ||
67 | #include "ip6_offload.h" | ||
68 | |||
67 | MODULE_AUTHOR("Cast of dozens"); | 69 | MODULE_AUTHOR("Cast of dozens"); |
68 | MODULE_DESCRIPTION("IPv6 protocol stack for Linux"); | 70 | MODULE_DESCRIPTION("IPv6 protocol stack for Linux"); |
69 | MODULE_LICENSE("GPL"); | 71 | MODULE_LICENSE("GPL"); |
@@ -959,6 +961,10 @@ static int __init inet6_init(void) | |||
959 | if (err) | 961 | if (err) |
960 | goto udplitev6_fail; | 962 | goto udplitev6_fail; |
961 | 963 | ||
964 | err = udpv6_offload_init(); | ||
965 | if (err) | ||
966 | goto udpv6_offload_fail; | ||
967 | |||
962 | err = tcpv6_init(); | 968 | err = tcpv6_init(); |
963 | if (err) | 969 | if (err) |
964 | goto tcpv6_fail; | 970 | goto tcpv6_fail; |
@@ -988,6 +994,8 @@ pingv6_fail: | |||
988 | ipv6_packet_fail: | 994 | ipv6_packet_fail: |
989 | tcpv6_exit(); | 995 | tcpv6_exit(); |
990 | tcpv6_fail: | 996 | tcpv6_fail: |
997 | udpv6_offload_exit(); | ||
998 | udpv6_offload_fail: | ||
991 | udplitev6_exit(); | 999 | udplitev6_exit(); |
992 | udplitev6_fail: | 1000 | udplitev6_fail: |
993 | udpv6_exit(); | 1001 | udpv6_exit(); |
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index 82e9f3076028..204af2219471 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c | |||
@@ -325,8 +325,6 @@ static int __init ipv6_offload_init(void) | |||
325 | 325 | ||
326 | if (tcpv6_offload_init() < 0) | 326 | if (tcpv6_offload_init() < 0) |
327 | pr_crit("%s: Cannot add TCP protocol offload\n", __func__); | 327 | pr_crit("%s: Cannot add TCP protocol offload\n", __func__); |
328 | if (udp_offload_init() < 0) | ||
329 | pr_crit("%s: Cannot add UDP protocol offload\n", __func__); | ||
330 | if (ipv6_exthdrs_offload_init() < 0) | 328 | if (ipv6_exthdrs_offload_init() < 0) |
331 | pr_crit("%s: Cannot add EXTHDRS protocol offload\n", __func__); | 329 | pr_crit("%s: Cannot add EXTHDRS protocol offload\n", __func__); |
332 | 330 | ||
diff --git a/net/ipv6/ip6_offload.h b/net/ipv6/ip6_offload.h index 2e155c651b35..96b40e41ac53 100644 --- a/net/ipv6/ip6_offload.h +++ b/net/ipv6/ip6_offload.h | |||
@@ -12,7 +12,8 @@ | |||
12 | #define __ip6_offload_h | 12 | #define __ip6_offload_h |
13 | 13 | ||
14 | int ipv6_exthdrs_offload_init(void); | 14 | int ipv6_exthdrs_offload_init(void); |
15 | int udp_offload_init(void); | 15 | int udpv6_offload_init(void); |
16 | int udpv6_offload_exit(void); | ||
16 | int tcpv6_offload_init(void); | 17 | int tcpv6_offload_init(void); |
17 | 18 | ||
18 | #endif | 19 | #endif |
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c index 2b0fbe6929e8..5429f6bcf047 100644 --- a/net/ipv6/udp_offload.c +++ b/net/ipv6/udp_offload.c | |||
@@ -153,7 +153,7 @@ static struct sk_buff **udp6_gro_receive(struct sk_buff **head, | |||
153 | 153 | ||
154 | skip: | 154 | skip: |
155 | NAPI_GRO_CB(skb)->is_ipv6 = 1; | 155 | NAPI_GRO_CB(skb)->is_ipv6 = 1; |
156 | return udp_gro_receive(head, skb, uh); | 156 | return udp_gro_receive(head, skb, uh, udp6_lib_lookup_skb); |
157 | 157 | ||
158 | flush: | 158 | flush: |
159 | NAPI_GRO_CB(skb)->flush = 1; | 159 | NAPI_GRO_CB(skb)->flush = 1; |
@@ -173,7 +173,7 @@ static int udp6_gro_complete(struct sk_buff *skb, int nhoff) | |||
173 | skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL; | 173 | skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL; |
174 | } | 174 | } |
175 | 175 | ||
176 | return udp_gro_complete(skb, nhoff); | 176 | return udp_gro_complete(skb, nhoff, udp6_lib_lookup_skb); |
177 | } | 177 | } |
178 | 178 | ||
179 | static const struct net_offload udpv6_offload = { | 179 | static const struct net_offload udpv6_offload = { |
@@ -184,7 +184,12 @@ static const struct net_offload udpv6_offload = { | |||
184 | }, | 184 | }, |
185 | }; | 185 | }; |
186 | 186 | ||
187 | int __init udp_offload_init(void) | 187 | int udpv6_offload_init(void) |
188 | { | 188 | { |
189 | return inet6_add_offload(&udpv6_offload, IPPROTO_UDP); | 189 | return inet6_add_offload(&udpv6_offload, IPPROTO_UDP); |
190 | } | 190 | } |
191 | |||
192 | int udpv6_offload_exit(void) | ||
193 | { | ||
194 | return inet6_del_offload(&udpv6_offload, IPPROTO_UDP); | ||
195 | } | ||