[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <98a7e20010265e3ebf9d7e6d6dfb7339d5db7b99.1764943231.git.pabeni@redhat.com>
Date: Fri, 5 Dec 2025 15:03:30 +0100
From: Paolo Abeni <pabeni@...hat.com>
To: netdev@...r.kernel.org
Cc: "David S. Miller" <davem@...emloft.net>,
Eric Dumazet <edumazet@...gle.com>,
Jakub Kicinski <kuba@...nel.org>,
Simon Horman <horms@...nel.org>,
Neal Cardwell <ncardwell@...gle.com>,
Kuniyuki Iwashima <kuniyu@...gle.com>,
David Ahern <dsahern@...nel.org>
Subject: [RFC PATCH 1/2] net: gro: avoid relaying on skb->transport_header at receive time
Currently {tcp,udp}_gro_receive relay on the gro network stage setting
the correct transport header offset for all the skbs held by the GRO
engine.
Such assumption is not necessary, as the code can instead leverage the
offset already available for the currently processed skb. Add a couple
of helpers to for readabilty' sake.
As skb->transport_header lays on a different cacheline wrt skb->data,
this should save a cacheline access for each packet aggregation.
Additionally this will make the next patch possible.
Note that the compiler (gcc 15.2.1) does inline the tcp_gro_lookup()
call in tcp_gro_receive(), so the additional argument is only relevant
for the fraglist case.
Signed-off-by: Paolo Abeni <pabeni@...hat.com>
---
include/net/gro.h | 26 ++++++++++++++++++++++++++
include/net/tcp.h | 3 ++-
net/ipv4/tcp_offload.c | 15 ++++++++-------
net/ipv4/udp_offload.c | 4 ++--
net/ipv6/tcpv6_offload.c | 2 +-
5 files changed, 39 insertions(+), 11 deletions(-)
diff --git a/include/net/gro.h b/include/net/gro.h
index b65f631c521d..fdb9285ab117 100644
--- a/include/net/gro.h
+++ b/include/net/gro.h
@@ -420,6 +420,18 @@ struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb,
struct udphdr *uh, struct sock *sk);
int udp_gro_complete(struct sk_buff *skb, int nhoff, udp_lookup_t lookup);
+/* Return the skb hdr corresponding to the specified skb2 hdr.
+ * skb2 is held in the gro engine, i.e. its headers are in the linear part.
+ */
+static inline const void *
+skb_gro_header_from(const struct sk_buff *skb, const struct sk_buff *skb2,
+ const void *hdr2)
+{
+ size_t offset = (unsigned char *)hdr2 - skb2->data;
+
+ return skb->data + offset;
+}
+
static inline struct udphdr *udp_gro_udphdr(struct sk_buff *skb)
{
struct udphdr *uh;
@@ -432,6 +444,13 @@ static inline struct udphdr *udp_gro_udphdr(struct sk_buff *skb)
return uh;
}
+static inline const struct udphdr *
+udp_gro_udphdr_from(const struct sk_buff *skb, const struct sk_buff *skb2,
+ const struct udphdr *uh)
+{
+ return (const struct udphdr *)skb_gro_header_from(skb, skb2, uh);
+}
+
static inline __wsum ip6_gro_compute_pseudo(const struct sk_buff *skb,
int proto)
{
@@ -620,4 +639,11 @@ static inline struct tcphdr *tcp_gro_pull_header(struct sk_buff *skb)
return th;
}
+static inline const struct tcphdr *
+tcp_gro_header_from(const struct sk_buff *skb, const struct sk_buff *skb2,
+ const struct tcphdr *th)
+{
+ return (const struct tcphdr *)skb_gro_header_from(skb, skb2, th);
+}
+
#endif /* _NET_GRO_H */
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 0deb5e9dd911..a4c239daf2ea 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -2313,7 +2313,8 @@ void tcp_v4_destroy_sock(struct sock *sk);
struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
netdev_features_t features);
-struct sk_buff *tcp_gro_lookup(struct list_head *head, struct tcphdr *th);
+struct sk_buff *tcp_gro_lookup(struct list_head *head, struct sk_buff *skb,
+ struct tcphdr *th);
struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb,
struct tcphdr *th);
INDIRECT_CALLABLE_DECLARE(int tcp4_gro_complete(struct sk_buff *skb, int thoff));
diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c
index fdda18b1abda..fa36686df6d7 100644
--- a/net/ipv4/tcp_offload.c
+++ b/net/ipv4/tcp_offload.c
@@ -261,16 +261,17 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
return segs;
}
-struct sk_buff *tcp_gro_lookup(struct list_head *head, struct tcphdr *th)
+struct sk_buff *tcp_gro_lookup(struct list_head *head, struct sk_buff *skb,
+ struct tcphdr *th)
{
- struct tcphdr *th2;
+ const struct tcphdr *th2;
struct sk_buff *p;
list_for_each_entry(p, head, list) {
if (!NAPI_GRO_CB(p)->same_flow)
continue;
- th2 = tcp_hdr(p);
+ th2 = tcp_gro_header_from(p, skb, th);
if (*(u32 *)&th->source ^ *(u32 *)&th2->source) {
NAPI_GRO_CB(p)->same_flow = 0;
continue;
@@ -287,8 +288,8 @@ struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb,
{
unsigned int thlen = th->doff * 4;
struct sk_buff *pp = NULL;
+ const struct tcphdr *th2;
struct sk_buff *p;
- struct tcphdr *th2;
unsigned int len;
__be32 flags;
unsigned int mss = 1;
@@ -298,11 +299,11 @@ struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb,
len = skb_gro_len(skb);
flags = tcp_flag_word(th);
- p = tcp_gro_lookup(head, th);
+ p = tcp_gro_lookup(head, skb, th);
if (!p)
goto out_check_final;
- th2 = tcp_hdr(p);
+ th2 = tcp_gro_header_from(p, skb, th);
flush = (__force int)(flags & TCP_FLAG_CWR);
flush |= (__force int)((flags ^ tcp_flag_word(th2)) &
~(TCP_FLAG_FIN | TCP_FLAG_PSH));
@@ -398,7 +399,7 @@ static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
if (likely(!(skb->dev->features & NETIF_F_GRO_FRAGLIST)))
return;
- p = tcp_gro_lookup(head, th);
+ p = tcp_gro_lookup(head, skb, th);
if (p) {
NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
return;
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index 19d0b5b09ffa..7048cb2a28a2 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -701,7 +701,7 @@ static struct sk_buff *udp_gro_receive_segment(struct list_head *head,
{
struct udphdr *uh = udp_gro_udphdr(skb);
struct sk_buff *pp = NULL;
- struct udphdr *uh2;
+ const struct udphdr *uh2;
struct sk_buff *p;
unsigned int ulen;
int ret = 0;
@@ -726,7 +726,7 @@ static struct sk_buff *udp_gro_receive_segment(struct list_head *head,
if (!NAPI_GRO_CB(p)->same_flow)
continue;
- uh2 = udp_hdr(p);
+ uh2 = udp_gro_udphdr_from(p, skb, uh);
/* Match ports only, as csum is always non zero */
if ((*(u32 *)&uh->source != *(u32 *)&uh2->source)) {
diff --git a/net/ipv6/tcpv6_offload.c b/net/ipv6/tcpv6_offload.c
index effeba58630b..ae481bf95651 100644
--- a/net/ipv6/tcpv6_offload.c
+++ b/net/ipv6/tcpv6_offload.c
@@ -27,7 +27,7 @@ static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
if (likely(!(skb->dev->features & NETIF_F_GRO_FRAGLIST)))
return;
- p = tcp_gro_lookup(head, th);
+ p = tcp_gro_lookup(head, skb, th);
if (p) {
NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
return;
--
2.52.0
Powered by blists - more mailing lists