Commit 9fd1ff5d authored by Steffen Klassert's avatar Steffen Klassert Committed by David S. Miller

udp: Support UDP fraglist GRO/GSO.

This patch extends UDP GRO to support fraglist GRO/GSO
by using the previously introduced infrastructure.
If the feature is enabled, all UDP packets are going to
fraglist GRO (local input and forward).

After validating the csum,  we mark ip_summed as
CHECKSUM_UNNECESSARY for fraglist GRO packets to
make sure that the csum is not touched.
Signed-off-by: default avatarSteffen Klassert <steffen.klassert@secunet.com>
Reviewed-by: default avatarWillem de Bruijn <willemb@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 3a1296a3
...@@ -167,7 +167,7 @@ typedef struct sock *(*udp_lookup_t)(struct sk_buff *skb, __be16 sport, ...@@ -167,7 +167,7 @@ typedef struct sock *(*udp_lookup_t)(struct sk_buff *skb, __be16 sport,
__be16 dport); __be16 dport);
struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb, struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb,
struct udphdr *uh, udp_lookup_t lookup); struct udphdr *uh, struct sock *sk);
int udp_gro_complete(struct sk_buff *skb, int nhoff, udp_lookup_t lookup); int udp_gro_complete(struct sk_buff *skb, int nhoff, udp_lookup_t lookup);
struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb, struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
......
...@@ -184,6 +184,20 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, ...@@ -184,6 +184,20 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
} }
EXPORT_SYMBOL(skb_udp_tunnel_segment); EXPORT_SYMBOL(skb_udp_tunnel_segment);
static struct sk_buff *__udp_gso_segment_list(struct sk_buff *skb,
netdev_features_t features)
{
unsigned int mss = skb_shinfo(skb)->gso_size;
skb = skb_segment_list(skb, features, skb_mac_header_len(skb));
if (IS_ERR(skb))
return skb;
udp_hdr(skb)->len = htons(sizeof(struct udphdr) + mss);
return skb;
}
struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb, struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
netdev_features_t features) netdev_features_t features)
{ {
...@@ -196,6 +210,9 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb, ...@@ -196,6 +210,9 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
__sum16 check; __sum16 check;
__be16 newlen; __be16 newlen;
if (skb_shinfo(gso_skb)->gso_type & SKB_GSO_FRAGLIST)
return __udp_gso_segment_list(gso_skb, features);
mss = skb_shinfo(gso_skb)->gso_size; mss = skb_shinfo(gso_skb)->gso_size;
if (gso_skb->len <= sizeof(*uh) + mss) if (gso_skb->len <= sizeof(*uh) + mss)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
...@@ -354,6 +371,7 @@ static struct sk_buff *udp_gro_receive_segment(struct list_head *head, ...@@ -354,6 +371,7 @@ static struct sk_buff *udp_gro_receive_segment(struct list_head *head,
struct udphdr *uh2; struct udphdr *uh2;
struct sk_buff *p; struct sk_buff *p;
unsigned int ulen; unsigned int ulen;
int ret = 0;
/* requires non zero csum, for symmetry with GSO */ /* requires non zero csum, for symmetry with GSO */
if (!uh->check) { if (!uh->check) {
...@@ -369,7 +387,6 @@ static struct sk_buff *udp_gro_receive_segment(struct list_head *head, ...@@ -369,7 +387,6 @@ static struct sk_buff *udp_gro_receive_segment(struct list_head *head,
} }
/* pull encapsulating udp header */ /* pull encapsulating udp header */
skb_gro_pull(skb, sizeof(struct udphdr)); skb_gro_pull(skb, sizeof(struct udphdr));
skb_gro_postpull_rcsum(skb, uh, sizeof(struct udphdr));
list_for_each_entry(p, head, list) { list_for_each_entry(p, head, list) {
if (!NAPI_GRO_CB(p)->same_flow) if (!NAPI_GRO_CB(p)->same_flow)
...@@ -383,14 +400,40 @@ static struct sk_buff *udp_gro_receive_segment(struct list_head *head, ...@@ -383,14 +400,40 @@ static struct sk_buff *udp_gro_receive_segment(struct list_head *head,
continue; continue;
} }
if (NAPI_GRO_CB(skb)->is_flist != NAPI_GRO_CB(p)->is_flist) {
NAPI_GRO_CB(skb)->flush = 1;
return p;
}
/* Terminate the flow on len mismatch or if it grow "too much". /* Terminate the flow on len mismatch or if it grow "too much".
* Under small packet flood GRO count could elsewhere grow a lot * Under small packet flood GRO count could elsewhere grow a lot
* leading to excessive truesize values. * leading to excessive truesize values.
* On len mismatch merge the first packet shorter than gso_size, * On len mismatch merge the first packet shorter than gso_size,
* otherwise complete the GRO packet. * otherwise complete the GRO packet.
*/ */
if (ulen > ntohs(uh2->len) || skb_gro_receive(p, skb) || if (ulen > ntohs(uh2->len)) {
ulen != ntohs(uh2->len) || pp = p;
} else {
if (NAPI_GRO_CB(skb)->is_flist) {
if (!pskb_may_pull(skb, skb_gro_offset(skb))) {
NAPI_GRO_CB(skb)->flush = 1;
return NULL;
}
if ((skb->ip_summed != p->ip_summed) ||
(skb->csum_level != p->csum_level)) {
NAPI_GRO_CB(skb)->flush = 1;
return NULL;
}
ret = skb_gro_receive_list(p, skb);
} else {
skb_gro_postpull_rcsum(skb, uh,
sizeof(struct udphdr));
ret = skb_gro_receive(p, skb);
}
}
if (ret || ulen != ntohs(uh2->len) ||
NAPI_GRO_CB(p)->count >= UDP_GRO_CNT_MAX) NAPI_GRO_CB(p)->count >= UDP_GRO_CNT_MAX)
pp = p; pp = p;
...@@ -401,36 +444,29 @@ static struct sk_buff *udp_gro_receive_segment(struct list_head *head, ...@@ -401,36 +444,29 @@ static struct sk_buff *udp_gro_receive_segment(struct list_head *head,
return NULL; return NULL;
} }
INDIRECT_CALLABLE_DECLARE(struct sock *udp6_lib_lookup_skb(struct sk_buff *skb,
__be16 sport, __be16 dport));
struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb, struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb,
struct udphdr *uh, udp_lookup_t lookup) struct udphdr *uh, struct sock *sk)
{ {
struct sk_buff *pp = NULL; struct sk_buff *pp = NULL;
struct sk_buff *p; struct sk_buff *p;
struct udphdr *uh2; struct udphdr *uh2;
unsigned int off = skb_gro_offset(skb); unsigned int off = skb_gro_offset(skb);
int flush = 1; int flush = 1;
struct sock *sk;
rcu_read_lock(); if (skb->dev->features & NETIF_F_GRO_FRAGLIST)
sk = INDIRECT_CALL_INET(lookup, udp6_lib_lookup_skb, NAPI_GRO_CB(skb)->is_flist = sk ? !udp_sk(sk)->gro_enabled: 1;
udp4_lib_lookup_skb, skb, uh->source, uh->dest);
if (!sk)
goto out_unlock;
if (udp_sk(sk)->gro_enabled) { if ((sk && udp_sk(sk)->gro_enabled) || NAPI_GRO_CB(skb)->is_flist) {
pp = call_gro_receive(udp_gro_receive_segment, head, skb); pp = call_gro_receive(udp_gro_receive_segment, head, skb);
rcu_read_unlock();
return pp; return pp;
} }
if (NAPI_GRO_CB(skb)->encap_mark || if (!sk || NAPI_GRO_CB(skb)->encap_mark ||
(skb->ip_summed != CHECKSUM_PARTIAL && (skb->ip_summed != CHECKSUM_PARTIAL &&
NAPI_GRO_CB(skb)->csum_cnt == 0 && NAPI_GRO_CB(skb)->csum_cnt == 0 &&
!NAPI_GRO_CB(skb)->csum_valid) || !NAPI_GRO_CB(skb)->csum_valid) ||
!udp_sk(sk)->gro_receive) !udp_sk(sk)->gro_receive)
goto out_unlock; goto out;
/* mark that this skb passed once through the tunnel gro layer */ /* mark that this skb passed once through the tunnel gro layer */
NAPI_GRO_CB(skb)->encap_mark = 1; NAPI_GRO_CB(skb)->encap_mark = 1;
...@@ -457,8 +493,7 @@ struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb, ...@@ -457,8 +493,7 @@ struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb,
skb_gro_postpull_rcsum(skb, uh, sizeof(struct udphdr)); skb_gro_postpull_rcsum(skb, uh, sizeof(struct udphdr));
pp = call_gro_receive_sk(udp_sk(sk)->gro_receive, sk, head, skb); pp = call_gro_receive_sk(udp_sk(sk)->gro_receive, sk, head, skb);
out_unlock: out:
rcu_read_unlock();
skb_gro_flush_final(skb, pp, flush); skb_gro_flush_final(skb, pp, flush);
return pp; return pp;
} }
...@@ -468,8 +503,10 @@ INDIRECT_CALLABLE_SCOPE ...@@ -468,8 +503,10 @@ INDIRECT_CALLABLE_SCOPE
struct sk_buff *udp4_gro_receive(struct list_head *head, struct sk_buff *skb) struct sk_buff *udp4_gro_receive(struct list_head *head, struct sk_buff *skb)
{ {
struct udphdr *uh = udp_gro_udphdr(skb); struct udphdr *uh = udp_gro_udphdr(skb);
struct sk_buff *pp;
struct sock *sk;
if (unlikely(!uh) || !static_branch_unlikely(&udp_encap_needed_key)) if (unlikely(!uh))
goto flush; goto flush;
/* Don't bother verifying checksum if we're going to flush anyway. */ /* Don't bother verifying checksum if we're going to flush anyway. */
...@@ -484,7 +521,11 @@ struct sk_buff *udp4_gro_receive(struct list_head *head, struct sk_buff *skb) ...@@ -484,7 +521,11 @@ struct sk_buff *udp4_gro_receive(struct list_head *head, struct sk_buff *skb)
inet_gro_compute_pseudo); inet_gro_compute_pseudo);
skip: skip:
NAPI_GRO_CB(skb)->is_ipv6 = 0; NAPI_GRO_CB(skb)->is_ipv6 = 0;
return udp_gro_receive(head, skb, uh, udp4_lib_lookup_skb); rcu_read_lock();
sk = static_branch_unlikely(&udp_encap_needed_key) ? udp4_lib_lookup_skb(skb, uh->source, uh->dest) : NULL;
pp = udp_gro_receive(head, skb, uh, sk);
rcu_read_unlock();
return pp;
flush: flush:
NAPI_GRO_CB(skb)->flush = 1; NAPI_GRO_CB(skb)->flush = 1;
...@@ -517,9 +558,7 @@ int udp_gro_complete(struct sk_buff *skb, int nhoff, ...@@ -517,9 +558,7 @@ int udp_gro_complete(struct sk_buff *skb, int nhoff,
rcu_read_lock(); rcu_read_lock();
sk = INDIRECT_CALL_INET(lookup, udp6_lib_lookup_skb, sk = INDIRECT_CALL_INET(lookup, udp6_lib_lookup_skb,
udp4_lib_lookup_skb, skb, uh->source, uh->dest); udp4_lib_lookup_skb, skb, uh->source, uh->dest);
if (sk && udp_sk(sk)->gro_enabled) { if (sk && udp_sk(sk)->gro_complete) {
err = udp_gro_complete_segment(skb);
} else if (sk && udp_sk(sk)->gro_complete) {
skb_shinfo(skb)->gso_type = uh->check ? SKB_GSO_UDP_TUNNEL_CSUM skb_shinfo(skb)->gso_type = uh->check ? SKB_GSO_UDP_TUNNEL_CSUM
: SKB_GSO_UDP_TUNNEL; : SKB_GSO_UDP_TUNNEL;
...@@ -529,6 +568,8 @@ int udp_gro_complete(struct sk_buff *skb, int nhoff, ...@@ -529,6 +568,8 @@ int udp_gro_complete(struct sk_buff *skb, int nhoff,
skb->encapsulation = 1; skb->encapsulation = 1;
err = udp_sk(sk)->gro_complete(sk, skb, err = udp_sk(sk)->gro_complete(sk, skb,
nhoff + sizeof(struct udphdr)); nhoff + sizeof(struct udphdr));
} else {
err = udp_gro_complete_segment(skb);
} }
rcu_read_unlock(); rcu_read_unlock();
...@@ -544,6 +585,23 @@ INDIRECT_CALLABLE_SCOPE int udp4_gro_complete(struct sk_buff *skb, int nhoff) ...@@ -544,6 +585,23 @@ INDIRECT_CALLABLE_SCOPE int udp4_gro_complete(struct sk_buff *skb, int nhoff)
const struct iphdr *iph = ip_hdr(skb); const struct iphdr *iph = ip_hdr(skb);
struct udphdr *uh = (struct udphdr *)(skb->data + nhoff); struct udphdr *uh = (struct udphdr *)(skb->data + nhoff);
if (NAPI_GRO_CB(skb)->is_flist) {
uh->len = htons(skb->len - nhoff);
skb_shinfo(skb)->gso_type |= (SKB_GSO_FRAGLIST|SKB_GSO_UDP_L4);
skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;
if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
if (skb->csum_level < SKB_MAX_CSUM_LEVEL)
skb->csum_level++;
} else {
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb->csum_level = 0;
}
return 0;
}
if (uh->check) if (uh->check)
uh->check = ~udp_v4_check(skb->len - nhoff, iph->saddr, uh->check = ~udp_v4_check(skb->len - nhoff, iph->saddr,
iph->daddr, 0); iph->daddr, 0);
......
...@@ -115,8 +115,10 @@ INDIRECT_CALLABLE_SCOPE ...@@ -115,8 +115,10 @@ INDIRECT_CALLABLE_SCOPE
struct sk_buff *udp6_gro_receive(struct list_head *head, struct sk_buff *skb) struct sk_buff *udp6_gro_receive(struct list_head *head, struct sk_buff *skb)
{ {
struct udphdr *uh = udp_gro_udphdr(skb); struct udphdr *uh = udp_gro_udphdr(skb);
struct sk_buff *pp;
struct sock *sk;
if (unlikely(!uh) || !static_branch_unlikely(&udpv6_encap_needed_key)) if (unlikely(!uh))
goto flush; goto flush;
/* Don't bother verifying checksum if we're going to flush anyway. */ /* Don't bother verifying checksum if we're going to flush anyway. */
...@@ -132,7 +134,11 @@ struct sk_buff *udp6_gro_receive(struct list_head *head, struct sk_buff *skb) ...@@ -132,7 +134,11 @@ struct sk_buff *udp6_gro_receive(struct list_head *head, struct sk_buff *skb)
skip: skip:
NAPI_GRO_CB(skb)->is_ipv6 = 1; NAPI_GRO_CB(skb)->is_ipv6 = 1;
return udp_gro_receive(head, skb, uh, udp6_lib_lookup_skb); rcu_read_lock();
sk = static_branch_unlikely(&udpv6_encap_needed_key) ? udp6_lib_lookup_skb(skb, uh->source, uh->dest) : NULL;
pp = udp_gro_receive(head, skb, uh, sk);
rcu_read_unlock();
return pp;
flush: flush:
NAPI_GRO_CB(skb)->flush = 1; NAPI_GRO_CB(skb)->flush = 1;
...@@ -144,6 +150,23 @@ INDIRECT_CALLABLE_SCOPE int udp6_gro_complete(struct sk_buff *skb, int nhoff) ...@@ -144,6 +150,23 @@ INDIRECT_CALLABLE_SCOPE int udp6_gro_complete(struct sk_buff *skb, int nhoff)
const struct ipv6hdr *ipv6h = ipv6_hdr(skb); const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
struct udphdr *uh = (struct udphdr *)(skb->data + nhoff); struct udphdr *uh = (struct udphdr *)(skb->data + nhoff);
if (NAPI_GRO_CB(skb)->is_flist) {
uh->len = htons(skb->len - nhoff);
skb_shinfo(skb)->gso_type |= (SKB_GSO_FRAGLIST|SKB_GSO_UDP_L4);
skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;
if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
if (skb->csum_level < SKB_MAX_CSUM_LEVEL)
skb->csum_level++;
} else {
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb->csum_level = 0;
}
return 0;
}
if (uh->check) if (uh->check)
uh->check = ~udp_v6_check(skb->len - nhoff, &ipv6h->saddr, uh->check = ~udp_v6_check(skb->len - nhoff, &ipv6h->saddr,
&ipv6h->daddr, 0); &ipv6h->daddr, 0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment