From: netdev-bot+sashiko@kernel.org
To: iav@iav.lv
Cc: andrew@lunn.ch, hkallweit1@gmail.com, linux@armlinux.org.uk,
davem@davemloft.net, edumazet@kernel.org, kuba@kernel.org,
pabeni@redhat.com, lukas@wunner.de, rafael.j.wysocki@intel.com,
wei.fang@nxp.com, niklas.soderlund@ragnatech.se,
paul@pbarker.dev, florian.fainelli@broadcom.com,
opendmb@gmail.com, Thangaraj.S@microchip.com,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH net v2 2/2] net: phy: postpone PHY interrupts during sleep with MAC-managed PM
Date: Tue, 27 Oct 2026 16:55:56 +0000 [thread overview]
Message-ID: <179312015623.3134.7149153472845451722@kernel.org> (raw)
In-Reply-To: <20261026045343.837889-3-iav@iav.lv>
Thank you for your contribution! Sashiko AI review found 3 potential
issue(s) to consider.
Critical: 0 · High: 0 · Medium: 2 (1) · Low: 1 (1)
If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).
- [Medium] For PHYs whose PM is managed by the MAC driver
(mac_managed_pm), mdio_bus_phy_suspend() now sets irq_suspended before…
- [Medium] When a PHY's interrupt is a nested threaded IRQ (PHY INT
routed through an I2C/SPI GPIO expander or MFD irqchip that uses…
- [Low] The new depth fixup in phy_free_interrupt() is an unsynchronized
check-then-act.
Pre-existing issues:
- [Medium] For a non-MAC-managed PHY, mdio_bus_phy_suspend() sets
irq_suspended and stops the state machine, then returns the result of…
- [Low] Topology: a PHY on another MAC's MDIO bus, linked to its consumer
MAC via device_link_add() in phy_attach_direct().
--
net: phy: postpone PHY interrupts during sleep with MAC-managed PM
This patch makes mdio_bus_phy_suspend() set irq_suspended for PHYs whose PM
is managed by the MAC driver. It replays the postponed interrupt from
mdio_bus_phy_resume() through a new mdio_bus_phy_resume_irq() helper. It
also makes phy_free_interrupt() re-enable a line left disabled by a
postponed interrupt if other handlers still share it.
> diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
> index fce9bc7be3304..00aa48e807a5e 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);
> + }
[Severity: Low]
Is this check-then-act safe if another driver calls request_irq() on the
same shared line at the same time?
By the time irq_has_action() runs, free_irq() has already dropped
desc->request_mutex and desc->lock. irq_has_action() only takes
rcu_read_lock().
If the PHY's action was the last one on the line, this interleaving
looks possible:
CPU1 CPU2
phy_free_interrupt()
free_irq()
__free_irq()
irq_shutdown() /* depth++ */
request_threaded_irq(IRQF_SHARED)
irq_startup()
desc->depth = 0;
irq_has_action() /* true */
enable_irq()
__enable_irq() /* depth == 0 */
WARN(1, KERN_WARNING "Unbalanced enable for IRQ %d\n", ...)
__enable_irq() does not change the depth when it is 0, so this seems
limited to a warning. The window is also narrow: irq_rerun must still be
set when the interface is closed during suspend, before
mdio_bus_phy_resume() runs.
The opposite order is harmless, because the depth++ in irq_shutdown()
absorbs the enable.
> diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
> index 5d4508bcfbe99..e2807e08e4365 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.
> */
[Severity: Low]
This isn't a regression introduced by this patch, but does this comment
claim more than the change covers?
When a PHY sits on another MAC's MDIO bus, phy_attach_direct() links it
to its consumer MAC:
if (dev && phydev->mdio.bus->parent && dev->dev.parent != phydev->mdio.bus->parent)
phydev->devlink = device_link_add(dev->dev.parent, &phydev->mdio.dev,
DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS);
In that topology the consumer MAC suspends before the PHY and resumes
after it. The handler can then still run, or be replayed from
mdio_bus_phy_resume_irq(), while the consumer MAC has cut the PHY's
regulator or reference clock (FEC, for example).
The MDIO bus owner is still an ancestor of the PHY, so the bus stall case
is covered.
Reads of an unpowered PHY over a live bus were also possible before this
patch. The pending interrupt was replayed from resume_device_irqs() in
dpm_resume_noirq(), which is even earlier than now.
Could the comment be narrowed to the MDIO bus case?
> if (phy_interrupt_is_valid(phydev)) {
> phydev->irq_suspended = 1;
> synchronize_irq(phydev->irq);
> }
>
> + if (phydev->mac_managed_pm)
> + return 0;
> +
[Severity: Medium]
Can this abort system suspend for MAC-managed PHYs when wakeup is enabled
on the MAC device but no ethtool WoL is configured?
The PHY device suspends before its MDIO bus and the MAC, so irq_suspended
is now set before the MAC's suspend callback runs. With stmmac and
priv->wolopts == 0, the MAC then powers the PHY down itself:
stmmac_suspend()
phylink_suspend(priv->phylink, false)
phylink_stop()
phy_stop()
phy_suspend()
phy_suspend() keeps the PHY powered only when phy_may_wakeup() is true or
netdev->ethtool->wol_enabled is set. Here the PHY powers down and raises
the link down interrupt that the commit message describes.
That interrupt now goes through the postpone branch of phy_interrupt():
else if (device_may_wakeup(&netdev->dev))
pm_wakeup_dev_event(&netdev->dev, 0, true);
else if (parent && device_may_wakeup(parent))
pm_wakeup_dev_event(parent, 0, true);
When pmt is set, stmmac_dvr_probe() calls
device_set_wakeup_capable(priv->device, 1). If power/wakeup is then
enabled on that device through sysfs, this hard wakeup event makes
pm_wakeup_pending() abort the suspend with -EBUSY. That would happen on
every attempt made while the link is up.
Core-managed PHYs avoid this, because mdio_bus_phy_may_suspend() keeps
the PHY powered in that case:
if (netdev->dev.parent && device_may_wakeup(netdev->dev.parent))
return false;
MAC-managed PHYs never go through that check. Before this patch, the
interrupt was handled in place and suspend went ahead.
> /* 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
[Severity: Medium]
This is a pre-existing issue and was not introduced by this patch. For
PHYs that are not MAC-managed, mdio_bus_phy_suspend() sets irq_suspended
and stops the state machine. It then returns the result of phy_suspend()
without undoing either step if that call fails:
phydev->suspended_by_mdio_bus = 1;
return phy_suspend(phydev);
The PM core skips resume for a device whose suspend failed. Does that
leave irq_suspended set? If so, phy_interrupt() would postpone every
later interrupt and leave the line disabled.
For mac_managed_pm PHYs, the ones newly affected here, this function
always returns 0 after setting the flag. So this patch adds no new
failure path.
[ ... ]
> @@ -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);
> + }
> +}
[Severity: Medium]
Does this replay work when the PHY interrupt is a nested threaded IRQ?
An example is PHY INT routed through an I2C or SPI GPIO expander that
uses handle_nested_irq().
__setup_irq() does not create a thread for nested IRQs:
if (new->thread_fn && !nested) {
ret = setup_irq_thread(new, irq, false);
So action->thread is NULL, and irq_wake_thread() does nothing.
The postpone branch in phy_interrupt() also returned without calling
handle_interrupt(). The PHY's interrupt status was therefore never read
or acked:
phydev->irq_rerun = 1;
disable_irq_nosync(irq);
return IRQ_HANDLED;
enable_irq() only resends if IRQS_PENDING was set. Take a parent that
accepts only edge triggers and filters masked sources, in the style of
pca953x. The PHY INT would stay asserted with no new edge, and link
interrupts could stay dead after resume.
The replay code itself predates this patch. However, mac_managed_pm PHYs
with a valid IRQ now go through it. The link goes down while the MAC
suspends, as the commit message describes, so a postponed interrupt is
likely on every suspend cycle.
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261026045343.837889-1-iav%40iav.lv
prev parent reply other threads:[~2026-10-27 16:55 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
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
2026-10-27 16:55 ` netdev-bot+sashiko [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=179312015623.3134.7149153472845451722@kernel.org \
--to=netdev-bot+sashiko@kernel.org \
--cc=Thangaraj.S@microchip.com \
--cc=andrew@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=florian.fainelli@broadcom.com \
--cc=hkallweit1@gmail.com \
--cc=iav@iav.lv \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@armlinux.org.uk \
--cc=lukas@wunner.de \
--cc=netdev@vger.kernel.org \
--cc=niklas.soderlund@ragnatech.se \
--cc=opendmb@gmail.com \
--cc=pabeni@redhat.com \
--cc=paul@pbarker.dev \
--cc=rafael.j.wysocki@intel.com \
--cc=wei.fang@nxp.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox