mirror of
https://github.com/immortalwrt/immortalwrt.git
synced 2025-08-11 06:11:53 +08:00

This is an automatically generated commit which aids following Kernel patch history, as git will see the move and copy as a rename thus defeating the purpose. For the original discussion see: https://lists.openwrt.org/pipermail/openwrt-devel/2023-October/041673.html Signed-off-by: Mieczyslaw Nalewaj <namiltd@yahoo.com> Link: https://github.com/openwrt/openwrt/pull/16547 Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
141 lines
4.1 KiB
Diff
141 lines
4.1 KiB
Diff
From c9d1d23e5239f41700be69133a5769ac5ebc88a8 Mon Sep 17 00:00:00 2001
|
|
From: Felix Fietkau <nbd@nbd.name>
|
|
Date: Thu, 2 May 2024 10:44:47 +0200
|
|
Subject: [PATCH 6/6] net: add heuristic for enabling TCP fraglist GRO
|
|
|
|
When forwarding TCP after GRO, software segmentation is very expensive,
|
|
especially when the checksum needs to be recalculated.
|
|
One case where that's currently unavoidable is when routing packets over
|
|
PPPoE. Performance improves significantly when using fraglist GRO
|
|
implemented in the same way as for UDP.
|
|
|
|
When NETIF_F_GRO_FRAGLIST is enabled, perform a lookup for an established
|
|
socket in the same netns as the receiving device. While this may not
|
|
cover all relevant use cases in multi-netns configurations, it should be
|
|
good enough for most configurations that need this.
|
|
|
|
Here's a measurement of running 2 TCP streams through a MediaTek MT7622
|
|
device (2-core Cortex-A53), which runs NAT with flow offload enabled from
|
|
one ethernet port to PPPoE on another ethernet port + cake qdisc set to
|
|
1Gbps.
|
|
|
|
rx-gro-list off: 630 Mbit/s, CPU 35% idle
|
|
rx-gro-list on: 770 Mbit/s, CPU 40% idle
|
|
|
|
Acked-by: Paolo Abeni <pabeni@redhat.com>
|
|
Reviewed-by: Eric Dumazet <edumazet@google.com>
|
|
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
|
Reviewed-by: David Ahern <dsahern@kernel.org>
|
|
Reviewed-by: Willem de Bruijn <willemb@google.com>
|
|
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
|
---
|
|
net/ipv4/tcp_offload.c | 32 ++++++++++++++++++++++++++++++++
|
|
net/ipv6/tcpv6_offload.c | 35 +++++++++++++++++++++++++++++++++++
|
|
2 files changed, 67 insertions(+)
|
|
|
|
--- a/net/ipv4/tcp_offload.c
|
|
+++ b/net/ipv4/tcp_offload.c
|
|
@@ -413,6 +413,36 @@ void tcp_gro_complete(struct sk_buff *sk
|
|
}
|
|
EXPORT_SYMBOL(tcp_gro_complete);
|
|
|
|
+static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
|
|
+ struct tcphdr *th)
|
|
+{
|
|
+ const struct iphdr *iph;
|
|
+ struct sk_buff *p;
|
|
+ struct sock *sk;
|
|
+ struct net *net;
|
|
+ int iif, sdif;
|
|
+
|
|
+ if (likely(!(skb->dev->features & NETIF_F_GRO_FRAGLIST)))
|
|
+ return;
|
|
+
|
|
+ p = tcp_gro_lookup(head, th);
|
|
+ if (p) {
|
|
+ NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ inet_get_iif_sdif(skb, &iif, &sdif);
|
|
+ iph = skb_gro_network_header(skb);
|
|
+ net = dev_net(skb->dev);
|
|
+ sk = __inet_lookup_established(net, net->ipv4.tcp_death_row.hashinfo,
|
|
+ iph->saddr, th->source,
|
|
+ iph->daddr, ntohs(th->dest),
|
|
+ iif, sdif);
|
|
+ NAPI_GRO_CB(skb)->is_flist = !sk;
|
|
+ if (sk)
|
|
+ sock_put(sk);
|
|
+}
|
|
+
|
|
INDIRECT_CALLABLE_SCOPE
|
|
struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb)
|
|
{
|
|
@@ -428,6 +458,8 @@ struct sk_buff *tcp4_gro_receive(struct
|
|
if (!th)
|
|
goto flush;
|
|
|
|
+ tcp4_check_fraglist_gro(head, skb, th);
|
|
+
|
|
return tcp_gro_receive(head, skb, th);
|
|
|
|
flush:
|
|
--- a/net/ipv6/tcpv6_offload.c
|
|
+++ b/net/ipv6/tcpv6_offload.c
|
|
@@ -7,12 +7,45 @@
|
|
*/
|
|
#include <linux/indirect_call_wrapper.h>
|
|
#include <linux/skbuff.h>
|
|
+#include <net/inet6_hashtables.h>
|
|
#include <net/gro.h>
|
|
#include <net/protocol.h>
|
|
#include <net/tcp.h>
|
|
#include <net/ip6_checksum.h>
|
|
#include "ip6_offload.h"
|
|
|
|
+static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
|
|
+ struct tcphdr *th)
|
|
+{
|
|
+#if IS_ENABLED(CONFIG_IPV6)
|
|
+ const struct ipv6hdr *hdr;
|
|
+ struct sk_buff *p;
|
|
+ struct sock *sk;
|
|
+ struct net *net;
|
|
+ int iif, sdif;
|
|
+
|
|
+ if (likely(!(skb->dev->features & NETIF_F_GRO_FRAGLIST)))
|
|
+ return;
|
|
+
|
|
+ p = tcp_gro_lookup(head, th);
|
|
+ if (p) {
|
|
+ NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ inet6_get_iif_sdif(skb, &iif, &sdif);
|
|
+ hdr = skb_gro_network_header(skb);
|
|
+ net = dev_net(skb->dev);
|
|
+ sk = __inet6_lookup_established(net, net->ipv4.tcp_death_row.hashinfo,
|
|
+ &hdr->saddr, th->source,
|
|
+ &hdr->daddr, ntohs(th->dest),
|
|
+ iif, sdif);
|
|
+ NAPI_GRO_CB(skb)->is_flist = !sk;
|
|
+ if (sk)
|
|
+ sock_put(sk);
|
|
+#endif /* IS_ENABLED(CONFIG_IPV6) */
|
|
+}
|
|
+
|
|
INDIRECT_CALLABLE_SCOPE
|
|
struct sk_buff *tcp6_gro_receive(struct list_head *head, struct sk_buff *skb)
|
|
{
|
|
@@ -28,6 +61,8 @@ struct sk_buff *tcp6_gro_receive(struct
|
|
if (!th)
|
|
goto flush;
|
|
|
|
+ tcp6_check_fraglist_gro(head, skb, th);
|
|
+
|
|
return tcp_gro_receive(head, skb, th);
|
|
|
|
flush:
|