[PATCH net v3 1/2] net: phy: move irq_suspended and irq_rerun out of the bitfield

From: Igor Velkov via B4 Relay

Date: Thu Oct 29 2026 - 00:58:48 EST


From: Igor Velkov <iav@xxxxxx>

irq_suspended and irq_rerun share a word with link, autoneg_complete
and interrupts. phy_interrupt() sets irq_rerun from the IRQ thread
without phydev->lock, while the state machine writes link and
autoneg_complete under it and phy_request_interrupt()/
phy_free_interrupt() write interrupts, so one read-modify-write can
undo another. A lost irq_rerun leaves the PHY interrupt disabled after
resume, as phy_interrupt() has already disabled the line.

The next patch sets irq_suspended for PHYs whose MAC driver manages
PHY PM; with stmmac that happens before the MAC stops the PHY, which
widens the window.

Taking phydev->lock in phy_interrupt() alone does not close it:
phy_suspend() stores suspended in the same word and cannot hold the
lock across phy_may_wakeup(), which may take it, and phy_init_hw() and
mdio_bus_phy_suspend() write the word unlocked too. Locking every
writer means auditing about 200 stores to that word in 63 files, 19 of
them MAC drivers, and one missed now or added later reopens the race.

Make both flags bools.

Fixes: 1758bde2e4aa ("net: phy: Don't trigger state machine while in suspend")
Signed-off-by: Igor Velkov <iav@xxxxxx>
---
drivers/net/phy/phy.c | 2 +-
drivers/net/phy/phy_device.c | 6 +++---
include/linux/phy.h | 5 +++--
3 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index fce9bc7be330..9af303d60bd6 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -1430,7 +1430,7 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat)
pm_wakeup_dev_event(parent, 0, true);
}

- phydev->irq_rerun = 1;
+ phydev->irq_rerun = true;
disable_irq_nosync(irq);
return IRQ_HANDLED;
}
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 5d4508bcfbe9..79dba43d703a 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 = 1;
+ phydev->irq_suspended = true;
synchronize_irq(phydev->irq);
}

@@ -394,14 +394,14 @@ static __maybe_unused int mdio_bus_phy_resume(struct device *dev)
return ret;
no_resume:
if (phy_interrupt_is_valid(phydev)) {
- phydev->irq_suspended = 0;
+ 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 = 0;
+ phydev->irq_rerun = false;
enable_irq(phydev->irq);
irq_wake_thread(phydev->irq, phydev);
}
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 21afc2082b17..0e0066186ba2 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -726,8 +726,9 @@ struct phy_device {

/* Interrupts are enabled */
unsigned interrupts:1;
- unsigned irq_suspended:1;
- unsigned irq_rerun:1;
+ /* Written from the IRQ thread and PM callbacks: not bitfields */
+ bool irq_suspended;
+ bool irq_rerun;

unsigned default_timestamp:1;


--
2.43.0