mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net] net: stmmac: Clear DMA errors for multi-channel interrupts
@ 2026-10-30 10:22 Maxime Chevallier
  2026-10-30 12:32 ` Lorenzo Bianconi
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Maxime Chevallier @ 2026-10-30 10:22 UTC (permalink / raw)
  To: Andrew Lunn, Jakub Kicinski, davem, Eric Dumazet, Paolo Abeni,
	Simon Horman, Maxime Coquelin, Alexandre Torgue, Russell King,
	Lorenzo Bianconi, Ovidiu Panait
  Cc: Maxime Chevallier, thomas.petazzoni, Alexis Lothoré,
	netdev, linux-kernel, linux-arm-kernel

The Multi-MSI interrupt handler doesn't clear the DMA error interrupts,
and this leads to interrupt storms when one of these interrupts starts
firing :

 - The RBU (rx buff unav) which triggers under high traffic load

   This triggers easily with iperf3 and small udp packets, and shows
   when running the netpoll kselftest

 - The RPS (rx process stop) which triggers when we stop the DMA RX.

   This one shows when restarting the RX logic without doing a down/up
   cycle, like when attaching an XDP program. The XSK kselftests
   triggers it.

Let's make sure we clear these interrupts for multi-MSI cases. It's
already properly done for the single-chan interrupt, and for that
introduce an "ERR" DMA dir, as we don't want to touch the RI/TI bits
when handling these interrupts.

To avoid duplication, the DMA TX error handling is factored out in a
dedicated helper (in the same commit to ease backporting).

Both of the above storms have been found on dwmac-motorcomm (YT8601),
and are triggered by the xdp.py, xsk_reconfig.py, xdp_features.py,
ring_reconfig.py and netpoll_basic kernel selftests.

This seems to only affect dwmac4, there's no multi-msi mode on
dwmac1000, and on dwxgmac these interrupts are masked as of today.

Fixes: 6ccf12ae111e ("net: stmmac: use interrupt mode INTM=1 for multi-MSI")
Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
---
 drivers/net/ethernet/stmicro/stmmac/common.h  |  1 +
 .../net/ethernet/stmicro/stmmac/dwmac4_dma.h  |  5 ++
 .../net/ethernet/stmicro/stmmac/dwmac4_lib.c  |  2 +
 .../net/ethernet/stmicro/stmmac/stmmac_main.c | 46 +++++++++++++------
 4 files changed, 40 insertions(+), 14 deletions(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
index c74bc7c9750e..a972bee66504 100644
--- a/drivers/net/ethernet/stmicro/stmmac/common.h
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
@@ -382,6 +382,7 @@ enum dma_irq_dir {
 	DMA_DIR_RX = 0x1,
 	DMA_DIR_TX = 0x2,
 	DMA_DIR_RXTX = 0x3,
+	DMA_DIR_ERR = 0x4,
 };
 
 enum request_irq_err {
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
index 43b036d4e95b..a7caac3c98e6 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
@@ -173,6 +173,11 @@ static inline u32 dma_chanx_base_addr(const struct dwmac4_addrs *addrs,
 					 DMA_CHAN_STATUS_TI | \
 					 DMA_CHAN_STATUS_MSK_COMMON)
 
+#define DMA_CHAN_STATUS_MSK_ERR		(DMA_CHAN_STATUS_AIS | \
+					 DMA_CHAN_STATUS_FBE | \
+					 DMA_CHAN_STATUS_RPS | \
+					 DMA_CHAN_STATUS_RBU)
+
 int dwmac4_dma_reset(void __iomem *ioaddr);
 void dwmac4_enable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr,
 			   u32 chan, bool rx, bool tx);
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
index a0249715fafa..02aa1a3dd1e2 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
@@ -150,6 +150,8 @@ int dwmac4_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr,
 		intr_status &= DMA_CHAN_STATUS_MSK_RX;
 	else if (dir == DMA_DIR_TX)
 		intr_status &= DMA_CHAN_STATUS_MSK_TX;
+	else if (dir == DMA_DIR_ERR)
+		intr_status &= DMA_CHAN_STATUS_MSK_ERR;
 
 	/* ABNORMAL interrupts */
 	if (unlikely(intr_status & DMA_CHAN_STATUS_AIS)) {
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 9ded31150fbe..2068b57df8d5 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -3141,6 +3141,16 @@ static int stmmac_napi_check(struct stmmac_priv *priv, u32 chan, u32 dir)
 	return status;
 }
 
+static void stmmac_dma_tx_error(struct stmmac_priv *priv, u32 chan, int status)
+{
+	if (unlikely(status & tx_hard_error_bump_tc)) {
+		/* Try to bump up the dma threshold on this failure */
+		stmmac_bump_dma_threshold(priv, chan);
+	} else if (unlikely(status == tx_hard_error)) {
+		stmmac_tx_err(priv, chan);
+	}
+}
+
 /**
  * stmmac_dma_interrupt - DMA ISR
  * @priv: driver private structure
@@ -3165,13 +3175,24 @@ static void stmmac_dma_interrupt(struct stmmac_priv *priv)
 		status[chan] = stmmac_napi_check(priv, chan,
 						 DMA_DIR_RXTX);
 
-	for (chan = 0; chan < tx_channel_count; chan++) {
-		if (unlikely(status[chan] & tx_hard_error_bump_tc)) {
-			/* Try to bump up the dma threshold on this failure */
-			stmmac_bump_dma_threshold(priv, chan);
-		} else if (unlikely(status[chan] == tx_hard_error)) {
-			stmmac_tx_err(priv, chan);
-		}
+	for (chan = 0; chan < tx_channel_count; chan++)
+		stmmac_dma_tx_error(priv, chan, status[chan]);
+}
+
+static void stmmac_dma_err_interrupt(struct stmmac_priv *priv)
+{
+	u8 tx_channel_count = priv->plat->tx_queues_to_use;
+	u8 channels_to_check = max(priv->plat->rx_queues_to_use,
+				   tx_channel_count);
+	int status;
+	u8 chan;
+
+	for (chan = 0; chan < channels_to_check; chan++) {
+		status = stmmac_dma_interrupt_status(priv, priv->ioaddr,
+						     &priv->xstats, chan,
+						     DMA_DIR_ERR);
+		if (chan < tx_channel_count)
+			stmmac_dma_tx_error(priv, chan, status);
 	}
 }
 
@@ -6466,6 +6487,9 @@ static irqreturn_t stmmac_mac_interrupt(int irq, void *dev_id)
 	/* To handle Common interrupts */
 	stmmac_common_interrupt(priv);
 
+	if (priv->plat->core_type == DWMAC_CORE_GMAC4)
+		stmmac_dma_err_interrupt(priv);
+
 	return IRQ_HANDLED;
 }
 
@@ -6500,13 +6524,7 @@ static irqreturn_t stmmac_msi_intr_tx(int irq, void *data)
 		return IRQ_HANDLED;
 
 	status = stmmac_napi_check(priv, chan, DMA_DIR_TX);
-
-	if (unlikely(status & tx_hard_error_bump_tc)) {
-		/* Try to bump up the dma threshold on this failure */
-		stmmac_bump_dma_threshold(priv, chan);
-	} else if (unlikely(status == tx_hard_error)) {
-		stmmac_tx_err(priv, chan);
-	}
+	stmmac_dma_tx_error(priv, chan, status);
 
 	return IRQ_HANDLED;
 }
-- 
2.56.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net] net: stmmac: Clear DMA errors for multi-channel interrupts
  2026-10-30 10:22 [PATCH net] net: stmmac: Clear DMA errors for multi-channel interrupts Maxime Chevallier
@ 2026-10-30 12:32 ` Lorenzo Bianconi
  2026-10-30 19:33 ` Nicolai Buchwitz
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Lorenzo Bianconi @ 2026-10-30 12:32 UTC (permalink / raw)
  To: Maxime Chevallier
  Cc: Andrew Lunn, Jakub Kicinski, davem, Eric Dumazet, Paolo Abeni,
	Simon Horman, Maxime Coquelin, Alexandre Torgue, Russell King,
	Ovidiu Panait, thomas.petazzoni, Alexis Lothoré,
	netdev, linux-kernel, linux-arm-kernel

[-- Attachment #1: Type: text/plain, Size: 6572 bytes --]

> The Multi-MSI interrupt handler doesn't clear the DMA error interrupts,
> and this leads to interrupt storms when one of these interrupts starts
> firing :
> 
>  - The RBU (rx buff unav) which triggers under high traffic load
> 
>    This triggers easily with iperf3 and small udp packets, and shows
>    when running the netpoll kselftest
> 
>  - The RPS (rx process stop) which triggers when we stop the DMA RX.
> 
>    This one shows when restarting the RX logic without doing a down/up
>    cycle, like when attaching an XDP program. The XSK kselftests
>    triggers it.
> 
> Let's make sure we clear these interrupts for multi-MSI cases. It's
> already properly done for the single-chan interrupt, and for that
> introduce an "ERR" DMA dir, as we don't want to touch the RI/TI bits
> when handling these interrupts.
> 
> To avoid duplication, the DMA TX error handling is factored out in a
> dedicated helper (in the same commit to ease backporting).
> 
> Both of the above storms have been found on dwmac-motorcomm (YT8601),
> and are triggered by the xdp.py, xsk_reconfig.py, xdp_features.py,
> ring_reconfig.py and netpoll_basic kernel selftests.
> 
> This seems to only affect dwmac4, there's no multi-msi mode on
> dwmac1000, and on dwxgmac these interrupts are masked as of today.
> 
> Fixes: 6ccf12ae111e ("net: stmmac: use interrupt mode INTM=1 for multi-MSI")
> Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>

Reviewed-by: Lorenzo Bianconi <lorenzo.bianconi@oss.qualcomm.com>

> ---
>  drivers/net/ethernet/stmicro/stmmac/common.h  |  1 +
>  .../net/ethernet/stmicro/stmmac/dwmac4_dma.h  |  5 ++
>  .../net/ethernet/stmicro/stmmac/dwmac4_lib.c  |  2 +
>  .../net/ethernet/stmicro/stmmac/stmmac_main.c | 46 +++++++++++++------
>  4 files changed, 40 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
> index c74bc7c9750e..a972bee66504 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/common.h
> +++ b/drivers/net/ethernet/stmicro/stmmac/common.h
> @@ -382,6 +382,7 @@ enum dma_irq_dir {
>  	DMA_DIR_RX = 0x1,
>  	DMA_DIR_TX = 0x2,
>  	DMA_DIR_RXTX = 0x3,
> +	DMA_DIR_ERR = 0x4,
>  };
>  
>  enum request_irq_err {
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
> index 43b036d4e95b..a7caac3c98e6 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
> @@ -173,6 +173,11 @@ static inline u32 dma_chanx_base_addr(const struct dwmac4_addrs *addrs,
>  					 DMA_CHAN_STATUS_TI | \
>  					 DMA_CHAN_STATUS_MSK_COMMON)
>  
> +#define DMA_CHAN_STATUS_MSK_ERR		(DMA_CHAN_STATUS_AIS | \
> +					 DMA_CHAN_STATUS_FBE | \
> +					 DMA_CHAN_STATUS_RPS | \
> +					 DMA_CHAN_STATUS_RBU)
> +
>  int dwmac4_dma_reset(void __iomem *ioaddr);
>  void dwmac4_enable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr,
>  			   u32 chan, bool rx, bool tx);
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
> index a0249715fafa..02aa1a3dd1e2 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
> @@ -150,6 +150,8 @@ int dwmac4_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr,
>  		intr_status &= DMA_CHAN_STATUS_MSK_RX;
>  	else if (dir == DMA_DIR_TX)
>  		intr_status &= DMA_CHAN_STATUS_MSK_TX;
> +	else if (dir == DMA_DIR_ERR)
> +		intr_status &= DMA_CHAN_STATUS_MSK_ERR;
>  
>  	/* ABNORMAL interrupts */
>  	if (unlikely(intr_status & DMA_CHAN_STATUS_AIS)) {
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> index 9ded31150fbe..2068b57df8d5 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> @@ -3141,6 +3141,16 @@ static int stmmac_napi_check(struct stmmac_priv *priv, u32 chan, u32 dir)
>  	return status;
>  }
>  
> +static void stmmac_dma_tx_error(struct stmmac_priv *priv, u32 chan, int status)
> +{
> +	if (unlikely(status & tx_hard_error_bump_tc)) {
> +		/* Try to bump up the dma threshold on this failure */
> +		stmmac_bump_dma_threshold(priv, chan);
> +	} else if (unlikely(status == tx_hard_error)) {
> +		stmmac_tx_err(priv, chan);
> +	}
> +}
> +
>  /**
>   * stmmac_dma_interrupt - DMA ISR
>   * @priv: driver private structure
> @@ -3165,13 +3175,24 @@ static void stmmac_dma_interrupt(struct stmmac_priv *priv)
>  		status[chan] = stmmac_napi_check(priv, chan,
>  						 DMA_DIR_RXTX);
>  
> -	for (chan = 0; chan < tx_channel_count; chan++) {
> -		if (unlikely(status[chan] & tx_hard_error_bump_tc)) {
> -			/* Try to bump up the dma threshold on this failure */
> -			stmmac_bump_dma_threshold(priv, chan);
> -		} else if (unlikely(status[chan] == tx_hard_error)) {
> -			stmmac_tx_err(priv, chan);
> -		}
> +	for (chan = 0; chan < tx_channel_count; chan++)
> +		stmmac_dma_tx_error(priv, chan, status[chan]);
> +}
> +
> +static void stmmac_dma_err_interrupt(struct stmmac_priv *priv)
> +{
> +	u8 tx_channel_count = priv->plat->tx_queues_to_use;
> +	u8 channels_to_check = max(priv->plat->rx_queues_to_use,
> +				   tx_channel_count);
> +	int status;
> +	u8 chan;
> +
> +	for (chan = 0; chan < channels_to_check; chan++) {
> +		status = stmmac_dma_interrupt_status(priv, priv->ioaddr,
> +						     &priv->xstats, chan,
> +						     DMA_DIR_ERR);
> +		if (chan < tx_channel_count)
> +			stmmac_dma_tx_error(priv, chan, status);
>  	}
>  }
>  
> @@ -6466,6 +6487,9 @@ static irqreturn_t stmmac_mac_interrupt(int irq, void *dev_id)
>  	/* To handle Common interrupts */
>  	stmmac_common_interrupt(priv);
>  
> +	if (priv->plat->core_type == DWMAC_CORE_GMAC4)
> +		stmmac_dma_err_interrupt(priv);
> +
>  	return IRQ_HANDLED;
>  }
>  
> @@ -6500,13 +6524,7 @@ static irqreturn_t stmmac_msi_intr_tx(int irq, void *data)
>  		return IRQ_HANDLED;
>  
>  	status = stmmac_napi_check(priv, chan, DMA_DIR_TX);
> -
> -	if (unlikely(status & tx_hard_error_bump_tc)) {
> -		/* Try to bump up the dma threshold on this failure */
> -		stmmac_bump_dma_threshold(priv, chan);
> -	} else if (unlikely(status == tx_hard_error)) {
> -		stmmac_tx_err(priv, chan);
> -	}
> +	stmmac_dma_tx_error(priv, chan, status);
>  
>  	return IRQ_HANDLED;
>  }
> -- 
> 2.56.0
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net] net: stmmac: Clear DMA errors for multi-channel interrupts
  2026-10-30 10:22 [PATCH net] net: stmmac: Clear DMA errors for multi-channel interrupts Maxime Chevallier
  2026-10-30 12:32 ` Lorenzo Bianconi
@ 2026-10-30 19:33 ` Nicolai Buchwitz
  2026-11-02  8:45 ` Magnus Leatti
  2026-11-02 10:23 ` netdev-bot+sashiko
  3 siblings, 0 replies; 6+ messages in thread
From: Nicolai Buchwitz @ 2026-10-30 19:33 UTC (permalink / raw)
  To: Maxime Chevallier, Andrew Lunn, Jakub Kicinski, davem,
	Eric Dumazet, Paolo Abeni, Simon Horman, Maxime Coquelin,
	Alexandre Torgue, Russell King, Lorenzo Bianconi, Ovidiu Panait
  Cc: thomas.petazzoni, Alexis Lothoré,
	netdev, linux-kernel, linux-arm-kernel

On Fri, 2026-10-30 at 11:22 +0100, Maxime Chevallier wrote:
> The Multi-MSI interrupt handler doesn't clear the DMA error
> interrupts,
> and this leads to interrupt storms when one of these interrupts
> starts
> firing :
> 
>  - The RBU (rx buff unav) which triggers under high traffic load
> 
>    This triggers easily with iperf3 and small udp packets, and shows
>    when running the netpoll kselftest
> 
>  - The RPS (rx process stop) which triggers when we stop the DMA RX.
> 
>    This one shows when restarting the RX logic without doing a
> down/up
>    cycle, like when attaching an XDP program. The XSK kselftests
>    triggers it.
> 
> Let's make sure we clear these interrupts for multi-MSI cases. It's
> already properly done for the single-chan interrupt, and for that
> introduce an "ERR" DMA dir, as we don't want to touch the RI/TI bits
> when handling these interrupts.
> 
> To avoid duplication, the DMA TX error handling is factored out in a
> dedicated helper (in the same commit to ease backporting).
> 
> Both of the above storms have been found on dwmac-motorcomm (YT8601),
> and are triggered by the xdp.py, xsk_reconfig.py, xdp_features.py,
> ring_reconfig.py and netpoll_basic kernel selftests.
> 
> This seems to only affect dwmac4, there's no multi-msi mode on
> dwmac1000, and on dwxgmac these interrupts are masked as of today.
> 
> Fixes: 6ccf12ae111e ("net: stmmac: use interrupt mode INTM=1 for
> multi-MSI")
> Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
> ---

> [..,]

Reviewed-by: Nicolai Buchwitz <nb@tipi-net.de>

thanks,
Nicolai

-- 
Nicolai Buchwitz <nb@tipi-net.de>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net] net: stmmac: Clear DMA errors for multi-channel interrupts
  2026-10-30 10:22 [PATCH net] net: stmmac: Clear DMA errors for multi-channel interrupts Maxime Chevallier
  2026-10-30 12:32 ` Lorenzo Bianconi
  2026-10-30 19:33 ` Nicolai Buchwitz
@ 2026-11-02  8:45 ` Magnus Leatti
  2026-11-02 10:23 ` netdev-bot+sashiko
  3 siblings, 0 replies; 6+ messages in thread
From: Magnus Leatti @ 2026-11-02  8:45 UTC (permalink / raw)
  To: Maxime Chevallier
  Cc: Andrew Lunn, Jakub Kicinski, davem, Eric Dumazet, Paolo Abeni,
	Simon Horman, Maxime Coquelin, Alexandre Torgue, Russell King,
	Lorenzo Bianconi, Ovidiu Panait, Nicolai Buchwitz,
	thomas.petazzoni, alexis.lothore, netdev, linux-kernel,
	linux-arm-kernel

On 10/30/26 11:22, Maxime Chevallier wrote:
> The Multi-MSI interrupt handler doesn't clear the DMA error interrupts,
> and this leads to interrupt storms when one of these interrupts starts
> firing :

Tested on a YT6801 (dwmac-motorcomm) with net at cb284914e749 and the
glue unmodified. Without this patch, a 64-byte UDP flood
(iperf3 -u -b 0 -l 64 -R -P 4) hangs the machine. On this board the
stuck RBU also keeps the MISC bit in MGMT_INT_CTRL0 set, so vector 5
fires about 120k times per second. With the patch, the MAC interrupt
only fires while the flood runs, about 1k/s, and stops afterwards;
same with eight streams plus a TX flood for 30 s. Throughput is
the same.

Tested-by: Magnus Leatti <magnusleatti@kernelriot.com>

nit: "YT8601" in the commit message should be YT6801.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net] net: stmmac: Clear DMA errors for multi-channel interrupts
  2026-10-30 10:22 [PATCH net] net: stmmac: Clear DMA errors for multi-channel interrupts Maxime Chevallier
                   ` (2 preceding siblings ...)
  2026-11-02  8:45 ` Magnus Leatti
@ 2026-11-02 10:23 ` netdev-bot+sashiko
  2026-11-02 10:40   ` Maxime Chevallier
  3 siblings, 1 reply; 6+ messages in thread
From: netdev-bot+sashiko @ 2026-11-02 10:23 UTC (permalink / raw)
  To: maxime.chevallier
  Cc: andrew+netdev, kuba, davem, edumazet, pabeni, horms,
	mcoquelin.stm32, alexandre.torgue, linux, lorenzo.bianconi,
	ovidiu.panait.rb, thomas.petazzoni, alexis.lothore, netdev,
	linux-kernel, linux-arm-kernel

Thank you for your contribution! Sashiko AI review found 3 potential
issue(s) to consider.

Critical: 0 · High: 1 · Medium: 2 · Low: 0

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [High] The new common-MSI error path runs stmmac_tx_err() without any
  synchronization against TX NAPI or the transmit path for the same…
- [Medium] In multi-MSI mode the patch adds new callers of
  stmmac_tx_err() for a channel, and nothing serializes them.
- [Medium] Use-after-free of the TX ring during AF_XDP pool
  setup/teardown while the device is up.

--

net: stmmac: Clear DMA errors for multi-channel interrupts

On dwmac4, the multi-MSI MAC interrupt handler now clears the per-channel
DMA error status bits (AIS, FBE, RPS, RBU) through a new DMA_DIR_ERR
direction. This avoids interrupt storms. The TX error handling is also
factored out into stmmac_dma_tx_error(), and the new
stmmac_dma_err_interrupt() path calls it as well.

> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> index 9ded31150fbe6..2068b57df8d52 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c

[ ... ]

> @@ -3165,13 +3175,24 @@ static void stmmac_dma_interrupt(struct stmmac_priv *priv)

[ ... ]

> +static void stmmac_dma_err_interrupt(struct stmmac_priv *priv)
> +{
> +	u8 tx_channel_count = priv->plat->tx_queues_to_use;
> +	u8 channels_to_check = max(priv->plat->rx_queues_to_use,
> +				   tx_channel_count);
> +	int status;
> +	u8 chan;
> +
> +	for (chan = 0; chan < channels_to_check; chan++) {
> +		status = stmmac_dma_interrupt_status(priv, priv->ioaddr,
> +						     &priv->xstats, chan,
> +						     DMA_DIR_ERR);
> +		if (chan < tx_channel_count)
> +			stmmac_dma_tx_error(priv, chan, status);

[Severity: High]
Can this race with TX NAPI or stmmac_xmit() on the same queue?

DMA_DIR_ERR masks out RI/TI/TBU. A latched FBE with AIS therefore makes
dwmac4_dma_interrupt() return exactly tx_hard_error, and the MAC IRQ goes
straight into the recovery path:

stmmac_mac_interrupt()
  stmmac_dma_err_interrupt()
    stmmac_dma_tx_error()
      stmmac_tx_err()
        netif_tx_stop_queue()
        stmmac_stop_tx_dma()
        dma_free_tx_skbufs()
        stmmac_clear_tx_descriptors()
        stmmac_reset_tx_queue()

stmmac_tx_err() does not take the txq lock and does not quiesce NAPI. At
the same time, TX NAPI for that queue may be in stmmac_tx_clean() on
another CPU, walking and freeing the same tx_skbuff[] entries under:

	__netif_tx_lock_bh(netdev_get_tx_queue(priv->dev, queue));

While NAPI runs, dwmac4_disable_dma_irq() only clears
DMA_CHAN_INTR_ENA_TIE, so the AIE/FBE-driven MAC line is not blocked.
The TX MSI affinity is set with cpumask_of(i % num_online_cpus()), so
NAPI for queue i will usually run on a different CPU from the MAC IRQ.

netif_tx_stop_queue() also does not wait for a stmmac_xmit() that is
already filling the ring.

Could this cause a double free or use-after-free of skbs and xdp_frames,
duplicate DMA unmaps, or corrupted cur_tx/dirty_tx?

Before this patch, stmmac_tx_err() could only be reached in multi-MSI
mode from stmmac_msi_intr_tx(). That handler is TI-driven, and its source
is masked while NAPI is active.

The single-IRQ path has the same unsynchronized stmmac_tx_err(). Taking
the BH-only txq lock from hardirq context would deadlock. Would it be
better to defer this recovery, for example to the service task or a
reset?

[Severity: Medium]
What happens here if an AF_XDP pool change on a running interface has
freed the TX queue's ring?

stmmac_xdp_enable_pool() and stmmac_xdp_disable_pool() call
stmmac_disable_tx_queue():

stmmac_disable_tx_queue() {
	spin_lock_irqsave(&ch->lock, flags);
	stmmac_disable_dma_irq(priv, priv->ioaddr, queue, 0, 1);
	spin_unlock_irqrestore(&ch->lock, flags);

	stmmac_stop_tx_dma(priv, queue);
	__free_dma_tx_desc_resources(priv, &priv->dma_conf, queue);
}

Only TIE is masked. __free_dma_tx_desc_resources() then frees the
descriptor ring and does the following, without clearing the pointers:

	kfree(tx_q->tx_skbuff_dma);
	kfree(tx_q->tx_skbuff);

During this window the MAC IRQ stays requested, AIE/FBE stay enabled,
and STMMAC_DOWN is not set. stmmac_dma_err_interrupt() walks every
channel below tx_queues_to_use without checking whether its ring is
allocated.

Suppose FBE is latched on that channel before stmmac_enable_tx_queue()
reallocates the ring. Would stmmac_tx_err() then read tx_skbuff_dma[i]
and tx_skbuff[i] from freed memory in stmmac_free_tx_buffer()? And would
stmmac_clear_tx_descriptors() write into freed coherent DMA memory?

Before this patch, no IRQ path touched the TX ring in this window in
multi-MSI mode.

>  	}
>  }

[ ... ]

> @@ -6466,6 +6487,9 @@ static irqreturn_t stmmac_mac_interrupt(int irq, void *dev_id)
>  	/* To handle Common interrupts */
>  	stmmac_common_interrupt(priv);
>  
> +	if (priv->plat->core_type == DWMAC_CORE_GMAC4)
> +		stmmac_dma_err_interrupt(priv);
> +

[Severity: Medium]
Can stmmac_tx_err() now run on the same channel from two CPUs at once?

In multi-MSI mode, stmmac_request_irq_multi_msi() installs
stmmac_mac_interrupt() on dev->irq. When wol_irq is a different line, it
installs the same handler there too:

	if (priv->wol_irq > 0 && priv->wol_irq != dev->irq) {
		...
		ret = request_irq(priv->wol_irq,
				  stmmac_mac_interrupt,
				  0, int_name, dev);

dwmac-intel, for example, sets res->wol_irq to a separate MSI vector.

stmmac_msi_intr_tx() can also reach tx_hard_error on the same channel,
because FBE and AIS are part of DMA_CHAN_STATUS_MSK_TX through
DMA_CHAN_STATUS_MSK_COMMON.

dwmac4_dma_interrupt() reads the status first and clears it later:

	u32 intr_status = readl(ioaddr + DMA_CHAN_STATUS(dwmac4_addrs, chan));
	...
	writel(intr_status & intr_en,
	       ioaddr + DMA_CHAN_STATUS(dwmac4_addrs, chan));

Two of these handlers could both see FBE before either one clears it,
and both would then call stmmac_tx_err() on the same tx_q.
stmmac_free_tx_buffer() does an unlocked check-then-free:

	if (tx_q->tx_skbuff[i] &&
	    tx_q->tx_skbuff_dma[i].buf_type == STMMAC_TXBUF_T_SKB) {
		dev_kfree_skb_any(tx_q->tx_skbuff[i]);
		tx_q->tx_skbuff[i] = NULL;
	}

Could this double free the skb or xdp_frame and double unmap
tx_skbuff_dma[i].buf? Could it also race on descriptor clearing, ring
reset and TX DMA channel reprogramming?

In INTM=1 the TX MSI normally fires with TI set. That gives
tx_hard_error | handle_tx, which fails the == tx_hard_error check, so the
TX MSI case probably needs a status snapshot without TI. The MAC plus
WoL case needs the WoL vector to fire at the same moment at runtime.

The single-IRQ path already has a similar MAC plus WoL race, since
stmmac_interrupt() is also requested on wol_irq there. In multi-MSI mode,
this patch is what introduces it.

>  	return IRQ_HANDLED;
>  }

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261030102251.208929-1-maxime.chevallier%40bootlin.com

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net] net: stmmac: Clear DMA errors for multi-channel interrupts
  2026-11-02 10:23 ` netdev-bot+sashiko
@ 2026-11-02 10:40   ` Maxime Chevallier
  0 siblings, 0 replies; 6+ messages in thread
From: Maxime Chevallier @ 2026-11-02 10:40 UTC (permalink / raw)
  To: netdev-bot+sashiko
  Cc: andrew+netdev, kuba, davem, edumazet, pabeni, horms,
	mcoquelin.stm32, alexandre.torgue, linux, lorenzo.bianconi,
	ovidiu.panait.rb, thomas.petazzoni, alexis.lothore, netdev,
	linux-kernel, linux-arm-kernel

Hi,

On 11/2/26 11:23, netdev-bot+sashiko@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 3 potential
> issue(s) to consider.
> 
> Critical: 0 · High: 1 · Medium: 2 · Low: 0
> 
> If you believe none of the issues are legitimate, please reply to each
> issue of Medium severity or higher and explain why the AI is wrong.
> If any of them are legitimate and you plan to respin, please let patchwork
> know by including "pw-bot: cr" as a separate line at the end of your reply
> (one such reply per series is enough).
> 
> - [High] The new common-MSI error path runs stmmac_tx_err() without any
>   synchronization against TX NAPI or the transmit path for the same…
> - [Medium] In multi-MSI mode the patch adds new callers of
>   stmmac_tx_err() for a channel, and nothing serializes them.
> - [Medium] Use-after-free of the TX ring during AF_XDP pool
>   setup/teardown while the device is up.

These are unlikely, but real. I'll address that in V2, as well
as the typo Magnus spotted :)

Maxime

pw-bot: cr


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-11-02 10:40 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-30 10:22 [PATCH net] net: stmmac: Clear DMA errors for multi-channel interrupts Maxime Chevallier
2026-10-30 12:32 ` Lorenzo Bianconi
2026-10-30 19:33 ` Nicolai Buchwitz
2026-11-02  8:45 ` Magnus Leatti
2026-11-02 10:23 ` netdev-bot+sashiko
2026-11-02 10:40   ` Maxime Chevallier

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®