[PATCH 06/10] net/bnxt: fix packet burst truncation after invalid Tx descriptor descriptor

Mohammad Shuab Siddique mohammad-shuab.siddique at broadcom.com
Thu Jun 4 05:18:47 CEST 2026


From: Zoe Cheimets <zoe.cheimets at broadcom.com>

When bnxt_start_xmit() returned -EINVAL for a bad packet, the burst
loop broke immediately, dropping all subsequent valid packets. The
Tx ring was also left in a corrupt state because tx_raw_prod was not
rolled back after a partial descriptor write. Additionally, the
tx_mbuf_drop oerrors accumulation was gated behind a tx_started
check, silently omitting drops from the statistics.

Fix by rolling back tx_raw_prod and any partially-written ring slots
on the drop path, replacing the break with continue so the burst
loop keeps processing remaining packets, and moving the tx_mbuf_drop
read to before the tx_started guard in both stats paths. Return
value is corrected to RTE_MIN(nb_tx_pkts + dropped, nb_pkts) to
accurately report consumed packet count.

Fixes: 220de9869bc3 ("net/bnxt: optimize Tx batching")
Cc: stable at dpdk.org
Signed-off-by: Zoe Cheimets <zoe.cheimets at broadcom.com>
Signed-off-by: Mohammad Shuab Siddique <mohammad-shuab.siddique at broadcom.com>
---
 drivers/net/bnxt/bnxt_txr.c | 34 ++++++++++++++++++++++++++++++----
 1 file changed, 30 insertions(+), 4 deletions(-)

diff --git a/drivers/net/bnxt/bnxt_txr.c b/drivers/net/bnxt/bnxt_txr.c
index a6b062637c..bb9917d705 100644
--- a/drivers/net/bnxt/bnxt_txr.c
+++ b/drivers/net/bnxt/bnxt_txr.c
@@ -239,15 +239,17 @@ static int bnxt_start_xmit(struct rte_mbuf *tx_pkt,
 				uint16_t *coal_pkts,
 				struct tx_bd_long **last_txbd)
 {
+	struct tx_bd_long *last_txbd_save = *last_txbd;
 	struct bnxt_tx_ring_info *txr = txq->tx_ring;
 	struct bnxt_ring *ring = txr->tx_ring_struct;
+	uint16_t tx_raw_prod_save = txr->tx_raw_prod;
 	uint32_t outer_tpid_bd = 0;
 	struct tx_bd_long *txbd;
 	struct tx_bd_long_hi *txbd1 = NULL;
 	uint32_t vlan_tag_flags;
 	bool long_bd = false;
 	unsigned short nr_bds;
-	uint16_t prod;
+	uint16_t prod, idx;
 	bool pkt_needs_ts = 0;
 	struct rte_mbuf *m_seg;
 	struct rte_mbuf **tx_buf;
@@ -507,6 +509,24 @@ static int bnxt_start_xmit(struct rte_mbuf *tx_pkt,
 
 	return 0;
 drop:
+	/* Roll back any descriptors and tx_buf_ring slots that were written
+	 * for this packet before the failure was detected.  Walking from the
+	 * saved producer index up to (but not including) the current one is
+	 * safe because we hold the Tx lock and the HW has not been notified
+	 * (the doorbell is only rung after bnxt_start_xmit() returns
+	 * successfully).
+	 */
+	idx = tx_raw_prod_save;
+
+	while (idx != txr->tx_raw_prod) {
+		uint16_t slot = RING_IDX(ring, idx);
+
+		txr->tx_buf_ring[slot] = NULL;
+		txr->tx_desc_ring[slot].address = 0;
+		idx = RING_NEXT(idx);
+	}
+	txr->tx_raw_prod = tx_raw_prod_save;
+	*last_txbd = last_txbd_save;
 	rte_pktmbuf_free(tx_pkt);
 ret:
 	return rc;
@@ -836,22 +856,28 @@ uint16_t _bnxt_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
 
 		if (unlikely(rc)) {
 			if (rc == -EINVAL) {
+				coal_pkts--;
 				rte_atomic_fetch_add_explicit(&txq->tx_mbuf_drop, 1,
 							      rte_memory_order_relaxed);
 				dropped++;
+				continue;
 			}
 			break;
 		}
 	}
 
-	if (likely(nb_tx_pkts)) {
+	/* last_txbd is used to check for if any packets have been sent in
+	 * the burst as bnxt_start_xmit will update it to the most recent
+	 * non-dropped buffer descriptor in the burst.
+	 */
+	if (likely(last_txbd != NULL)) {
 		/* Request a completion on the last packet */
 		last_txbd->flags_type &= ~TX_BD_LONG_FLAGS_NO_CMPL;
 		bnxt_db_write(&txq->tx_ring->tx_db, txq->tx_ring->tx_raw_prod);
 	}
 
-	nb_tx_pkts += dropped;
-	return nb_tx_pkts;
+	return RTE_MIN((uint16_t)(nb_tx_pkts + dropped), nb_pkts);
+
 }
 
 int bnxt_tx_queue_start(struct rte_eth_dev *dev, uint16_t tx_queue_id)
-- 
2.47.3



More information about the dev mailing list