From: Denis Yarkovoy Date: Sun, 12 Jul 2026 07:34:57 +0000 (+0300) Subject: ipq40xx: ipqess: recover TX queue on watchdog timeout X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c85adddd8d12309f4b7561bd8a71350eb1c73583;p=thirdparty%2Fopenwrt.git ipq40xx: ipqess: recover TX queue on watchdog timeout The current ipqess TX timeout handler only logs the timeout. If TX completion interrupts are lost or left masked, descriptors can remain pending indefinitely and the master Ethernet device stays wedged until a reboot. Log the TX ring indexes and interrupt state to make future failures useful, then force the TX completion NAPI path to run and re-kick the TX producer index. This gives the driver a chance to reclaim pending TX descriptors and wake the queue instead of reporting the same watchdog forever. Signed-off-by: Denis Yarkovoy Link: https://github.com/openwrt/openwrt/pull/24122 Signed-off-by: Jonas Jelonek --- diff --git a/target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/ipqess.c b/target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/ipqess.c index b3dfd6f1ae3..78c48c0bb93 100644 --- a/target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/ipqess.c +++ b/target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/ipqess.c @@ -894,7 +894,38 @@ static int ipqess_set_mac_address(struct net_device *netdev, void *p) static void ipqess_tx_timeout(struct net_device *netdev, unsigned int txq_id) { - netdev_err(netdev, "TX timeout on queue %d\n", txq_id); + struct ipqess_tx_ring *tr; + struct ipqess *ess; + u32 sw_cons; + u32 tx_imr; + u32 tx_isr; + u32 cons; + u32 prod; + u32 idx; + + ess = netdev_priv(netdev); + tr = &ess->tx_ring[txq_id]; + + idx = ipqess_r32(ess, IPQESS_REG_TPD_IDX_Q(tr->idx)); + prod = (idx >> IPQESS_TPD_PROD_IDX_SHIFT) & IPQESS_TPD_PROD_IDX_MASK; + cons = (idx >> IPQESS_TPD_CONS_IDX_SHIFT) & IPQESS_TPD_CONS_IDX_MASK; + sw_cons = ipqess_r32(ess, IPQESS_REG_TX_SW_CONS_IDX_Q(tr->idx)); + tx_isr = ipqess_r32(ess, IPQESS_REG_TX_ISR); + tx_imr = ipqess_r32(ess, IPQESS_REG_TX_INT_MASK_Q(tr->idx)); + + netdev_warn(netdev, + "TX timeout on queue %d head=%u tail=%u hw_prod=%u hw_cons=%u sw_cons=%u tx_isr=%08x tx_imr=%08x\n", + tr->idx, tr->head, tr->tail, prod, cons, sw_cons, tx_isr, tx_imr); + + /* A lost or masked TX completion interrupt leaves descriptors pending + * forever. Force the TX completion path to run and re-kick the producer. + */ + if (napi_schedule_prep(&tr->napi_tx)) { + __napi_schedule(&tr->napi_tx); + ipqess_w32(ess, IPQESS_REG_TX_INT_MASK_Q(tr->idx), 0x0); + } + + ipqess_kick_tx(tr); } static const struct net_device_ops ipqess_axi_netdev_ops = { diff --git a/target/linux/ipq40xx/patches-6.18/702-net-ipqess-Add-out-of-band-DSA-tagging-support.patch b/target/linux/ipq40xx/patches-6.18/702-net-ipqess-Add-out-of-band-DSA-tagging-support.patch index 1969cb386e7..406817f9116 100644 --- a/target/linux/ipq40xx/patches-6.18/702-net-ipqess-Add-out-of-band-DSA-tagging-support.patch +++ b/target/linux/ipq40xx/patches-6.18/702-net-ipqess-Add-out-of-band-DSA-tagging-support.patch @@ -43,15 +43,15 @@ Signed-off-by: Matthias Schiffer #include #include #include -@@ -23,6 +24,7 @@ - #include +@@ -22,6 +23,7 @@ + #include #include #include +#include #include #include "ipqess.h" -@@ -328,6 +330,7 @@ static int ipqess_rx_poll(struct ipqess_ +@@ -327,6 +329,7 @@ static int ipqess_rx_poll(struct ipqess_ tail &= IPQESS_RFD_CONS_IDX_MASK; while (done < budget) { @@ -59,7 +59,7 @@ Signed-off-by: Matthias Schiffer struct ipqess_rx_desc *rd; struct sk_buff *skb; -@@ -407,6 +410,12 @@ static int ipqess_rx_poll(struct ipqess_ +@@ -406,6 +409,12 @@ static int ipqess_rx_poll(struct ipqess_ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021AD), le16_to_cpu(rd->rrd4)); @@ -72,7 +72,7 @@ Signed-off-by: Matthias Schiffer napi_gro_receive(&rx_ring->napi_rx, skb); rx_ring->ess->stats.rx_packets++; -@@ -702,6 +711,26 @@ static void ipqess_rollback_tx(struct ip +@@ -701,6 +710,26 @@ static void ipqess_rollback_tx(struct ip tx_ring->head = start_index; } @@ -99,7 +99,7 @@ Signed-off-by: Matthias Schiffer static int ipqess_tx_map_and_fill(struct ipqess_tx_ring *tx_ring, struct sk_buff *skb) { -@@ -712,6 +741,8 @@ static int ipqess_tx_map_and_fill(struct +@@ -711,6 +740,8 @@ static int ipqess_tx_map_and_fill(struct u16 len; int i; @@ -108,7 +108,7 @@ Signed-off-by: Matthias Schiffer if (skb_is_gso(skb)) { if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) { lso_word1 |= IPQESS_TPD_IPV4_EN; -@@ -910,6 +941,33 @@ static const struct net_device_ops ipqes +@@ -940,6 +971,33 @@ static const struct net_device_ops ipqes .ndo_tx_timeout = ipqess_tx_timeout, }; @@ -142,7 +142,7 @@ Signed-off-by: Matthias Schiffer static void ipqess_hw_stop(struct ipqess *ess) { int i; -@@ -1186,12 +1244,19 @@ static int ipqess_axi_probe(struct platf +@@ -1216,12 +1274,19 @@ static int ipqess_axi_probe(struct platf netif_napi_add(netdev, &ess->rx_ring[i].napi_rx, ipqess_rx_napi); } diff --git a/target/linux/ipq40xx/patches-6.18/703-net-qualcomm-ipqess-release-IRQ-s-on-network-device-.patch b/target/linux/ipq40xx/patches-6.18/703-net-qualcomm-ipqess-release-IRQ-s-on-network-device-.patch index d033928b49d..1ad96ecd87b 100644 --- a/target/linux/ipq40xx/patches-6.18/703-net-qualcomm-ipqess-release-IRQ-s-on-network-device-.patch +++ b/target/linux/ipq40xx/patches-6.18/703-net-qualcomm-ipqess-release-IRQ-s-on-network-device-.patch @@ -50,7 +50,7 @@ Signed-off-by: Robert Marko --- a/drivers/net/ethernet/qualcomm/ipqess/ipqess.c +++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess.c -@@ -632,9 +632,22 @@ static int ipqess_stop(struct net_device +@@ -631,9 +631,22 @@ static int ipqess_stop(struct net_device netif_tx_stop_all_queues(netdev); phylink_stop(ess->phylink); ipqess_irq_disable(ess); diff --git a/target/linux/ipq40xx/patches-6.18/704-net-qualcomm-ipqess-enable-threaded-NAPI-by-default.patch b/target/linux/ipq40xx/patches-6.18/704-net-qualcomm-ipqess-enable-threaded-NAPI-by-default.patch index 1722a35dbe5..42976357fa6 100644 --- a/target/linux/ipq40xx/patches-6.18/704-net-qualcomm-ipqess-enable-threaded-NAPI-by-default.patch +++ b/target/linux/ipq40xx/patches-6.18/704-net-qualcomm-ipqess-enable-threaded-NAPI-by-default.patch @@ -16,7 +16,7 @@ Signed-off-by: Robert Marko --- a/drivers/net/ethernet/qualcomm/ipqess/ipqess.c +++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess.c -@@ -531,9 +531,9 @@ static irqreturn_t ipqess_interrupt_tx(i +@@ -530,9 +530,9 @@ static irqreturn_t ipqess_interrupt_tx(i struct ipqess_tx_ring *tx_ring = (struct ipqess_tx_ring *)priv; if (likely(napi_schedule_prep(&tx_ring->napi_tx))) { @@ -27,7 +27,7 @@ Signed-off-by: Robert Marko } return IRQ_HANDLED; -@@ -544,9 +544,9 @@ static irqreturn_t ipqess_interrupt_rx(i +@@ -543,9 +543,9 @@ static irqreturn_t ipqess_interrupt_rx(i struct ipqess_rx_ring *rx_ring = (struct ipqess_rx_ring *)priv; if (likely(napi_schedule_prep(&rx_ring->napi_rx))) { @@ -38,7 +38,7 @@ Signed-off-by: Robert Marko } return IRQ_HANDLED; -@@ -1266,6 +1266,8 @@ static int ipqess_axi_probe(struct platf +@@ -1296,6 +1296,8 @@ static int ipqess_axi_probe(struct platf if (err) goto err_notifier_unregister; diff --git a/target/linux/ipq40xx/patches-6.18/711-net-qualcomm-ipqess-fix-TX-timeout-errors.patch b/target/linux/ipq40xx/patches-6.18/711-net-qualcomm-ipqess-fix-TX-timeout-errors.patch index 535ad8987fc..149208aa69d 100644 --- a/target/linux/ipq40xx/patches-6.18/711-net-qualcomm-ipqess-fix-TX-timeout-errors.patch +++ b/target/linux/ipq40xx/patches-6.18/711-net-qualcomm-ipqess-fix-TX-timeout-errors.patch @@ -35,7 +35,7 @@ Signed-off-by: Christian Marangi --- a/drivers/net/ethernet/qualcomm/ipqess/ipqess.c +++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess.c -@@ -454,13 +454,22 @@ static int ipqess_tx_complete(struct ipq +@@ -453,13 +453,22 @@ static int ipqess_tx_complete(struct ipq tail >>= IPQESS_TPD_CONS_IDX_SHIFT; tail &= IPQESS_TPD_CONS_IDX_MASK;