[PATCH net v4 2/5] net: phy: disable the line of a postponed PHY interrupt only once
flat view
HOTtoday
From: Igor Velkov via B4 Relay <devnull+iav.iav.lv@kernel.org>
Date: 2026-10-31 08:01:18
Also in:
b4-sent, linux-arm-kernel, linux-rockchip, lkml
Subsystem:
ethernet phy library, networking drivers, the rest · Maintainers:
Andrew Lunn, Heiner Kallweit, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
From: Igor Velkov <redacted> phy_interrupt() postpones interrupts during a system sleep transition: it sets irq_rerun and disables the line, and mdio_bus_phy_resume() re-enables the line once and reruns the handler. On an edge-triggered line the IRQ core does not mask a ONESHOT interrupt, so an edge that arrives before disable_irq_nosync() runs the thread again. That second run disables the line again, and the line stays disabled after resume. Make irq_rerun atomic, as the next patch adds a second setter, and let only the run that sets it disable the line. Mark the irq_suspended accesses with READ_ONCE() and WRITE_ONCE(): the handler reads it while the PM callbacks write it. Fixes: 1758bde2e4aa ("net: phy: Don't trigger state machine while in suspend") Signed-off-by: Igor Velkov <redacted> --- drivers/net/phy/phy.c | 7 ++++--- drivers/net/phy/phy_device.c | 7 +++---- include/linux/phy.h | 5 ++--- 3 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 9af303d60bd6..b47f0d3d99e5 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c@@ -1416,7 +1416,7 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat) /* Wakeup interrupts may occur during a system sleep transition. * Postpone handling until the PHY has resumed. */ - if (IS_ENABLED(CONFIG_PM_SLEEP) && phydev->irq_suspended) { + if (IS_ENABLED(CONFIG_PM_SLEEP) && READ_ONCE(phydev->irq_suspended)) { struct net_device *netdev = phydev->attached_dev; if (netdev) {
@@ -1430,8 +1430,9 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat) pm_wakeup_dev_event(parent, 0, true); } - phydev->irq_rerun = true; - disable_irq_nosync(irq); + /* Disable the line only once: an edge can rerun the thread. */ + if (!atomic_xchg(&phydev->irq_rerun, 1)) + disable_irq_nosync(irq); return IRQ_HANDLED; }
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 79dba43d703a..d5d0efc10418 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c@@ -344,7 +344,7 @@ static __maybe_unused int mdio_bus_phy_suspend(struct device *dev) * has resumed. Wait for concurrent interrupt handler to complete. */ if (phy_interrupt_is_valid(phydev)) { - phydev->irq_suspended = true; + WRITE_ONCE(phydev->irq_suspended, true); synchronize_irq(phydev->irq); }
@@ -394,14 +394,13 @@ static __maybe_unused int mdio_bus_phy_resume(struct device *dev) return ret; no_resume: if (phy_interrupt_is_valid(phydev)) { - phydev->irq_suspended = false; + WRITE_ONCE(phydev->irq_suspended, false); synchronize_irq(phydev->irq); /* Rerun interrupts which were postponed by phy_interrupt() * because they occurred during the system sleep transition. */ - if (phydev->irq_rerun) { - phydev->irq_rerun = false; + if (atomic_xchg(&phydev->irq_rerun, 0)) { enable_irq(phydev->irq); irq_wake_thread(phydev->irq, phydev); }
diff --git a/include/linux/phy.h b/include/linux/phy.h
index bbe28ff97e51..48b4e852f230 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h@@ -651,8 +651,7 @@ struct phy_oatc14_sqi_capability { * @interrupts: Flag interrupts have been enabled * @irq_suspended: Flag indicating PHY is suspended and therefore interrupt * handling shall be postponed until PHY has resumed - * @irq_rerun: Flag indicating interrupts occurred while PHY was suspended, - * requiring a rerun of the interrupt handler after resume + * @irq_rerun: Set by the postponed interrupt that disabled the line * @default_timestamp: Flag indicating whether we are using the phy * timestamp as the default one * @interface: enum phy_interface_t value
@@ -728,7 +727,7 @@ struct phy_device { unsigned interrupts:1; /* Written without phydev->lock, so not bitfields */ bool irq_suspended; - bool irq_rerun; + atomic_t irq_rerun; unsigned default_timestamp:1;
--
2.43.0