[PATCH net-next v5 18/19] net: stmmac: prepare device-local DMA interrupt quiescence
flat view
WARM1d
From: James Hilliard <hidden>
Date: 2026-09-27 22:00:37
Also in:
bpf, linux-sunxi, linux-tegra, lkml
Subsystem:
networking drivers, stmmac ethernet driver, the rest · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Maxime Chevallier, Linus Torvalds
Add DMA interrupt-mask accessors for the supported cores and a per-channel gate protected by the channel lock. A handler invoked on a shared IRQ can then mask newly enabled device sources without acknowledging pending events or touching rings being replaced. The retained-ring MTU transaction will save and restore these masks and synchronize the registered handlers. No interrupt-controller line needs to be disabled, so other devices sharing the IRQ remain serviceable. Signed-off-by: James Hilliard <redacted> --- drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 11 ++++++++ .../net/ethernet/stmicro/stmmac/dwmac1000_dma.c | 1 + drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c | 1 + drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c | 2 ++ drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h | 2 ++ drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c | 11 ++++++++ drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h | 2 ++ drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c | 10 ++++++++ drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c | 11 ++++++++ drivers/net/ethernet/stmicro/stmmac/hwif.h | 5 ++++ drivers/net/ethernet/stmicro/stmmac/stmmac.h | 2 ++ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 30 ++++++++++++++++------ 12 files changed, 80 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
index c9145441aab0..8a3f15134402 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c@@ -374,6 +374,16 @@ static void sun8i_dwmac_disable_dma_irq(struct stmmac_priv *priv, writel(value, ioaddr + EMAC_INT_EN); } +static u32 sun8i_dwmac_set_dma_irq_mask(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 chan, u32 mask) +{ + u32 old_mask = readl(ioaddr + EMAC_INT_EN); + + writel(mask, ioaddr + EMAC_INT_EN); + readl(ioaddr + EMAC_INT_EN); + return old_mask; +} + static void sun8i_dwmac_dma_start_tx(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan) {
@@ -579,6 +589,7 @@ static const struct stmmac_dma_ops sun8i_dwmac_dma_ops = { .enable_dma_transmission = sun8i_dwmac_enable_dma_transmission, .enable_dma_irq = sun8i_dwmac_enable_dma_irq, .disable_dma_irq = sun8i_dwmac_disable_dma_irq, + .set_irq_mask = sun8i_dwmac_set_dma_irq_mask, .start_tx = sun8i_dwmac_dma_start_tx, .stop_tx = sun8i_dwmac_dma_stop_tx, .start_rx = sun8i_dwmac_dma_start_rx,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
index 4cb7e6c16bdd..2285eac69071 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c@@ -264,6 +264,7 @@ const struct stmmac_dma_ops dwmac1000_dma_ops = { .enable_dma_reception = dwmac_enable_dma_reception, .enable_dma_irq = dwmac_enable_dma_irq, .disable_dma_irq = dwmac_disable_dma_irq, + .set_irq_mask = dwmac_set_dma_irq_mask, .start_tx = dwmac_dma_start_tx, .stop_tx = dwmac_dma_stop_tx, .start_rx = dwmac_dma_start_rx,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
index 5ffd3c1471c4..41579d10af3c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c@@ -118,6 +118,7 @@ const struct stmmac_dma_ops dwmac100_dma_ops = { .enable_dma_transmission = dwmac_enable_dma_transmission, .enable_dma_irq = dwmac_enable_dma_irq, .disable_dma_irq = dwmac_disable_dma_irq, + .set_irq_mask = dwmac_set_dma_irq_mask, .start_tx = dwmac_dma_start_tx, .stop_tx = dwmac_dma_stop_tx, .start_rx = dwmac_dma_start_rx,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
index d7928678dee1..d5f0cc8b851d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c@@ -582,6 +582,7 @@ const struct stmmac_dma_ops dwmac4_dma_ops = { .dma_tx_mode = dwmac4_dma_tx_chan_op_mode, .enable_dma_irq = dwmac4_enable_dma_irq, .disable_dma_irq = dwmac4_disable_dma_irq, + .set_irq_mask = dwmac4_set_dma_irq_mask, .start_tx = dwmac4_dma_start_tx, .stop_tx = dwmac4_dma_stop_tx, .start_rx = dwmac4_dma_start_rx,
@@ -613,6 +614,7 @@ const struct stmmac_dma_ops dwmac410_dma_ops = { .dma_tx_mode = dwmac4_dma_tx_chan_op_mode, .enable_dma_irq = dwmac4_enable_dma_irq, .disable_dma_irq = dwmac4_disable_dma_irq, + .set_irq_mask = dwmac4_set_dma_irq_mask, .start_tx = dwmac4_dma_start_tx, .stop_tx = dwmac4_dma_stop_tx, .start_rx = dwmac4_dma_start_rx,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
index 9352107204eb..edccc09f0b03 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h@@ -184,6 +184,8 @@ void dwmac4_enable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan, bool rx, bool tx); void dwmac4_disable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan, bool rx, bool tx); +u32 dwmac4_set_dma_irq_mask(struct stmmac_priv *priv, void __iomem *ioaddr, + u32 chan, u32 mask); void dwmac4_dma_start_tx(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan); void dwmac4_dma_stop_tx(struct stmmac_priv *priv, void __iomem *ioaddr,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
index 9af0565a9bca..477bdb081c52 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c@@ -155,6 +155,17 @@ void dwmac4_disable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr, writel(value, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); } +u32 dwmac4_set_dma_irq_mask(struct stmmac_priv *priv, void __iomem *ioaddr, + u32 chan, u32 mask) +{ + const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs; + u32 old_mask = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); + + writel(mask, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); + readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); + return old_mask; +} + int dwmac4_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr, struct stmmac_extra_stats *x, u32 chan, u32 dir) {
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
index 970495bccfd2..4726801253f5 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h@@ -150,6 +150,8 @@ void dwmac_enable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan, bool rx, bool tx); void dwmac_disable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan, bool rx, bool tx); +u32 dwmac_set_dma_irq_mask(struct stmmac_priv *priv, void __iomem *ioaddr, + u32 chan, u32 mask); void dwmac_dma_start_tx(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan); void dwmac_dma_stop_tx(struct stmmac_priv *priv, void __iomem *ioaddr,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
index bb907db8fca1..88d904ed4685 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c@@ -84,6 +84,16 @@ void dwmac_disable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr, writel(value, ioaddr + DMA_CHAN_INTR_ENA(chan)); } +u32 dwmac_set_dma_irq_mask(struct stmmac_priv *priv, void __iomem *ioaddr, + u32 chan, u32 mask) +{ + u32 old_mask = readl(ioaddr + DMA_CHAN_INTR_ENA(chan)); + + writel(mask, ioaddr + DMA_CHAN_INTR_ENA(chan)); + readl(ioaddr + DMA_CHAN_INTR_ENA(chan)); + return old_mask; +} + void dwmac_dma_start_tx(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan) {
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
index ff83858ebc1f..30915d3f5230 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c@@ -256,6 +256,16 @@ static void dwxgmac2_disable_dma_irq(struct stmmac_priv *priv, writel(value, ioaddr + XGMAC_DMA_CH_INT_EN(chan)); } +static u32 dwxgmac2_set_dma_irq_mask(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 chan, u32 mask) +{ + u32 old_mask = readl(ioaddr + XGMAC_DMA_CH_INT_EN(chan)); + + writel(mask, ioaddr + XGMAC_DMA_CH_INT_EN(chan)); + readl(ioaddr + XGMAC_DMA_CH_INT_EN(chan)); + return old_mask; +} + static void dwxgmac2_dma_start_tx(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan) {
@@ -604,6 +614,7 @@ const struct stmmac_dma_ops dwxgmac210_dma_ops = { .dma_tx_mode = dwxgmac2_dma_tx_mode, .enable_dma_irq = dwxgmac2_enable_dma_irq, .disable_dma_irq = dwxgmac2_disable_dma_irq, + .set_irq_mask = dwxgmac2_set_dma_irq_mask, .start_tx = dwxgmac2_dma_start_tx, .stop_tx = dwxgmac2_dma_stop_tx, .start_rx = dwxgmac2_dma_start_rx,
diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h
index 4b7381a6fcce..20de97ac011a 100644
--- a/drivers/net/ethernet/stmicro/stmmac/hwif.h
+++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h@@ -197,6 +197,9 @@ struct stmmac_dma_ops { u32 chan, bool rx, bool tx); void (*disable_dma_irq)(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan, bool rx, bool tx); + /* Replace and flush the full interrupt enable mask; return the old mask. */ + u32 (*set_irq_mask)(struct stmmac_priv *priv, void __iomem *ioaddr, + u32 chan, u32 mask); void (*start_tx)(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan); void (*stop_tx)(struct stmmac_priv *priv, void __iomem *ioaddr,
@@ -263,6 +266,8 @@ struct stmmac_dma_ops { stmmac_do_void_callback(__priv, dma, enable_dma_irq, __priv, __args) #define stmmac_disable_dma_irq(__priv, __args...) \ stmmac_do_void_callback(__priv, dma, disable_dma_irq, __priv, __args) +#define stmmac_set_dma_irq_mask(__priv, __args...) \ + stmmac_do_callback(__priv, dma, set_irq_mask, __priv, __args) #define stmmac_start_tx(__priv, __args...) \ stmmac_do_void_callback(__priv, dma, start_tx, __priv, __args) #define stmmac_stop_tx(__priv, __args...) \
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index 090d79aeb2ad..06fe750624b6 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h@@ -146,6 +146,8 @@ struct stmmac_channel { struct stmmac_priv *priv_data; spinlock_t lock; u32 index; + /* Protected by lock; IRQ handlers must not access the DMA rings. */ + bool irq_quiesced; }; struct stmmac_fpe_cfg {
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 98dbc873e1c8..435c76b7db30 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c@@ -3370,14 +3370,30 @@ static bool stmmac_safety_feat_interrupt(struct stmmac_priv *priv) static int stmmac_napi_check(struct stmmac_priv *priv, u32 chan, u32 dir) { - int status = stmmac_dma_interrupt_status(priv, priv->ioaddr, - &priv->xstats, chan, dir); - struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[chan]; - struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[chan]; struct stmmac_channel *ch = &priv->channel[chan]; + struct stmmac_rx_queue *rx_q; + struct stmmac_tx_queue *tx_q; struct napi_struct *rx_napi; struct napi_struct *tx_napi; unsigned long flags; + int status; + + spin_lock_irqsave(&ch->lock, flags); + if (unlikely(ch->irq_quiesced)) { + /* A shared IRQ may still invoke us, and DMA initialization can + * restore interrupt enables. Mask them again without acknowledging + * pending events or accessing the configuration being replaced. + */ + stmmac_set_dma_irq_mask(priv, priv->ioaddr, chan, 0); + spin_unlock_irqrestore(&ch->lock, flags); + return 0; + } + spin_unlock_irqrestore(&ch->lock, flags); + + status = stmmac_dma_interrupt_status(priv, priv->ioaddr, + &priv->xstats, chan, dir); + rx_q = &priv->dma_conf->rx_queue[chan]; + tx_q = &priv->dma_conf->tx_queue[chan]; rx_napi = rx_q->xsk_pool ? &ch->rxtx_napi : &ch->rx_napi; tx_napi = tx_q->xsk_pool ? &ch->rxtx_napi : &ch->tx_napi;
@@ -4096,8 +4112,7 @@ static void stmmac_free_irq(struct net_device *dev, for (j = irq_idx - 1; msi && j >= 0; j--) { if (msi->tx_irq[j] > 0) { irq_set_affinity_hint(msi->tx_irq[j], NULL); - free_irq(msi->tx_irq[j], - &priv->channel[j]); + free_irq(msi->tx_irq[j], &priv->channel[j]); } } irq_idx = priv->plat->rx_queues_to_use;
@@ -4106,8 +4121,7 @@ static void stmmac_free_irq(struct net_device *dev, for (j = irq_idx - 1; msi && j >= 0; j--) { if (msi->rx_irq[j] > 0) { irq_set_affinity_hint(msi->rx_irq[j], NULL); - free_irq(msi->rx_irq[j], - &priv->channel[j]); + free_irq(msi->rx_irq[j], &priv->channel[j]); } }
--
2.53.0