Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Tom Herbert and committed by
David S. Miller
a6024562 63058308

+54 -42
+8
include/linux/udp.h
··· 71 71 */ 72 72 int (*encap_rcv)(struct sock *sk, struct sk_buff *skb); 73 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 84 static inline struct udp_sock *udp_sk(const struct sock *sk)
+5 -2
include/net/udp.h
··· 167 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 173 struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb, 171 - struct udphdr *uh); 172 - int udp_gro_complete(struct sk_buff *skb, int nhoff); 174 + struct udphdr *uh, udp_lookup_t lookup); 175 + int udp_gro_complete(struct sk_buff *skb, int nhoff, udp_lookup_t lookup); 173 176 174 177 static inline struct udphdr *udp_gro_udphdr(struct sk_buff *skb) 175 178 {
+20 -32
net/ipv4/udp_offload.c
··· 179 179 180 180 return segs; 181 181 } 182 + EXPORT_SYMBOL(skb_udp_tunnel_segment); 182 183 183 184 static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, 184 185 netdev_features_t features) ··· 305 304 EXPORT_SYMBOL(udp_del_offload); 306 305 307 306 struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb, 308 - struct udphdr *uh) 307 + struct udphdr *uh, udp_lookup_t lookup) 309 308 { 310 - struct udp_offload_priv *uo_priv; 311 309 struct sk_buff *p, **pp = NULL; 312 310 struct udphdr *uh2; 313 311 unsigned int off = skb_gro_offset(skb); 314 312 int flush = 1; 313 + struct sock *sk; 315 314 316 315 if (NAPI_GRO_CB(skb)->encap_mark || 317 316 (skb->ip_summed != CHECKSUM_PARTIAL && ··· 323 322 NAPI_GRO_CB(skb)->encap_mark = 1; 324 323 325 324 rcu_read_lock(); 326 - uo_priv = rcu_dereference(udp_offload_base); 327 - for (; uo_priv != NULL; uo_priv = rcu_dereference(uo_priv->next)) { 328 - if (net_eq(read_pnet(&uo_priv->net), dev_net(skb->dev)) && 329 - uo_priv->offload->port == uh->dest && 330 - uo_priv->offload->callbacks.gro_receive) 331 - goto unflush; 332 - } 325 + sk = (*lookup)(skb, uh->source, uh->dest); 326 + 327 + if (sk && udp_sk(sk)->gro_receive) 328 + goto unflush; 329 + 333 330 goto out_unlock; 334 331 335 332 unflush: ··· 351 352 352 353 skb_gro_pull(skb, sizeof(struct udphdr)); /* pull encapsulating udp header */ 353 354 skb_gro_postpull_rcsum(skb, uh, sizeof(struct udphdr)); 354 - NAPI_GRO_CB(skb)->proto = uo_priv->offload->ipproto; 355 - pp = uo_priv->offload->callbacks.gro_receive(head, skb, 356 - uo_priv->offload); 355 + pp = udp_sk(sk)->gro_receive(sk, head, skb); 357 356 358 357 out_unlock: 359 358 rcu_read_unlock(); ··· 359 362 NAPI_GRO_CB(skb)->flush |= flush; 360 363 return pp; 361 364 } 365 + EXPORT_SYMBOL(udp_gro_receive); 362 366 363 367 static struct sk_buff **udp4_gro_receive(struct sk_buff **head, 364 368 struct sk_buff *skb) ··· 381 383 inet_gro_compute_pseudo); 382 384 skip: 383 385 NAPI_GRO_CB(skb)->is_ipv6 = 0; 384 - return udp_gro_receive(head, skb, uh); 386 + return udp_gro_receive(head, skb, uh, udp4_lib_lookup_skb); 385 387 386 388 flush: 387 389 NAPI_GRO_CB(skb)->flush = 1; 388 390 return NULL; 389 391 } 390 392 391 - int udp_gro_complete(struct sk_buff *skb, int nhoff) 393 + int udp_gro_complete(struct sk_buff *skb, int nhoff, 394 + udp_lookup_t lookup) 392 395 { 393 - struct udp_offload_priv *uo_priv; 394 396 __be16 newlen = htons(skb->len - nhoff); 395 397 struct udphdr *uh = (struct udphdr *)(skb->data + nhoff); 396 398 int err = -ENOSYS; 399 + struct sock *sk; 397 400 398 401 uh->len = newlen; 399 402 400 403 rcu_read_lock(); 401 - 402 - uo_priv = rcu_dereference(udp_offload_base); 403 - for (; uo_priv != NULL; uo_priv = rcu_dereference(uo_priv->next)) { 404 - if (net_eq(read_pnet(&uo_priv->net), dev_net(skb->dev)) && 405 - uo_priv->offload->port == uh->dest && 406 - uo_priv->offload->callbacks.gro_complete) 407 - break; 408 - } 409 - 410 - if (uo_priv) { 411 - NAPI_GRO_CB(skb)->proto = uo_priv->offload->ipproto; 412 - err = uo_priv->offload->callbacks.gro_complete(skb, 413 - nhoff + sizeof(struct udphdr), 414 - uo_priv->offload); 415 - } 416 - 404 + sk = (*lookup)(skb, uh->source, uh->dest); 405 + if (sk && udp_sk(sk)->gro_complete) 406 + err = udp_sk(sk)->gro_complete(sk, skb, 407 + nhoff + sizeof(struct udphdr)); 417 408 rcu_read_unlock(); 418 409 419 410 if (skb->remcsum_offload) ··· 413 426 414 427 return err; 415 428 } 429 + EXPORT_SYMBOL(udp_gro_complete); 416 430 417 431 static int udp4_gro_complete(struct sk_buff *skb, int nhoff) 418 432 { ··· 428 440 skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL; 429 441 } 430 442 431 - return udp_gro_complete(skb, nhoff); 443 + return udp_gro_complete(skb, nhoff, udp4_lib_lookup_skb); 432 444 } 433 445 434 446 static const struct net_offload udpv4_offload = {
+3 -2
net/ipv6/Makefile
··· 8 8 addrlabel.o \ 9 9 route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \ 10 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 16 ipv6-$(CONFIG_SYSCTL) = sysctl_net_ipv6.o 16 17 ipv6-$(CONFIG_IPV6_MROUTE) += ip6mr.o
+8
net/ipv6/af_inet6.c
··· 64 64 #include <asm/uaccess.h> 65 65 #include <linux/mroute6.h> 66 66 67 + #include "ip6_offload.h" 68 + 67 69 MODULE_AUTHOR("Cast of dozens"); 68 70 MODULE_DESCRIPTION("IPv6 protocol stack for Linux"); 69 71 MODULE_LICENSE("GPL"); ··· 961 959 if (err) 962 960 goto udplitev6_fail; 963 961 962 + err = udpv6_offload_init(); 963 + if (err) 964 + goto udpv6_offload_fail; 965 + 964 966 err = tcpv6_init(); 965 967 if (err) 966 968 goto tcpv6_fail; ··· 994 988 ipv6_packet_fail: 995 989 tcpv6_exit(); 996 990 tcpv6_fail: 991 + udpv6_offload_exit(); 992 + udpv6_offload_fail: 997 993 udplitev6_exit(); 998 994 udplitev6_fail: 999 995 udpv6_exit();
-2
net/ipv6/ip6_offload.c
··· 325 325 326 326 if (tcpv6_offload_init() < 0) 327 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 328 if (ipv6_exthdrs_offload_init() < 0) 331 329 pr_crit("%s: Cannot add EXTHDRS protocol offload\n", __func__); 332 330
+2 -1
net/ipv6/ip6_offload.h
··· 12 12 #define __ip6_offload_h 13 13 14 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 17 int tcpv6_offload_init(void); 17 18 18 19 #endif
+8 -3
net/ipv6/udp_offload.c
··· 153 153 154 154 skip: 155 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 158 flush: 159 159 NAPI_GRO_CB(skb)->flush = 1; ··· 173 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 179 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 189 return inet6_add_offload(&udpv6_offload, IPPROTO_UDP); 190 + } 191 + 192 + int udpv6_offload_exit(void) 193 + { 194 + return inet6_del_offload(&udpv6_offload, IPPROTO_UDP); 190 195 }