[PATCH net v2 1/9] gve: increment work_done for XDP and error packets

From: Joshua Washington

Date: Tue Sep 22 2026 - 15:47:13 EST


The GVE RX NAPI will continue polling as long as

1) there are packets to be processed, and
2) less than NAPI budget SKBs (denoted in GVE by work_done) have been
passed up to the kernel.

However, GVE does not account for all of the packets that don't create
SKBs, namely error packets and XDP packets.

This can result in XDP programs that scarcely return XDP_PASS failing to
exit the NAPI poll as long as the NIC is DMA'ing packets, possibly
processing the entire RX ring before returning from the NAPI.

This has 3 negative implications:

1) XDP RX path can run much longer than is desirable, hogging CPU
resources.
2) If XDP_PASS is never returned, the work_done never increases beyond
0, which can lead to scheduling delays due to missed chances to
reschedule the NAPI.
3) In AF_XDP zero-copy, XSK_TX occurs after the RX poll. If the RX poll
takes a long time, it will delay TX, leading to degraded performance.

Ensure every packet is accounted for in work_done by incrementing
work_done before checking for the existence of a SKB.

Fixes: 293b49361f91 ("gve: add XDP DROP and PASS support for DQ")
Cc: stable@xxxxxxxxxxxxxxx
Reviewed-by: Tim Hostetler <thostet@xxxxxxxxxx>
Reviewed-by: Jordan Rhee <jordanrhee@xxxxxxxxxx>
Signed-off-by: Joshua Washington <joshwash@xxxxxxxxxx>
---
v2:
- corrected stat counting for packets relative to work_done
---
drivers/net/ethernet/google/gve/gve_rx_dqo.c | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/google/gve/gve_rx_dqo.c b/drivers/net/ethernet/google/gve/gve_rx_dqo.c
index 5cf242b28557..c3f4a76b0fac 100644
--- a/drivers/net/ethernet/google/gve/gve_rx_dqo.c
+++ b/drivers/net/ethernet/google/gve/gve_rx_dqo.c
@@ -932,6 +932,10 @@ static int gve_rx_dqo(struct napi_struct *napi, struct gve_rx_ring *rx,
if (xdp_act != XDP_PASS) {
gve_xdp_done_dqo(priv, rx, &gve_xdp.xdp, xprog, xdp_act,
buf_state);
+ u64_stats_update_begin(&rx->statss);
+ rx->rpackets++;
+ rx->rbytes += compl_desc->packet_len;
+ u64_stats_update_end(&rx->statss);
return 0;
}

@@ -1090,6 +1094,7 @@ int gve_rx_poll_dqo(struct gve_notify_block *block, int budget)
struct gve_rx_ring *rx;
struct gve_priv *priv;
u64 xdp_redirects;
+ u32 rx_packets = 0;
u32 work_done = 0;
u64 bytes = 0;
u64 xdp_txs;
@@ -1150,13 +1155,14 @@ int gve_rx_poll_dqo(struct gve_notify_block *block, int budget)
/* Free running counter of completed descriptors */
rx->cnt++;

- if (!rx->ctx.skb_head)
- continue;
-
if (!compl_desc->end_of_packet)
continue;

work_done++;
+
+ if (!rx->ctx.skb_head)
+ continue;
+
pkt_bytes = rx->ctx.skb_head->len;
/* The ethernet header (first ETH_HLEN bytes) is snipped off
* by eth_type_trans.
@@ -1164,6 +1170,9 @@ int gve_rx_poll_dqo(struct gve_notify_block *block, int budget)
if (skb_headlen(rx->ctx.skb_head))
pkt_bytes += ETH_HLEN;

+ rx_packets++;
+ bytes += pkt_bytes;
+
/* gve_rx_complete_skb() will consume skb if successful */
if (gve_rx_complete_skb(rx, napi, compl_desc, feat) != 0) {
gve_rx_free_skb(napi, rx);
@@ -1173,7 +1182,6 @@ int gve_rx_poll_dqo(struct gve_notify_block *block, int budget)
continue;
}

- bytes += pkt_bytes;
rx->ctx.skb_head = NULL;
rx->ctx.skb_tail = NULL;
}
@@ -1187,7 +1195,7 @@ int gve_rx_poll_dqo(struct gve_notify_block *block, int budget)
gve_rx_post_buffers_dqo(rx);

u64_stats_update_begin(&rx->statss);
- rx->rpackets += work_done;
+ rx->rpackets += rx_packets;
rx->rbytes += bytes;
u64_stats_update_end(&rx->statss);

--
2.55.0.1082.g2b9226bbc0-goog