Message ID | 20240509-gemini-ethernet-locking-v1-1-afd00a528b95@linaro.org (mailing list archive) |
---|---|
State | Accepted |
Commit | 812552808f7ff71133fc59768cdc253c5b8ca1bf |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | [net] net: ethernet: cortina: Locking fixes | expand |
On Thu, May 09, 2024 at 09:44:54AM +0200, Linus Walleij wrote: > This fixes a probably long standing problem in the Cortina > Gemini ethernet driver: there are some paths in the code > where the IRQ registers are written without taking the proper > locks. > > Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") > Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Reviewed-by: Simon Horman <horms@kernel.org>
On Thu, May 09, 2024 at 09:44:54AM +0200, Linus Walleij wrote: > This fixes a probably long standing problem in the Cortina > Gemini ethernet driver: there are some paths in the code > where the IRQ registers are written without taking the proper > locks. > > Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") > Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Reviewed-by: Simon Horman <horms@kernel.org>
Hello: This patch was applied to netdev/net.git (main) by Jakub Kicinski <kuba@kernel.org>: On Thu, 09 May 2024 09:44:54 +0200 you wrote: > This fixes a probably long standing problem in the Cortina > Gemini ethernet driver: there are some paths in the code > where the IRQ registers are written without taking the proper > locks. > > Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") > Signed-off-by: Linus Walleij <linus.walleij@linaro.org> > > [...] Here is the summary with links: - [net] net: ethernet: cortina: Locking fixes https://git.kernel.org/netdev/net/c/812552808f7f You are awesome, thank you!
On Thu, May 09, 2024 at 09:44:54AM +0200, Linus Walleij wrote: > This fixes a probably long standing problem in the Cortina > Gemini ethernet driver: there are some paths in the code > where the IRQ registers are written without taking the proper > locks. > > Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") > Signed-off-by: Linus Walleij <linus.walleij@linaro.org> > --- > drivers/net/ethernet/cortina/gemini.c | 12 ++++++++++-- > 1 file changed, 10 insertions(+), 2 deletions(-) > > diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c > index 705c3eb19cd3..d1fbadbf86d4 100644 > --- a/drivers/net/ethernet/cortina/gemini.c > +++ b/drivers/net/ethernet/cortina/gemini.c > @@ -1107,10 +1107,13 @@ static void gmac_tx_irq_enable(struct net_device *netdev, > { > struct gemini_ethernet_port *port = netdev_priv(netdev); > struct gemini_ethernet *geth = port->geth; > + unsigned long flags; > u32 val, mask; > > netdev_dbg(netdev, "%s device %d\n", __func__, netdev->dev_id); > > + spin_lock_irqsave(&geth->irq_lock, flags); > + > mask = GMAC0_IRQ0_TXQ0_INTS << (6 * netdev->dev_id + txq); > > if (en) > @@ -1119,6 +1122,8 @@ static void gmac_tx_irq_enable(struct net_device *netdev, > val = readl(geth->base + GLOBAL_INTERRUPT_ENABLE_0_REG); > val = en ? val | mask : val & ~mask; > writel(val, geth->base + GLOBAL_INTERRUPT_ENABLE_0_REG); > + > + spin_unlock_irqrestore(&geth->irq_lock, flags); > } > Looks good, though spinlock looks necessary only around the ENABLE0 rmw, as the 'if (en)' part is resetting the "triggered" flag of the interrupt (acking earlier-ignored interrupts). > static void gmac_tx_irq(struct net_device *netdev, unsigned int txq_num) > @@ -1415,15 +1420,19 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) > union gmac_rxdesc_3 word3; > struct page *page = NULL; > unsigned int page_offs; > + unsigned long flags; > unsigned short r, w; > union dma_rwptr rw; > dma_addr_t mapping; > int frag_nr = 0; > > + spin_lock_irqsave(&geth->irq_lock, flags); > rw.bits32 = readl(ptr_reg); > /* Reset interrupt as all packages until here are taken into account */ > writel(DEFAULT_Q0_INT_BIT << netdev->dev_id, > geth->base + GLOBAL_INTERRUPT_STATUS_1_REG); > + spin_unlock_irqrestore(&geth->irq_lock, flags); > + This doesn't look right: one, those are different registers, two it is an IRQ-acking write. In this case it is important that readl() is ordered before writel(), but the spinlock doesn't guarantee that. > @@ -1726,10 +1735,9 @@ static irqreturn_t gmac_irq(int irq, void *data) > gmac_update_hw_stats(netdev); > > if (val & (GMAC0_RX_OVERRUN_INT_BIT << (netdev->dev_id * 8))) { > + spin_lock(&geth->irq_lock); > writel(GMAC0_RXDERR_INT_BIT << (netdev->dev_id * 8), > geth->base + GLOBAL_INTERRUPT_STATUS_4_REG); > - > - spin_lock(&geth->irq_lock); > u64_stats_update_begin(&port->ir_stats_syncp); > ++port->stats.rx_fifo_errors; > u64_stats_update_end(&port->ir_stats_syncp); This, too, is a IRQ-acking write that doesn't seem to gain much by running inside the spin-locked section. Best Regards Michał Mirosław
diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c index 705c3eb19cd3..d1fbadbf86d4 100644 --- a/drivers/net/ethernet/cortina/gemini.c +++ b/drivers/net/ethernet/cortina/gemini.c @@ -1107,10 +1107,13 @@ static void gmac_tx_irq_enable(struct net_device *netdev, { struct gemini_ethernet_port *port = netdev_priv(netdev); struct gemini_ethernet *geth = port->geth; + unsigned long flags; u32 val, mask; netdev_dbg(netdev, "%s device %d\n", __func__, netdev->dev_id); + spin_lock_irqsave(&geth->irq_lock, flags); + mask = GMAC0_IRQ0_TXQ0_INTS << (6 * netdev->dev_id + txq); if (en) @@ -1119,6 +1122,8 @@ static void gmac_tx_irq_enable(struct net_device *netdev, val = readl(geth->base + GLOBAL_INTERRUPT_ENABLE_0_REG); val = en ? val | mask : val & ~mask; writel(val, geth->base + GLOBAL_INTERRUPT_ENABLE_0_REG); + + spin_unlock_irqrestore(&geth->irq_lock, flags); } static void gmac_tx_irq(struct net_device *netdev, unsigned int txq_num) @@ -1415,15 +1420,19 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) union gmac_rxdesc_3 word3; struct page *page = NULL; unsigned int page_offs; + unsigned long flags; unsigned short r, w; union dma_rwptr rw; dma_addr_t mapping; int frag_nr = 0; + spin_lock_irqsave(&geth->irq_lock, flags); rw.bits32 = readl(ptr_reg); /* Reset interrupt as all packages until here are taken into account */ writel(DEFAULT_Q0_INT_BIT << netdev->dev_id, geth->base + GLOBAL_INTERRUPT_STATUS_1_REG); + spin_unlock_irqrestore(&geth->irq_lock, flags); + r = rw.bits.rptr; w = rw.bits.wptr; @@ -1726,10 +1735,9 @@ static irqreturn_t gmac_irq(int irq, void *data) gmac_update_hw_stats(netdev); if (val & (GMAC0_RX_OVERRUN_INT_BIT << (netdev->dev_id * 8))) { + spin_lock(&geth->irq_lock); writel(GMAC0_RXDERR_INT_BIT << (netdev->dev_id * 8), geth->base + GLOBAL_INTERRUPT_STATUS_4_REG); - - spin_lock(&geth->irq_lock); u64_stats_update_begin(&port->ir_stats_syncp); ++port->stats.rx_fifo_errors; u64_stats_update_end(&port->ir_stats_syncp);
This fixes a probably long standing problem in the Cortina Gemini ethernet driver: there are some paths in the code where the IRQ registers are written without taking the proper locks. Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") Signed-off-by: Linus Walleij <linus.walleij@linaro.org> --- drivers/net/ethernet/cortina/gemini.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) --- base-commit: 4cece764965020c22cff7665b18a012006359095 change-id: 20240509-gemini-ethernet-locking-7c1cb004ed49 Best regards,