Netdev List
 help / color / mirror / Atom feed
* [PATCH net v2 0/2] net: phy: postpone PHY interrupts during sleep with MAC-managed PM
@ 2026-10-26  4:53 Igor Velkov
  2026-10-26  4:53 ` [PATCH net v2 1/2] net: phy: move irq_suspended and irq_rerun out of the bitfield Igor Velkov
  2026-10-26  4:53 ` [PATCH net v2 2/2] net: phy: postpone PHY interrupts during sleep with MAC-managed PM Igor Velkov
  0 siblings, 2 replies; 5+ messages in thread
From: Igor Velkov @ 2026-10-26  4:53 UTC (permalink / raw)
  To: Andrew Lunn, Heiner Kallweit, Russell King, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni
  Cc: Lukas Wunner, Rafael J. Wysocki, Wei Fang, Niklas Söderlund,
	Paul Barker, Florian Fainelli, Doug Berger, Thangaraj Samynathan,
	netdev, linux-kernel, Igor Velkov

When the MAC driver manages PHY PM, phylib never sets irq_suspended, and
a PHY interrupt during the sleep transition runs the PHY driver's
handler while the MDIO bus may be powered down. On Helios64 (dwmac-rk)
this hangs the board on suspend as well as on resume.

Patch 1 moves the two flags out of a bitfield they share with the link
state written under phydev->lock; patch 2 sets irq_suspended for these
PHYs too.

mac_managed_pm (fba863b81604) means the MAC driver suspends and resumes
the PHY; it does not cover the PHY interrupt. Neither phylink nor any
MAC driver touches irq_suspended, and resume_device_irqs() runs at the
end of the noirq phase, before the normal resume callbacks of the MAC,
the MDIO bus and the PHY, so callback order does not help the
interrupt. v1 asked whether phylink_suspend()/phylink_resume() would be
the better place; v2 keeps the fix in phylib, which also covers MAC
drivers that use phylib without phylink.

Changes in v2:
- new patch 1: bools instead of bitfields (Sashiko); sizeof(struct
  phy_device) is unchanged on arm64
- phy_free_interrupt(): re-enable a shared line after free_irq()
  instead of dropping irq_rerun before it (Sashiko)
- reworded the comment in mdio_bus_phy_suspend() (Andrew)
- described and tested the suspend-side window, noted the wakeup
  event; the v1 tests with Wake-on-LAN (Helios64 20/20, ODROID-HC4
  10/10) were on v1 code
- W=1 allmodconfig/allyesconfig: no new warnings

Not tested: patch 1 alone, and a vanilla net kernel on hardware; the
tests ran on an Armbian 7.3 kernel whose Helios64 DT describes the PHY
interrupt.

Link to v1: https://lore.kernel.org/r/20261002043548.1302145-1-iav@iav.lv

Igor Velkov (2):
  net: phy: move irq_suspended and irq_rerun out of the bitfield
  net: phy: postpone PHY interrupts during sleep with MAC-managed PM

 drivers/net/phy/phy.c        |  9 ++++++++
 drivers/net/phy/phy_device.c | 44 ++++++++++++++++++++++--------------
 include/linux/phy.h          |  5 ++--
 3 files changed, 39 insertions(+), 19 deletions(-)


base-commit: 7076f684d5132e6a6145e1da2fa74771ecd052e5
-- 
2.43.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH net v2 1/2] net: phy: move irq_suspended and irq_rerun out of the bitfield
  2026-10-26  4:53 [PATCH net v2 0/2] net: phy: postpone PHY interrupts during sleep with MAC-managed PM Igor Velkov
@ 2026-10-26  4:53 ` Igor Velkov
  2026-10-26 12:54   ` Andrew Lunn
  2026-10-26  4:53 ` [PATCH net v2 2/2] net: phy: postpone PHY interrupts during sleep with MAC-managed PM Igor Velkov
  1 sibling, 1 reply; 5+ messages in thread
From: Igor Velkov @ 2026-10-26  4:53 UTC (permalink / raw)
  To: Andrew Lunn, Heiner Kallweit, Russell King, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni
  Cc: Lukas Wunner, Rafael J. Wysocki, Wei Fang, Niklas Söderlund,
	Paul Barker, Florian Fainelli, Doug Berger, Thangaraj Samynathan,
	netdev, linux-kernel, Igor Velkov

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.

Make both flags bools.

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

diff --git a/include/linux/phy.h b/include/linux/phy.h
index 21afc2082b17..ec2c4d9a15d3 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;
+	/* irq_rerun is set by the IRQ thread: not bitfields */
+	bool irq_suspended;
+	bool irq_rerun;
 
 	unsigned default_timestamp:1;
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH net v2 2/2] net: phy: postpone PHY interrupts during sleep with MAC-managed PM
  2026-10-26  4:53 [PATCH net v2 0/2] net: phy: postpone PHY interrupts during sleep with MAC-managed PM Igor Velkov
  2026-10-26  4:53 ` [PATCH net v2 1/2] net: phy: move irq_suspended and irq_rerun out of the bitfield Igor Velkov
@ 2026-10-26  4:53 ` Igor Velkov
  1 sibling, 0 replies; 5+ messages in thread
From: Igor Velkov @ 2026-10-26  4:53 UTC (permalink / raw)
  To: Andrew Lunn, Heiner Kallweit, Russell King, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni
  Cc: Lukas Wunner, Rafael J. Wysocki, Wei Fang, Niklas Söderlund,
	Paul Barker, Florian Fainelli, Doug Berger, Thangaraj Samynathan,
	netdev, linux-kernel, Igor Velkov

mdio_bus_phy_suspend() and mdio_bus_phy_resume() return early when the
MAC driver manages PHY PM, so the PHY never gets irq_suspended and
phy_interrupt() runs the PHY driver's handler at any point of the sleep
transition. If the MAC driver powers the MDIO bus down, the MDIO access
in that handler can stall the CPU. This happens on both sides:

- suspend: the MAC's suspend callback stops the PHY, the link goes
  down, and the handler runs while the MAC powers down;
- resume: resume_device_irqs() re-enables a pending interrupt before
  the MAC resumes.

On Helios64 (dwmac-rk, RTL8211F with its interrupt line described in
DT) both hang the board: the handler reads INSR while the GMAC clocks
are off, after rk_gmac_suspend() or before rk_gmac_resume().

Set irq_suspended for these PHYs too. The PHY device is a child of its
MDIO bus, so mdio_bus_phy_resume() runs after the bus is back (with
stmmac, after the MAC) and replays the postponed interrupt then.
phy_suspend(), phy_resume() and the state machine stay with the MAC
driver. As for other PHYs, an interrupt in that window now signals a
wakeup event if Wake-on-LAN is enabled or the device may wake the
system.

MAC drivers that close the interface in suspend free the interrupt
before the PHY resumes, so phy_free_interrupt() re-enables a line left
disabled by a postponed interrupt if other handlers still share it.

Tested on Helios64 with 7.3, s2idle without Wake-on-LAN, GMAC clocks
off in suspend: 10 of 10 cycles got through suspend, 4 of them then
hit a separate PCIe error on resume. The same kernel without these
changes hung or reset after the link went down in 10 of 10.

Fixes: 1758bde2e4aa ("net: phy: Don't trigger state machine while in suspend")
Signed-off-by: Igor Velkov <iav@iav.lv>
---
 drivers/net/phy/phy.c        |  9 ++++++++
 drivers/net/phy/phy_device.c | 44 ++++++++++++++++++++++--------------
 2 files changed, 36 insertions(+), 17 deletions(-)

diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index fce9bc7be330..00aa48e807a5 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -1508,6 +1508,15 @@ void phy_free_interrupt(struct phy_device *phydev)
 {
 	phy_disable_interrupts(phydev);
 	free_irq(phydev->irq, phydev);
+
+	/* Undo the disable_irq_nosync() of a postponed interrupt if the line
+	 * is still shared; otherwise the next irq_startup() resets the depth.
+	 */
+	if (phydev->irq_rerun) {
+		phydev->irq_rerun = 0;
+		if (irq_has_action(phydev->irq))
+			enable_irq(phydev->irq);
+	}
 }
 EXPORT_SYMBOL(phy_free_interrupt);
 
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 5d4508bcfbe9..e2807e08e436 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -336,18 +336,20 @@ static __maybe_unused int mdio_bus_phy_suspend(struct device *dev)
 {
 	struct phy_device *phydev = to_phy_device(dev);
 
-	if (phydev->mac_managed_pm)
-		return 0;
-
 	/* Wakeup interrupts may occur during the system sleep transition when
 	 * the PHY is inaccessible. Set flag to postpone handling until the PHY
 	 * has resumed. Wait for concurrent interrupt handler to complete.
+	 * This holds when the MAC manages PHY PM too: an interrupt can still
+	 * fire while the MAC has the PHY or the MDIO bus powered down.
 	 */
 	if (phy_interrupt_is_valid(phydev)) {
 		phydev->irq_suspended = 1;
 		synchronize_irq(phydev->irq);
 	}
 
+	if (phydev->mac_managed_pm)
+		return 0;
+
 	/* We must stop the state machine manually, otherwise it stops out of
 	 * control, possibly with the phydev->lock held. Upon resume, netdev
 	 * may call phy routines that try to grab the same lock, and that may
@@ -364,13 +366,33 @@ static __maybe_unused int mdio_bus_phy_suspend(struct device *dev)
 	return phy_suspend(phydev);
 }
 
+static __maybe_unused void mdio_bus_phy_resume_irq(struct phy_device *phydev)
+{
+	if (!phy_interrupt_is_valid(phydev))
+		return;
+
+	phydev->irq_suspended = 0;
+	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;
+		enable_irq(phydev->irq);
+		irq_wake_thread(phydev->irq, phydev);
+	}
+}
+
 static __maybe_unused int mdio_bus_phy_resume(struct device *dev)
 {
 	struct phy_device *phydev = to_phy_device(dev);
 	int ret;
 
-	if (phydev->mac_managed_pm)
+	if (phydev->mac_managed_pm) {
+		mdio_bus_phy_resume_irq(phydev);
 		return 0;
+	}
 
 	if (!phydev->suspended_by_mdio_bus)
 		goto no_resume;
@@ -393,19 +415,7 @@ static __maybe_unused int mdio_bus_phy_resume(struct device *dev)
 	if (ret < 0)
 		return ret;
 no_resume:
-	if (phy_interrupt_is_valid(phydev)) {
-		phydev->irq_suspended = 0;
-		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;
-			enable_irq(phydev->irq);
-			irq_wake_thread(phydev->irq, phydev);
-		}
-	}
+	mdio_bus_phy_resume_irq(phydev);
 
 	if (phy_uses_state_machine(phydev))
 		phy_start_machine(phydev);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH net v2 1/2] net: phy: move irq_suspended and irq_rerun out of the bitfield
  2026-10-26  4:53 ` [PATCH net v2 1/2] net: phy: move irq_suspended and irq_rerun out of the bitfield Igor Velkov
@ 2026-10-26 12:54   ` Andrew Lunn
  2026-10-26 17:32     ` Igor Velkov
  0 siblings, 1 reply; 5+ messages in thread
From: Andrew Lunn @ 2026-10-26 12:54 UTC (permalink / raw)
  To: Igor Velkov
  Cc: Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Lukas Wunner, Rafael J. Wysocki,
	Wei Fang, Niklas Söderlund, Paul Barker, Florian Fainelli,
	Doug Berger, Thangaraj Samynathan, netdev, linux-kernel

On Mon, Oct 26, 2026 at 06:53:42AM +0200, Igor Velkov wrote:
> 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

As you say, it is an IRQ thread. Because interrupt handlers might need
to access the PHY, which is a blocking operation,
request_threaded_irq() is used in phy_request_interrupt(). So you can
just take the mutex and make the RMW thread atomic.

     Andrew

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH net v2 1/2] net: phy: move irq_suspended and irq_rerun out of the bitfield
  2026-10-26 12:54   ` Andrew Lunn
@ 2026-10-26 17:32     ` Igor Velkov
  0 siblings, 0 replies; 5+ messages in thread
From: Igor Velkov @ 2026-10-26 17:32 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Lukas Wunner, Rafael J. Wysocki,
	Wei Fang, Niklas Söderlund, Paul Barker, Florian Fainelli,
	Doug Berger, Thangaraj Samynathan, netdev, linux-kernel,
	Igor Velkov

On Mon, Oct 26, 2026, Andrew Lunn wrote:
> As you say, it is an IRQ thread. Because interrupt handlers might need
> to access the PHY, which is a blocking operation,
> request_threaded_irq() is used in phy_request_interrupt(). So you can
> just take the mutex and make the RMW thread atomic.

The lock in phy_interrupt() only orders against writers that hold it.
phy_suspend() writes wol_enabled and suspended unlocked, e.g. from
phy_stop() in the MAC's suspend while the link-down interrupt may set
irq_rerun, and it cannot take the lock: phy_may_wakeup() can call
phy_ethtool_get_wol(), which takes it. mdio_bus_phy_suspend() and
phy_config_interrupt() write that word unlocked too. As bools,
irq_suspended and irq_rerun share it with none of them.
Would you still prefer the lock?

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-10-26 17:32 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-26  4:53 [PATCH net v2 0/2] net: phy: postpone PHY interrupts during sleep with MAC-managed PM Igor Velkov
2026-10-26  4:53 ` [PATCH net v2 1/2] net: phy: move irq_suspended and irq_rerun out of the bitfield Igor Velkov
2026-10-26 12:54   ` Andrew Lunn
2026-10-26 17:32     ` Igor Velkov
2026-10-26  4:53 ` [PATCH net v2 2/2] net: phy: postpone PHY interrupts during sleep with MAC-managed PM Igor Velkov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox