mirror of
https://github.com/lukaszraczylo/talos-builder.git
synced 2026-07-16 06:34:05 +00:00
153 lines
5.4 KiB
Diff
153 lines
5.4 KiB
Diff
From 0000000000000000000000000000000000000003 Mon Sep 17 00:00:00 2001
|
|
From: Lukasz Raczylo <lukasz@raczylo.com>
|
|
Date: Fri, 24 Apr 2026 00:00:00 +0000
|
|
Subject: [PATCH 3/3] net: macb: add TX stall watchdog as defence-in-depth
|
|
safety net
|
|
|
|
Patches 1/3 and 2/3 address two candidate races that could lead
|
|
to a TCOMP completion being missed on PCIe-attached macb
|
|
instances. This patch adds a defence-in-depth safety net, in
|
|
case a further race remains that we have not identified.
|
|
|
|
The watchdog is a per-queue delayed_work that runs once per
|
|
second. It snapshots queue->tx_tail; if the ring is non-empty
|
|
(queue->tx_head != queue->tx_tail) and tx_tail has not advanced
|
|
since the previous tick, it calls macb_tx_restart().
|
|
|
|
No new recovery logic is introduced. macb_tx_restart() already
|
|
exists in this file, is correctly locked (tx_ptr_lock, bp->lock),
|
|
and verifies that the hardware's TBQP is behind the driver's
|
|
head index before re-asserting TSTART. On a healthy ring it is
|
|
a no-op at the hardware level; the watchdog only supplies the
|
|
missing trigger.
|
|
|
|
On a healthy queue the per-tick cost is one spin_lock_irqsave()
|
|
/ spin_unlock_irqrestore() and one branch. The delayed_work is
|
|
only scheduled between macb_open() and macb_close(), and is
|
|
cancelled synchronously on close.
|
|
|
|
Context for submission: on our 24-node Raspberry Pi 5 fleet,
|
|
before this series, an out-of-band user-space watchdog
|
|
(monitoring tx_packets from /sys/class/net/.../statistics and
|
|
toggling the link down/up when it froze) was required to keep
|
|
nodes usable. We include this kernel-side watchdog as a cleaner
|
|
in-kernel equivalent for any residual stall that patches 1 and
|
|
2 do not cover. We are willing to drop this patch if the view
|
|
is that 1 and 2 should stand alone.
|
|
|
|
Link: https://github.com/cilium/cilium/issues/43198
|
|
Link: https://bugs.launchpad.net/ubuntu/+source/linux-raspi/+bug/2133877
|
|
Signed-off-by: Lukasz Raczylo <lukasz@raczylo.com>
|
|
---
|
|
drivers/net/ethernet/cadence/macb.h | 4 ++
|
|
drivers/net/ethernet/cadence/macb_main.c | 63 ++++++++++++++++++++++++
|
|
2 files changed, 67 insertions(+)
|
|
|
|
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
|
|
--- a/drivers/net/ethernet/cadence/macb.h
|
|
+++ b/drivers/net/ethernet/cadence/macb.h
|
|
@@ -1294,6 +1294,11 @@
|
|
struct work_struct tx_error_task;
|
|
bool txubr_pending;
|
|
bool tx_pending;
|
|
+
|
|
+ /* TX stall watchdog -- see macb_tx_stall_watchdog() in macb_main.c */
|
|
+ struct delayed_work tx_stall_watchdog_work;
|
|
+ unsigned int tx_stall_last_tail;
|
|
+
|
|
struct napi_struct napi_tx;
|
|
|
|
dma_addr_t rx_ring_dma;
|
|
diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
|
|
--- a/drivers/net/ethernet/cadence/macb_main.c
|
|
+++ b/drivers/net/ethernet/cadence/macb_main.c
|
|
@@ -2030,6 +2030,59 @@
|
|
return work_done;
|
|
}
|
|
|
|
+#define MACB_TX_STALL_INTERVAL_MS 1000
|
|
+
|
|
+/*
|
|
+ * TX stall watchdog.
|
|
+ *
|
|
+ * Defence-in-depth against lost TCOMP interrupts. macb already has a
|
|
+ * recovery chain (tx_pending -> txubr_pending -> macb_tx_restart())
|
|
+ * that fires on TCOMP; if TCOMP itself is lost the TX ring stalls
|
|
+ * silently until something else kicks TSTART. This watchdog runs
|
|
+ * once per second per queue, snapshots tx_tail, and calls
|
|
+ * macb_tx_restart() if the ring is non-empty and tx_tail has not
|
|
+ * advanced since the previous tick.
|
|
+ *
|
|
+ * macb_tx_restart() already checks the hardware's TBQP against the
|
|
+ * driver's head index before re-asserting TSTART, so on a healthy
|
|
+ * ring this is a no-op at the hardware level. The watchdog only
|
|
+ * adds the missing trigger.
|
|
+ */
|
|
+static void macb_tx_stall_watchdog(struct work_struct *work)
|
|
+{
|
|
+ struct macb_queue *queue = container_of(to_delayed_work(work),
|
|
+ struct macb_queue,
|
|
+ tx_stall_watchdog_work);
|
|
+ struct macb *bp = queue->bp;
|
|
+ unsigned int cur_tail, cur_head;
|
|
+ bool stalled = false;
|
|
+ unsigned long flags;
|
|
+
|
|
+ if (!netif_running(bp->dev))
|
|
+ return;
|
|
+
|
|
+ spin_lock_irqsave(&queue->tx_ptr_lock, flags);
|
|
+ cur_tail = queue->tx_tail;
|
|
+ cur_head = queue->tx_head;
|
|
+ if (cur_head != cur_tail &&
|
|
+ cur_tail == queue->tx_stall_last_tail)
|
|
+ stalled = true;
|
|
+ else
|
|
+ queue->tx_stall_last_tail = cur_tail;
|
|
+ spin_unlock_irqrestore(&queue->tx_ptr_lock, flags);
|
|
+
|
|
+ if (stalled) {
|
|
+ netdev_warn_once(bp->dev,
|
|
+ "TX stall detected on queue %u (tail=%u head=%u); re-kicking TSTART\n",
|
|
+ (unsigned int)(queue - bp->queues),
|
|
+ cur_tail, cur_head);
|
|
+ macb_tx_restart(queue);
|
|
+ }
|
|
+
|
|
+ schedule_delayed_work(&queue->tx_stall_watchdog_work,
|
|
+ msecs_to_jiffies(MACB_TX_STALL_INTERVAL_MS));
|
|
+}
|
|
+
|
|
static void macb_hresp_error_task(struct work_struct *work)
|
|
{
|
|
struct macb *bp = from_work(bp, work, hresp_err_bh_work);
|
|
@@ -3297,6 +3350,9 @@
|
|
for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
|
|
napi_enable(&queue->napi_rx);
|
|
napi_enable(&queue->napi_tx);
|
|
+ queue->tx_stall_last_tail = queue->tx_tail;
|
|
+ schedule_delayed_work(&queue->tx_stall_watchdog_work,
|
|
+ msecs_to_jiffies(MACB_TX_STALL_INTERVAL_MS));
|
|
}
|
|
|
|
macb_init_hw(bp);
|
|
@@ -3343,6 +3399,7 @@
|
|
for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
|
|
napi_disable(&queue->napi_rx);
|
|
napi_disable(&queue->napi_tx);
|
|
+ cancel_delayed_work_sync(&queue->tx_stall_watchdog_work);
|
|
netdev_tx_reset_queue(netdev_get_tx_queue(dev, q));
|
|
}
|
|
|
|
@@ -4941,6 +4998,8 @@
|
|
}
|
|
|
|
INIT_WORK(&queue->tx_error_task, macb_tx_error_task);
|
|
+ INIT_DELAYED_WORK(&queue->tx_stall_watchdog_work,
|
|
+ macb_tx_stall_watchdog);
|
|
q++;
|
|
}
|
|
|
|
--
|
|
2.44.0
|