From: Willem de Bruijn <will...@google.com> Very rough initial version of udp gro, for discussion purpose only at this point.
Among others it - lacks the cmsg UDP_SEGMENT to return gso_size - probably breaks udp tunnels - hard breaks at 40 segments - does not allow a last segment of unequal size Signed-off-by: Willem de Bruijn <will...@google.com> --- include/uapi/linux/udp.h | 1 + net/ipv4/udp.c | 71 ++++++++++++++++++++++++++++++++++++++++ net/ipv4/udp_offload.c | 11 +++---- 3 files changed, 76 insertions(+), 7 deletions(-) diff --git a/include/uapi/linux/udp.h b/include/uapi/linux/udp.h index 09d00f8c442b..7fda3e8c7fcf 100644 --- a/include/uapi/linux/udp.h +++ b/include/uapi/linux/udp.h @@ -33,6 +33,7 @@ struct udphdr { #define UDP_NO_CHECK6_TX 101 /* Disable sending checksum for UDP6X */ #define UDP_NO_CHECK6_RX 102 /* Disable accpeting checksum for UDP6 */ #define UDP_SEGMENT 103 /* Set GSO segmentation size */ +#define UDP_GRO 104 /* Enable GRO */ /* UDP encapsulation types */ #define UDP_ENCAP_ESPINUDP_NON_IKE 1 /* draft-ietf-ipsec-nat-t-ike-00/01 */ diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index bd873a5b8a86..ae49c08e6225 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -2387,6 +2387,51 @@ void udp_destroy_sock(struct sock *sk) } } +static struct sk_buff *udp_gro_receive_cb(struct sock *sk, + struct list_head *head, + struct sk_buff *skb) +{ + struct sk_buff *p; + unsigned int off; + + off = skb_gro_offset(skb) - sizeof(struct udphdr); + + list_for_each_entry(p, head, list) { + if (!NAPI_GRO_CB(p)->same_flow) + continue; + + /* TODO: for UDP_GRO: match size unless last segment */ + if (NAPI_GRO_CB(p)->flush) + break; + + /* TODO: look into ip id check */ + if (skb_gro_receive(p, skb)) { + NAPI_GRO_CB(skb)->flush = 1; + break; + } + + if (NAPI_GRO_CB(skb)->count >= 40) { + return p; + } + + return NULL; + } + + return NULL; +} + +static int udp_gro_complete_cb(struct sock *sk, struct sk_buff *skb, + int nhoff) +{ + skb->csum_start = (unsigned char *)udp_hdr(skb) - skb->head; + skb->csum_offset = offsetof(struct udphdr, check); + skb->ip_summed = CHECKSUM_PARTIAL; + + skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count; + + return 0; +} + /* * Socket option code for UDP */ @@ -2450,6 +2495,32 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, up->gso_size = val; break; + case UDP_GRO: + { + if (val < 0 || val > 1) + return -EINVAL; + + lock_sock(sk); + if (val) { + + if (!udp_sk(sk)->gro_receive) { + udp_sk(sk)->gro_complete = udp_gro_complete_cb; + udp_sk(sk)->gro_receive = udp_gro_receive_cb; + } else { + err = -EALREADY; + } + } else { + if (udp_sk(sk)->gro_receive) { + udp_sk(sk)->gro_receive = NULL; + udp_sk(sk)->gro_complete = NULL; + } else { + err = -ENOENT; + } + } + release_sock(sk); + break; + } + /* * UDP-Lite's partial checksum coverage (RFC 3828). */ diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index f44fe328aa0f..6dd3f0a28b5e 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c @@ -386,6 +386,8 @@ struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb, NAPI_GRO_CB(p)->same_flow = 0; continue; } + + /* TODO: for UDP_GRO: match size */ } skb_gro_pull(skb, sizeof(struct udphdr)); /* pull encapsulating udp header */ @@ -437,11 +439,6 @@ int udp_gro_complete(struct sk_buff *skb, int nhoff, uh->len = newlen; - /* Set encapsulation before calling into inner gro_complete() functions - * to make them set up the inner offsets. - */ - skb->encapsulation = 1; - rcu_read_lock(); sk = (*lookup)(skb, uh->source, uh->dest); if (sk && udp_sk(sk)->gro_complete) @@ -462,11 +459,11 @@ static int udp4_gro_complete(struct sk_buff *skb, int nhoff) struct udphdr *uh = (struct udphdr *)(skb->data + nhoff); if (uh->check) { - skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL_CSUM; + skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_L4; uh->check = ~udp_v4_check(skb->len - nhoff, iph->saddr, iph->daddr, 0); } else { - skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL; + skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_L4; } return udp_gro_complete(skb, nhoff, udp4_lib_lookup_skb); -- 2.19.0.397.gdd90340f6a-goog