From: Tathagat Priyadarshi <tathagat.dpdk@gmail.com>
To: dev@dpdk.org
Cc: joshwash@google.com, stephen@networkplumber.org, stable@dpdk.org
Subject: [PATCH v2 4/4] net/gve: add packet and TSO hardware constraints for DQO
Date: Tue, 13 Oct 2026 10:43:48 +0000 [thread overview]
Message-ID: <20261013104348.714550-5-tathagat.dpdk@gmail.com> (raw)
In-Reply-To: <20261013104348.714550-1-tathagat.dpdk@gmail.com>
The GVNIC DQO hardware restricts each packet (or MTU segment in TSO)
to at most 10 data descriptors (GVE_TX_MAX_DATA_DESCS), beyond which
the hardware drops traffic. In addition to this 10-descriptor limit,
the hardware imposes strict packet sizing and header constraints.
Previously, failing the descriptor limit or rte_mbuf_check() broke
from the Tx burst loop without consuming the invalid mbuf. This caused
tx_burst to return a short count, leading the application to retry the
same rejected packet indefinitely and causing Head-of-Line (HOL)
queue stalls.
Enforce hardware validation and prevent HOL blocking in DPDK:
- Validate packet and hardware constraints prior to ring space checks.
- When packet validation or rte_mbuf_check() fails, drop and free the
invalid mbuf with rte_pktmbuf_free(), update stats, and continue the
burst to prevent Head-of-Line (HOL) blocking.
- Exclude dropped packets from txq->stats.packets while returning the
total consumed count to the caller.
- Rename too_many_descs in struct gve_tx_stats to dropped_pkts to reflect
all validation and malformed mbuf drop events.
- Reject packets whose descriptor count exceeds the maximum capacity of
the ring (accounting for reserved slots, cacheline overlap margin, and
unreported descriptors), preventing permanent queue stalls.
- Define GVE_TX_MIN_PKT_SIZE_DQO (17 bytes) and GVE_TX_MAX_PKT_SIZE_DQO
(9728 bytes) to enforce wire packet boundaries for non-TSO packets
and TSO MSS.
- Define GVE_TX_MAX_TSO_LEN_DQO (262143 bytes) to enforce the hardware
L4 payload limit in the 24-bit tso_total_len field.
- In gve_tx_validate_descs():
* Enforce packet size in [17, 9728] for non-TSO packets alongside
the 10-descriptor limit (nb_descs <= GVE_TX_MAX_DATA_DESCS).
* Reject TSO packets missing an L4 header (l4_len == 0).
* Reject packet with headers (L2-L4) not contiguous in first segment.
* Reject TSO packets without payload (pkt_len <= headlen).
* Enforce TSO total payload length limit (pkt_len - headlen <= 262143).
* Enforce MSS limits [88, 9728] and header length limit <= 255.
Fixes: 4022f9999f56 ("net/gve: support basic Tx data path for DQO")
Fixes: f4a70b449bfa ("net/gve: add DQO Tx descriptor limit")
Fixes: 403c671a46b6 ("net/gve: support TSO in DQO RDA")
Cc: stable@dpdk.org
Signed-off-by: Tathagat Priyadarshi <tathagat.dpdk@gmail.com>
---
drivers/net/gve/base/gve_desc_dqo.h | 3 +
drivers/net/gve/gve_ethdev.h | 2 +-
drivers/net/gve/gve_tx_dqo.c | 92 +++++++++++++++++++++++------
3 files changed, 79 insertions(+), 18 deletions(-)
diff --git a/drivers/net/gve/base/gve_desc_dqo.h b/drivers/net/gve/base/gve_desc_dqo.h
index c1534959c2..53e394f01c 100644
--- a/drivers/net/gve/base/gve_desc_dqo.h
+++ b/drivers/net/gve/base/gve_desc_dqo.h
@@ -12,6 +12,9 @@
#define GVE_TX_MAX_HDR_SIZE_DQO 255
#define GVE_TX_MIN_TSO_MSS_DQO 88
+#define GVE_TX_MIN_PKT_SIZE_DQO 17
+#define GVE_TX_MAX_PKT_SIZE_DQO 9728
+#define GVE_TX_MAX_TSO_LEN_DQO ((256 * 1024) - 1)
/* Basic TX descriptor (DTYPE 0x0C) */
struct gve_tx_pkt_desc_dqo {
diff --git a/drivers/net/gve/gve_ethdev.h b/drivers/net/gve/gve_ethdev.h
index 2033f49444..d6d71c31bb 100644
--- a/drivers/net/gve/gve_ethdev.h
+++ b/drivers/net/gve/gve_ethdev.h
@@ -113,7 +113,7 @@ struct gve_tx_stats {
uint64_t packets;
uint64_t bytes;
uint64_t errors;
- uint64_t too_many_descs;
+ uint64_t dropped_pkts;
};
struct gve_rx_stats {
diff --git a/drivers/net/gve/gve_tx_dqo.c b/drivers/net/gve/gve_tx_dqo.c
index eee1f10df1..94ea426961 100644
--- a/drivers/net/gve/gve_tx_dqo.c
+++ b/drivers/net/gve/gve_tx_dqo.c
@@ -126,20 +126,67 @@ gve_tx_clean_descs_dqo(struct gve_tx_queue *txq, uint16_t nb_descs) {
gve_tx_clean_dqo(txq);
}
+/* Max descriptors a single packet may use. A larger packet can never pass
+ * gve_can_tx(): one slot is always reserved, the cacheline overlap margin
+ * must stay free, and up to GVE_TX_MIN_RE_INTERVAL - 1 descriptors posted
+ * after the last report_event are not reclaimed until more are posted.
+ */
+static inline int
+gve_tx_max_pkt_descs(const struct gve_tx_queue *txq)
+{
+ return (int)txq->nb_tx_desc - 1 - GVE_TX_MIN_DESC_PREVENT_CACHE_OVERLAP -
+ (GVE_TX_MIN_RE_INTERVAL - 1);
+}
+
/* GVE expects at most 10 data descriptors per mtu-sized segment. Beyond this,
* the hardware will assume the driver is malicious and stop transmitting
* packets altogether. Validate that a packet can be sent to avoid sending
* posting descriptors for an invalid packet.
*/
static inline bool
-gve_tx_validate_descs(struct rte_mbuf *tx_pkt, uint16_t nb_descs, bool is_tso)
+gve_tx_validate_descs(const struct gve_tx_queue *txq, struct rte_mbuf *tx_pkt,
+ uint16_t nb_descs, bool is_tso)
{
- if (!is_tso)
+ int num_descs, seg_offset, mbuf_len;
+ int tso_segsz, headlen;
+
+ /* Reject packets that can never fit in the ring */
+ if (unlikely(nb_descs > gve_tx_max_pkt_descs(txq)))
+ return false;
+
+ if (!is_tso) {
+ /* Reject packets that violate hardware length constraints */
+ if (unlikely(tx_pkt->pkt_len < GVE_TX_MIN_PKT_SIZE_DQO ||
+ tx_pkt->pkt_len > GVE_TX_MAX_PKT_SIZE_DQO))
+ return false;
return nb_descs <= GVE_TX_MAX_DATA_DESCS;
+ }
- int tso_segsz = tx_pkt->tso_segsz;
- int num_descs, seg_offset, mbuf_len;
- int headlen = tx_pkt->l2_len + tx_pkt->l3_len + tx_pkt->l4_len;
+ headlen = tx_pkt->l2_len + tx_pkt->l3_len + tx_pkt->l4_len;
+
+ /* Reject missing TCP header on TSO */
+ if (unlikely(tx_pkt->l4_len == 0))
+ return false;
+
+ /* Reject packet with headers (L2-L4) not contiguous in first segment */
+ if (unlikely(tx_pkt->data_len < headlen))
+ return false;
+
+ /* Reject TSO packet without payload */
+ if (unlikely(tx_pkt->pkt_len <= (uint32_t)headlen))
+ return false;
+
+ /* Enforce GVE hardware TSO total length limit */
+ if (unlikely(tx_pkt->pkt_len - headlen > GVE_TX_MAX_TSO_LEN_DQO))
+ return false;
+
+ /* Enforce GVE hardware MSS and header limits */
+ if (unlikely(tx_pkt->tso_segsz < GVE_TX_MIN_TSO_MSS_DQO ||
+ tx_pkt->tso_segsz > GVE_TX_MAX_PKT_SIZE_DQO ||
+ headlen > GVE_TX_MAX_HDR_SIZE_DQO))
+ return false;
+
+ tso_segsz = tx_pkt->tso_segsz;
/* Headers will be split into their own buffer. */
num_descs = 1;
@@ -147,14 +194,18 @@ gve_tx_validate_descs(struct rte_mbuf *tx_pkt, uint16_t nb_descs, bool is_tso)
mbuf_len = tx_pkt->data_len - headlen;
while (tx_pkt) {
+ int seg_remain;
+
if (!mbuf_len)
goto next_mbuf;
- int seg_remain = tso_segsz - seg_offset;
+ seg_remain = tso_segsz - seg_offset;
if (num_descs == GVE_TX_MAX_DATA_DESCS && seg_remain)
return false;
if (seg_remain < mbuf_len) {
+ int mbuf_remain = tx_pkt->data_len % GVE_TX_MAX_BUF_SIZE_DQO;
+
seg_offset = mbuf_len % tso_segsz;
/* The MSS is bound from above by 9728B, so a
* single TSO segment in the middle of an mbuf
@@ -162,7 +213,6 @@ gve_tx_validate_descs(struct rte_mbuf *tx_pkt, uint16_t nb_descs, bool is_tso)
* is not at risk of defying this limitation.
* Thus, such segments are ignored.
*/
- int mbuf_remain = tx_pkt->data_len % GVE_TX_MAX_BUF_SIZE_DQO;
/* For each TSO segment, HW will prepend
* headers. The remaining bytes of this mbuf
@@ -184,7 +234,6 @@ gve_tx_validate_descs(struct rte_mbuf *tx_pkt, uint16_t nb_descs, bool is_tso)
mbuf_len = tx_pkt->data_len;
}
-
return true;
}
@@ -256,6 +305,7 @@ gve_tx_burst_dqo(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
struct rte_mbuf *tx_pkt;
struct gve_tx_pkt *pkts;
uint32_t last_desc_idx;
+ uint16_t nb_drop = 0;
uint16_t nb_tx = 0;
uint64_t bytes = 0;
const char *reason;
@@ -281,9 +331,13 @@ gve_tx_burst_dqo(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
txq->rs_thresh);
- if (rte_mbuf_check(tx_pkt, true, &reason)) {
+ if (unlikely(rte_mbuf_check(tx_pkt, true, &reason))) {
PMD_DRV_DP_LOG(DEBUG, "Invalid mbuf: %s", reason);
- break;
+ txq->stats.dropped_pkts++;
+ txq->stats.errors++;
+ rte_pktmbuf_free(tx_pkt);
+ nb_drop++;
+ continue;
}
ol_flags = tx_pkt->ol_flags;
@@ -292,6 +346,16 @@ gve_tx_burst_dqo(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
nb_descs = gve_tx_pkt_nb_data_descs(tx_pkt);
nb_descs += tso;
+
+ /* Drop packet if it doesn't adhere to hardware limits. */
+ if (unlikely(!gve_tx_validate_descs(txq, tx_pkt, nb_descs, tso))) {
+ txq->stats.dropped_pkts++;
+ txq->stats.errors++;
+ rte_pktmbuf_free(tx_pkt);
+ nb_drop++;
+ continue;
+ }
+
req_descs = nb_descs + GVE_TX_MIN_DESC_PREVENT_CACHE_OVERLAP;
/* Clean if there aren't enough descriptors to send the packet
@@ -306,12 +370,6 @@ gve_tx_burst_dqo(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
break;
}
- /* Drop packet if it doesn't adhere to hardware limits. */
- if (!gve_tx_validate_descs(tx_pkt, nb_descs, tso)) {
- txq->stats.too_many_descs++;
- break;
- }
-
if (!gve_free_compl_tags_pop(txq, &compl_tag))
break;
@@ -357,7 +415,7 @@ gve_tx_burst_dqo(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
}
if (nb_tx > 0) {
- txq->stats.packets += nb_tx;
+ txq->stats.packets += (nb_tx - nb_drop);
txq->stats.bytes += bytes;
txq->stats.errors += nb_pkts - nb_tx;
}
--
2.43.0
next prev parent reply other threads:[~2026-10-13 14:50 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-12 10:50 [PATCH 0/4] net/gve: fix TX queue stalls and sync DQO RDA with Linux driver Tathagat Priyadarshi
2026-10-12 10:50 ` [PATCH 1/4] net/gve: fix DQO descriptor cleaning loop bound Tathagat Priyadarshi
2026-10-12 10:50 ` [PATCH 2/4] net/gve: fix DQO TX queue stall on multi-seg and TSO Tathagat Priyadarshi
2026-10-12 18:13 ` Joshua Washington
2026-10-12 10:50 ` [PATCH 3/4] net/gve: add cacheline overlap margin to DQO TX ring Tathagat Priyadarshi
2026-10-12 10:50 ` [PATCH 4/4] net/gve: add packet and TSO hardware constraints for DQO Tathagat Priyadarshi
2026-10-12 19:40 ` Joshua Washington
2026-10-12 21:28 ` [PATCH 0/4] net/gve: fix TX queue stalls and sync DQO RDA with Linux driver Stephen Hemminger
2026-10-13 10:43 ` [PATCH v2 0/4] net/gve: fix Tx " Tathagat Priyadarshi
2026-10-13 10:43 ` [PATCH v2 1/4] net/gve: fix DQO descriptor cleaning loop bound Tathagat Priyadarshi
2026-10-13 10:43 ` [PATCH v2 2/4] net/gve: fix DQO Tx queue stall on multi-seg and TSO Tathagat Priyadarshi
2026-10-14 0:30 ` Joshua Washington
2026-10-13 10:43 ` [PATCH v2 3/4] net/gve: add cacheline overlap margin to DQO Tx ring Tathagat Priyadarshi
2026-10-13 10:43 ` Tathagat Priyadarshi [this message]
2026-10-14 0:50 ` [PATCH v2 4/4] net/gve: add packet and TSO hardware constraints for DQO Joshua Washington
2026-10-14 4:46 ` Tathagat Priyadarshi
2026-10-13 15:22 ` [PATCH v2 0/4] net/gve: fix Tx queue stalls and sync DQO RDA with Linux driver Stephen Hemminger
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=20261013104348.714550-5-tathagat.dpdk@gmail.com \
--to=tathagat.dpdk@gmail.com \
--cc=dev@dpdk.org \
--cc=joshwash@google.com \
--cc=stable@dpdk.org \
--cc=stephen@networkplumber.org \
/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