[openwrt/openwrt] realtek: avoid interrupt storm on mass packet receive

LEDE Commits lede-commits at lists.infradead.org
Fri Jul 18 14:53:45 PDT 2025


hauke pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/963ee6ac3fc287328ce5a1e49d7f5910b060ec96

commit 963ee6ac3fc287328ce5a1e49d7f5910b060ec96
Author: Markus Stockhausen <markus.stockhausen at gmx.de>
AuthorDate: Thu Jul 10 16:00:46 2025 -0400

    realtek: avoid interrupt storm on mass packet receive
    
    RTL83xx devices have two types of receive interrupts for each of its
    8 rings. One for packet received and another for ring overflow. When
    the switch is flooded with incoming packets the receive handler will
    disable the packet receive notification but still keeps the overflow
    notification enabled. While the receive path "slowly" processes the
    received packets each new packet triggers the overflow IRQ again. The
    device becomes unresponsive and eventually produces messages like:
    
    [18441.709764] rcu: Stack dump where RCU GP kthread last ran:
    [18441.727892] Sending NMI from CPU 1 to CPUs 0:
    [18441.742300] NMI backtrace for cpu 0 skipped: idling at 0x8080e994
    [18415.251700] rcu: INFO: rcu_sched detected stalls on CPUs/tasks:
    [18415.271350] rcu:     0-...!: (0 ticks this GP) idle=d740/0/0x0 ...
    [18415.303046] rcu:     (detected by 1, t=6004 jiffies, g=230925, ...
    [18415.326095] Sending NMI from CPU 1 to CPUs 0:
    [18415.340540] NMI backtrace for cpu 0
    
    Fix this issue by always disabling receive and overflow interrupts at
    the same time.
    
    Test with hping3 --udp -p 5021 -d 1400 --flood 192.168.2.72
    
    Before (3sec run):
    [183260.324846] rtl838x-eth 1b00a300.ethernet eth0: RX buffer overrun: status 0x101, mask: 0x7ffeff
    [183260.340524] rtl838x-eth 1b00a300.ethernet eth0: RX buffer overrun: status 0x1, mask: 0x7ffeff
    [183260.345799] net_ratelimit: 489997 callbacks suppressed
    
    After (3 sec run):
    [  373.981479] rtl838x-eth 1b00a300.ethernet eth0: rx ring overrun: status 0x101, mask: 0x7fffff
    [  374.031118] rtl838x-eth 1b00a300.ethernet eth0: rx ring overrun: status 0x101, mask: 0x7fffff
    [  377.919996] net_ratelimit: 34 callbacks suppressed
    
    Signed-off-by: Markus Stockhausen <markus.stockhausen at gmx.de>
    Link: https://github.com/openwrt/openwrt/pull/19365
    Signed-off-by: Hauke Mehrtens <hauke at hauke-m.de>
---
 .../files-6.12/drivers/net/ethernet/rtl838x_eth.c  | 40 +++++++++-------------
 .../files-6.12/drivers/net/ethernet/rtl838x_eth.h  |  7 ++--
 2 files changed, 21 insertions(+), 26 deletions(-)

diff --git a/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.c
index d815356c9d..91577ac766 100644
--- a/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.c
+++ b/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.c
@@ -452,28 +452,25 @@ static irqreturn_t rtl83xx_net_irq(int irq, void *dev_id)
 	struct net_device *ndev = dev_id;
 	struct rtl838x_eth_priv *priv = netdev_priv(ndev);
 	u32 status = sw_r32(priv->r->dma_if_intr_sts);
+	unsigned long ring, rings;
 
-	netdev_dbg(ndev, "RX IRQ received: %08x\n", status);
+	netdev_dbg(ndev, "rx interrupt received, status %08x\n", status);
 
-	if ((status & RTL83XX_DMA_IF_INTR_STS_RX_RUN_OUT_MASK) && net_ratelimit())
-		netdev_warn(ndev, "RX buffer overrun: status 0x%x, mask: 0x%x\n",
-			   status, sw_r32(priv->r->dma_if_intr_msk));
+	if (status & RTL83XX_DMA_IF_INTR_RX_RUN_OUT_MASK)
+		if (net_ratelimit())
+			netdev_warn(ndev, "rx ring overrun, status 0x%08x, mask 0x%08x\n",
+				    status, sw_r32(priv->r->dma_if_intr_msk));
 
-	if (status & RTL83XX_DMA_IF_INTR_STS_RX_DONE_MASK) {
-		/* Disable rx interrupts */
-		sw_w32_mask(0xff00 & status, 0, priv->r->dma_if_intr_msk);
-		for (int i = 0; i < priv->rxrings; i++) {
-			if (status & BIT(i + 8)) {
-				pr_debug("Scheduling queue: %d\n", i);
-				napi_schedule(&priv->rx_qs[i].napi);
-			}
-		}
+	rings = FIELD_GET(RTL83XX_DMA_IF_INTR_RX_DONE_MASK, status);
+	for_each_set_bit(ring, &rings, priv->rxrings) {
+		netdev_dbg(ndev, "schedule rx ring %lu\n", ring);
+		sw_w32_mask(RTL83XX_DMA_IF_INTR_RX_MASK(ring), 0, priv->r->dma_if_intr_msk);
+		napi_schedule(&priv->rx_qs[ring].napi);
 	}
 
-	if ((status & RTL83XX_DMA_IF_INTR_STS_NOTIFY_MASK) && priv->family_id == RTL8390_FAMILY_ID)
+	if (status & RTL839X_DMA_IF_INTR_NOTIFY_MASK)
 		rtl839x_l2_notification_handler(priv);
 
-	/* Acknowledge all interrupts */
 	sw_w32(status, priv->r->dma_if_intr_sts);
 
 	return IRQ_HANDLED;
@@ -1353,25 +1350,22 @@ static int rtl838x_poll_rx(struct napi_struct *napi, int budget)
 {
 	struct rtl838x_rx_q *rx_q = container_of(napi, struct rtl838x_rx_q, napi);
 	struct rtl838x_eth_priv *priv = rx_q->priv;
+	int ring = rx_q->id;
 	int work_done = 0;
-	int r = rx_q->id;
-	int work;
 
 	while (work_done < budget) {
-		work = rtl838x_hw_receive(priv->netdev, r, budget - work_done);
+		int work = rtl838x_hw_receive(priv->netdev, ring, budget - work_done);
 		if (!work)
 			break;
 		work_done += work;
 	}
 
-	if (work_done < budget) {
-		napi_complete_done(napi, work_done);
-
-		/* Enable RX interrupt */
+	if (work_done < budget && napi_complete_done(napi, work_done)) {
+		/* Re-enable rx interrupts */
 		if (priv->family_id == RTL9300_FAMILY_ID || priv->family_id == RTL9310_FAMILY_ID)
 			sw_w32(0xffffffff, priv->r->dma_if_intr_rx_done_msk);
 		else
-			sw_w32_mask(0, 0xf00ff | BIT(r + 8), priv->r->dma_if_intr_msk);
+			sw_w32_mask(0, RTL83XX_DMA_IF_INTR_RX_MASK(ring), priv->r->dma_if_intr_msk);
 	}
 
 	return work_done;
diff --git a/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.h b/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.h
index 8d138cabb9..e899467d13 100644
--- a/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.h
+++ b/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.h
@@ -48,9 +48,10 @@
 #define RTL930X_MAC_FORCE_MODE_CTRL		(0xCA1C)
 #define RTL931X_MAC_FORCE_MODE_CTRL		(0x0dcc)
 
-#define RTL83XX_DMA_IF_INTR_STS_NOTIFY_MASK	GENMASK(22, 20)
-#define RTL83XX_DMA_IF_INTR_STS_RX_DONE_MASK	GENMASK(15, 8)
-#define RTL83XX_DMA_IF_INTR_STS_RX_RUN_OUT_MASK	GENMASK(7, 0)
+#define RTL839X_DMA_IF_INTR_NOTIFY_MASK		GENMASK(22, 20)
+#define RTL83XX_DMA_IF_INTR_RX_DONE_MASK	GENMASK(15, 8)
+#define RTL83XX_DMA_IF_INTR_RX_RUN_OUT_MASK	GENMASK(7, 0)
+#define RTL83XX_DMA_IF_INTR_RX_MASK(ring)	(BIT(ring) | BIT(ring + 8))
 
 /* MAC address settings */
 #define RTL838X_MAC				(0xa9ec)




More information about the lede-commits mailing list