[PATCH net] net: bnxt: ring the doorbell when SW USO exits early
From: Joe Damato <hidden>
Date: 2026-08-18 21:15:49
Also in:
lkml, stable
Subsystem:
broadcom bnxt_en 50 gigabit ethernet driver, networking drivers, the rest · Maintainers:
Michael Chan, Pavan Chebbi, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
When a burst of packets is handed down to the driver, the driver defers
the doorbell to the end by setting txr->kick_pending = 1. The normal TX
path handles this, but the SW USO path can miss it if it returns
early.
If bnxt_sw_udp_gso_xmit runs but returns early with NETDEV_TX_BUSY and
txr->kick_pending was previously set to 1, then the TX queue can
stall because the driver wrote some BDs but never wrote the doorbell.
The device won't know to do the TX which would generate the completion
that would wake the queue back up.
Fix the success case of bnxt_sw_udp_gso_xmit by clearing
txr->kick_pending when writing the doorbell. When bnxt_sw_udp_gso_xmit
returns, check txr->kick_pending and write the doorbell if
bnxt_sw_udp_gso_xmit returned early and a doorbell is pending.
This TX queue stall was observed on a production system with a netdev TX
watchdog informing about the queue stall.
Fixes: cc5d90667db8 ("net: bnxt: Implement software USO")
Cc: stable@vger.kernel.org
Signed-off-by: Joe Damato <redacted>
---
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 17 +++++++++++++----
drivers/net/ethernet/broadcom/bnxt/bnxt.h | 2 ++
drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c | 4 +---
3 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index bc7b37cb74a7..b6cc755f7a04 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c@@ -459,8 +459,7 @@ u16 bnxt_xmit_get_cfa_action(struct sk_buff *skb) return md_dst->u.port_info.port_id; } -static void bnxt_txr_db_kick(struct bnxt *bp, struct bnxt_tx_ring_info *txr, - u16 prod) +void bnxt_txr_db_kick(struct bnxt *bp, struct bnxt_tx_ring_info *txr, u16 prod) { /* Sync BD data before updating doorbell */ wmb();
@@ -485,6 +484,7 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev) struct bnxt_sw_tx_bd *tx_buf; __le32 lflags = 0; skb_frag_t *frag; + netdev_tx_t ret; i = skb_get_queue_mapping(skb); if (unlikely(i >= bp->tx_nr_rings)) {
@@ -510,8 +510,17 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev) #endif if (skb_is_gso(skb) && (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4) && - !(bp->flags & BNXT_FLAG_UDP_GSO_CAP)) - return bnxt_sw_udp_gso_xmit(bp, txr, txq, skb); + !(bp->flags & BNXT_FLAG_UDP_GSO_CAP)) { + /* if the USO code exits early (for example, it returns + * NETDEV_TX_BUSY) make sure we ring any outstanding doorbells + * for any BDs in the ring before returning. + */ + ret = bnxt_sw_udp_gso_xmit(bp, txr, txq, skb); + if (txr->kick_pending) + bnxt_txr_db_kick(bp, txr, txr->tx_prod); + + return ret; + } free_size = bnxt_tx_avail(bp, txr); if (unlikely(free_size < skb_shinfo(skb)->nr_frags + 2)) {
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index dc8ec5e5733e..60fbe3cabcf9 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h@@ -3009,6 +3009,8 @@ int bnxt_reserve_rings(struct bnxt *bp, bool irq_re_init); void bnxt_tx_disable(struct bnxt *bp); void bnxt_tx_enable(struct bnxt *bp); u16 bnxt_xmit_get_cfa_action(struct sk_buff *skb); +void bnxt_txr_db_kick(struct bnxt *bp, struct bnxt_tx_ring_info *txr, + u16 prod); void bnxt_sched_reset_txr(struct bnxt *bp, struct bnxt_tx_ring_info *txr, u16 curr); void bnxt_report_link(struct bnxt *bp);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c
index f317f60414e8..3c58b1d94ac9 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c@@ -223,9 +223,7 @@ netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp, netdev_tx_sent_queue(txq, skb->len); WRITE_ONCE(txr->tx_prod, prod); - /* Sync BDs before doorbell */ - wmb(); - bnxt_db_write(bp, &txr->tx_db, prod); + bnxt_txr_db_kick(bp, txr, prod); if (unlikely(bnxt_tx_avail(bp, txr) <= bp->tx_wake_thresh)) netif_txq_try_stop(txq, bnxt_tx_avail(bp, txr),
base-commit: e2466392a0b8496000e12181cb1ee1535eb0da25 -- 2.53.0-Meta