1 From c9d1d23e5239f41700be69133a5769ac5ebc88a8 Mon Sep 17 00:00:00 2001
2 From: Felix Fietkau <nbd@nbd.name>
3 Date: Thu, 2 May 2024 10:44:47 +0200
4 Subject: [PATCH 6/6] net: add heuristic for enabling TCP fraglist GRO
6 When forwarding TCP after GRO, software segmentation is very expensive,
7 especially when the checksum needs to be recalculated.
8 One case where that's currently unavoidable is when routing packets over
9 PPPoE. Performance improves significantly when using fraglist GRO
10 implemented in the same way as for UDP.
12 When NETIF_F_GRO_FRAGLIST is enabled, perform a lookup for an established
13 socket in the same netns as the receiving device. While this may not
14 cover all relevant use cases in multi-netns configurations, it should be
15 good enough for most configurations that need this.
17 Here's a measurement of running 2 TCP streams through a MediaTek MT7622
18 device (2-core Cortex-A53), which runs NAT with flow offload enabled from
19 one ethernet port to PPPoE on another ethernet port + cake qdisc set to
22 rx-gro-list off: 630 Mbit/s, CPU 35% idle
23 rx-gro-list on: 770 Mbit/s, CPU 40% idle
25 Acked-by: Paolo Abeni <pabeni@redhat.com>
26 Reviewed-by: Eric Dumazet <edumazet@google.com>
27 Signed-off-by: Felix Fietkau <nbd@nbd.name>
28 Reviewed-by: David Ahern <dsahern@kernel.org>
29 Reviewed-by: Willem de Bruijn <willemb@google.com>
30 Signed-off-by: Paolo Abeni <pabeni@redhat.com>
32 net/ipv4/tcp_offload.c | 32 ++++++++++++++++++++++++++++++++
33 net/ipv6/tcpv6_offload.c | 35 +++++++++++++++++++++++++++++++++++
34 2 files changed, 67 insertions(+)
36 --- a/net/ipv4/tcp_offload.c
37 +++ b/net/ipv4/tcp_offload.c
38 @@ -413,6 +413,36 @@ void tcp_gro_complete(struct sk_buff *sk
40 EXPORT_SYMBOL(tcp_gro_complete);
42 +static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
45 + const struct iphdr *iph;
51 + if (likely(!(skb->dev->features & NETIF_F_GRO_FRAGLIST)))
54 + p = tcp_gro_lookup(head, th);
56 + NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
60 + inet_get_iif_sdif(skb, &iif, &sdif);
61 + iph = skb_gro_network_header(skb);
62 + net = dev_net(skb->dev);
63 + sk = __inet_lookup_established(net, net->ipv4.tcp_death_row.hashinfo,
64 + iph->saddr, th->source,
65 + iph->daddr, ntohs(th->dest),
67 + NAPI_GRO_CB(skb)->is_flist = !sk;
72 INDIRECT_CALLABLE_SCOPE
73 struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb)
75 @@ -428,6 +458,8 @@ struct sk_buff *tcp4_gro_receive(struct
79 + tcp4_check_fraglist_gro(head, skb, th);
81 return tcp_gro_receive(head, skb, th);
84 --- a/net/ipv6/tcpv6_offload.c
85 +++ b/net/ipv6/tcpv6_offload.c
88 #include <linux/indirect_call_wrapper.h>
89 #include <linux/skbuff.h>
90 +#include <net/inet6_hashtables.h>
92 #include <net/protocol.h>
94 #include <net/ip6_checksum.h>
95 #include "ip6_offload.h"
97 +static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
100 +#if IS_ENABLED(CONFIG_IPV6)
101 + const struct ipv6hdr *hdr;
107 + if (likely(!(skb->dev->features & NETIF_F_GRO_FRAGLIST)))
110 + p = tcp_gro_lookup(head, th);
112 + NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
116 + inet6_get_iif_sdif(skb, &iif, &sdif);
117 + hdr = skb_gro_network_header(skb);
118 + net = dev_net(skb->dev);
119 + sk = __inet6_lookup_established(net, net->ipv4.tcp_death_row.hashinfo,
120 + &hdr->saddr, th->source,
121 + &hdr->daddr, ntohs(th->dest),
123 + NAPI_GRO_CB(skb)->is_flist = !sk;
126 +#endif /* IS_ENABLED(CONFIG_IPV6) */
129 INDIRECT_CALLABLE_SCOPE
130 struct sk_buff *tcp6_gro_receive(struct list_head *head, struct sk_buff *skb)
132 @@ -28,6 +61,8 @@ struct sk_buff *tcp6_gro_receive(struct
136 + tcp6_check_fraglist_gro(head, skb, th);
138 return tcp_gro_receive(head, skb, th);