From: Igor Velkov <redacted>
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 last patch of this series 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, MAC drivers included, is a wide audit, 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 <redacted>
---
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..bbe28ff97e51 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 without phydev->lock, so not bitfields */
+ bool irq_suspended;
+ bool irq_rerun;
unsigned default_timestamp:1;
--
2.43.0