From: Joshua Washington <joshwash@google.com>
To: netdev@vger.kernel.org
Cc: Joshua Washington <joshwash@google.com>,
Harshitha Ramamurthy <hramamurthy@google.com>,
Andrew Lunn <andrew+netdev@lunn.ch>,
"David S. Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@kernel.org>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
Jordan Rhee <jordanrhee@google.com>,
Ankit Garg <nktgrg@google.com>,
Tim Hostetler <thostet@google.com>,
Willem de Bruijn <willemb@google.com>,
linux-kernel@vger.kernel.org, stable@vger.kernel.org,
Praveen Kaligineedi <pkaligineedi@google.com>
Subject: [PATCH net] gve: fix checksum state and gso_segs of HW-GRO packets
Date: Fri, 13 Nov 2026 11:55:53 -0800 [thread overview]
Message-ID: <20261113195553.2220562-1-joshwash@google.com> (raw)
From: Ankit Garg <nktgrg@google.com>
With HW-GRO enabled, gve_rx_complete_rsc() turns a coalesced packet into
a GSO skb by setting gso_size, gso_segs and gso_type, but leaves it
CHECKSUM_UNNECESSARY with th->check as the device wrote it. Unlike
tcp4_gro_complete() and tcp6_gro_complete() for a SW-GRO packet, it does
not seed th->check with the TCP pseudo-header checksum or set
CHECKSUM_PARTIAL.
The GRO stack completes a HW-GRO skb itself only if it holds the skb for
merging (since commit 5eddb24901ee ("gro: add support of (hw)gro packets
to gro stack")). Otherwise the skb is delivered as is, e.g. with rx-gro
off or for a packet that is flushed immediately (e.g. with PSH set). If
such an skb is forwarded out of a TSO-capable device, netif_needs_gso()
accepts it as CHECKSUM_UNNECESSARY and the device segments it with a
bogus checksum seed, so every segment is dropped by the receiver. This
blackholes forwarded TCP traffic on VMs acting as routers or NAT
gateways and was observed on VMs in real use.
Fix it in a manner consistent with other drivers with HW-GRO: set the
network and transport headers and, for packets whose checksum the device
verified, seed th->check with the pseudo-header checksum and call
tcp_gro_complete(), passing gso_segs through NAPI_GRO_CB(skb)->count.
skb->data points at the Ethernet header on the napi_gro_frags() path and
at the IP header on the header-split and copybreak paths, so account for
that when setting the network header. Since the header layout now
matters, tell IPv4 from IPv6 by the EtherType, as qede and bnx2x do,
rather than by the packet type.
Also compute gso_segs from the TCP payload length. It was computed from
skb->data_len, which is 0 for a packet that rx_copybreak copied whole
into the linear part, leaving such packets with gso_segs 0 and a
non-zero gso_size.
Fixes: e637c244b954 ("gve: Advertise NETIF_F_GRO_HW instead of NETIF_F_LRO")
Cc: stable@vger.kernel.org
Reviewed-by: Joshua Washington <joshwash@google.com>
Reviewed-by: Jordan Rhee <jordanrhee@google.com>
Reviewed-by: Praveen Kaligineedi <pkaligineedi@google.com>
Signed-off-by: Ankit Garg <nktgrg@google.com>
---
drivers/net/ethernet/google/gve/gve_rx_dqo.c | 69 +++++++++++++++-----
1 file changed, 53 insertions(+), 16 deletions(-)
diff --git a/drivers/net/ethernet/google/gve/gve_rx_dqo.c b/drivers/net/ethernet/google/gve/gve_rx_dqo.c
index 82ed5e48b00a..58696d26c698 100644
--- a/drivers/net/ethernet/google/gve/gve_rx_dqo.c
+++ b/drivers/net/ethernet/google/gve/gve_rx_dqo.c
@@ -13,6 +13,7 @@
#include <linux/ipv6.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
+#include <net/gro.h>
#include <net/ip6_checksum.h>
#include <net/ipv6.h>
#include <net/tcp.h>
@@ -1000,13 +1001,34 @@ static int gve_rx_dqo(struct napi_struct *napi, struct gve_rx_ring *rx,
return -ENOMEM;
}
+/* Prepare a HW-GRO packet the way tcp4/6_gro_complete() prepare a SW-GRO
+ * packet, so that it can be resegmented by GSO or TSO if it is forwarded:
+ * seed th->check with the TCP pseudo-header checksum and set CHECKSUM_PARTIAL.
+ */
+static void gve_rx_fixup_gro_checksum(struct sk_buff *skb)
+{
+ int len = skb->len - skb_transport_offset(skb);
+ struct tcphdr *th = tcp_hdr(skb);
+
+ if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4)
+ th->check = ~tcp_v4_check(len, ip_hdr(skb)->saddr,
+ ip_hdr(skb)->daddr, 0);
+ else
+ th->check = ~tcp_v6_check(len, &ipv6_hdr(skb)->saddr,
+ &ipv6_hdr(skb)->daddr, 0);
+
+ NAPI_GRO_CB(skb)->count = skb_shinfo(skb)->gso_segs;
+ tcp_gro_complete(skb);
+}
+
static int gve_rx_complete_rsc(struct sk_buff *skb,
const struct gve_rx_compl_desc_dqo *desc,
struct gve_ptype ptype)
{
struct skb_shared_info *shinfo = skb_shinfo(skb);
- int rsc_segments, rsc_seg_len, hdr_len;
+ int rsc_seg_len, hdr_len, nhoff, thoff;
skb_frag_t *frag;
+ __be16 proto;
void *va;
/* HW-GRO only coalesces TCP. */
@@ -1017,20 +1039,11 @@ static int gve_rx_complete_rsc(struct sk_buff *skb,
if (!rsc_seg_len)
return 0;
- switch (ptype.l3_type) {
- case GVE_L3_TYPE_IPV4:
- shinfo->gso_type = SKB_GSO_TCPV4;
- break;
- case GVE_L3_TYPE_IPV6:
- shinfo->gso_type = SKB_GSO_TCPV6;
- break;
- default:
- return -EINVAL;
- }
-
if (skb_headlen(skb)) {
- /* With header-split, payload is in the non-linear part */
- rsc_segments = DIV_ROUND_UP(skb->data_len, rsc_seg_len);
+ /* With header-split or copybreak, eth_type_trans() has already
+ * pulled the Ethernet header.
+ */
+ nhoff = 0;
} else {
/* HW-GRO packets are guaranteed to have complete TCP/IP
* headers in frag[0] when header-split is not enabled.
@@ -1039,7 +1052,6 @@ static int gve_rx_complete_rsc(struct sk_buff *skb,
va = skb_frag_address(frag);
hdr_len =
eth_get_headlen(skb->dev, va, skb_frag_size(frag));
- rsc_segments = DIV_ROUND_UP(skb->len - hdr_len, rsc_seg_len);
skb_copy_to_linear_data(skb, va, hdr_len);
skb_frag_size_sub(frag, hdr_len);
/* Verify we didn't empty the fragment completely as that could
@@ -1049,10 +1061,35 @@ static int gve_rx_complete_rsc(struct sk_buff *skb,
skb_frag_off_add(frag, hdr_len);
skb->data_len -= hdr_len;
skb->tail += hdr_len;
+ /* napi_gro_frags() pulls the Ethernet header later. */
+ nhoff = ETH_HLEN;
}
+
+ skb_set_network_header(skb, nhoff);
+ proto = nhoff ? ((struct ethhdr *)skb->data)->h_proto : skb->protocol;
+ switch (proto) {
+ case htons(ETH_P_IP):
+ shinfo->gso_type = SKB_GSO_TCPV4;
+ thoff = nhoff + ip_hdrlen(skb);
+ break;
+ case htons(ETH_P_IPV6):
+ shinfo->gso_type = SKB_GSO_TCPV6;
+ thoff = nhoff + sizeof(struct ipv6hdr);
+ break;
+ default:
+ return -EINVAL;
+ }
+ skb_set_transport_header(skb, thoff);
+
shinfo->gso_size = rsc_seg_len;
- shinfo->gso_segs = rsc_segments;
+ shinfo->gso_segs = DIV_ROUND_UP(skb->len - thoff - tcp_hdrlen(skb),
+ rsc_seg_len);
+
+ if (!IS_ENABLED(CONFIG_INET) ||
+ unlikely(skb->ip_summed != CHECKSUM_UNNECESSARY))
+ return 0;
+ gve_rx_fixup_gro_checksum(skb);
return 0;
}
base-commit: dee3429e94d8089713645706df678ec54a049e13
--
2.56.0.611.g1030f0b940-goog
next reply other threads:[~2026-11-13 19:56 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-13 19:55 Joshua Washington [this message]
2026-11-13 20:37 ` Joshua Washington
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261113195553.2220562-1-joshwash@google.com \
--to=joshwash@google.com \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=hramamurthy@google.com \
--cc=jordanrhee@google.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=nktgrg@google.com \
--cc=pabeni@redhat.com \
--cc=pkaligineedi@google.com \
--cc=stable@vger.kernel.org \
--cc=thostet@google.com \
--cc=willemb@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®