mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Aleksei Sviridkin <f@lex.la>
To: andrew@lunn.ch, hkallweit1@gmail.com, linux@armlinux.org.uk
Cc: olteanv@gmail.com, davem@davemloft.net, edumazet@google.com,
	kuba@kernel.org, pabeni@redhat.com, horms@kernel.org,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH net v5 2/2] net: phy: restore the interrupt phy_probe() replaced with PHY_POLL
Date: Sun,  6 Sep 2026 17:46:43 +0000	[thread overview]
Message-ID: <20260906174643.4107607-3-f@lex.la> (raw)
In-Reply-To: <20260906174643.4107607-1-f@lex.la>

phy_probe() sets phydev->irq to PHY_POLL when the driver it is binding
has no interrupt callbacks, and nothing puts the number back. The
driver that binds afterwards therefore starts polled, and unless its
consumer installs the interrupt again the PHY stays that way for the
rest of the uptime, with no trace beyond an informational irq=POLL
beside that driver's name. A DSA switch that connects its user ports
before the rootfs holding the PHY driver module is mounted hits this on
every boot.

Save the number where it is taken away and put it back in phy_remove(),
in phy_probe()'s own error path, which the driver core does not follow
with a remove, and in phy_attach_direct()'s unwind of a generic bind
that failed after its probe succeeded. Only a probe that took a number
away arms the restore, and phy_link_change suppresses it when a
consumer holds the PHY, since that consumer skipped requesting an
interrupt on the value it saw. phy_detach() clears phy_link_change
before it releases the driver, so the case above still restores.

That last part arrived in commit e0d1c55501d3 ("net: phy: fix
phy_uses_state_machine()"). Without it the mark is never cleared once a
consumer has attached, so the restore this patch exists for never fires;
a backport needs that commit first.

Fixes: 00db8189d984 ("This patch adds a PHY Abstraction Layer to the Linux Kernel, enabling ethernet drivers to remain as ignorant as is reasonable of the connected PHY's design and operation details.")
Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@lex.la>
---
 drivers/net/phy/phy_device.c | 23 ++++++++++++++++++++++-
 include/linux/phy.h          |  3 +++
 2 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 94b2e85e00a3..ba117147721a 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -770,6 +770,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,
 	mdiodev->device_remove = phy_mdio_device_remove;
 	mdiodev->reset_state = -1;
 
+	dev->irq_saved = PHY_POLL;
 	dev->speed = SPEED_UNKNOWN;
 	dev->duplex = DUPLEX_UNKNOWN;
 	dev->pause = false;
@@ -1734,6 +1735,19 @@ static bool phy_drv_supports_irq(const struct phy_driver *phydrv)
 	return phydrv->config_intr && phydrv->handle_interrupt;
 }
 
+/* Give back what phy_probe() took, but not while phy_link_change marks a
+ * consumer: it skipped phy_request_interrupt() on the value it saw, so
+ * phy_disconnect() would free an interrupt nobody requested.
+ */
+static void phy_restore_probe_irq(struct phy_device *phydev)
+{
+	if (phydev->phy_link_change || phydev->irq_saved == PHY_POLL)
+		return;
+
+	phydev->irq = phydev->irq_saved;
+	phydev->irq_saved = PHY_POLL;
+}
+
 /**
  * phy_attach_direct - attach a network device to a given PHY device pointer
  * @dev: network device to attach
@@ -1896,6 +1910,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 
 error_module_put:
 	module_put(d->driver->owner);
+	phy_restore_probe_irq(phydev);
 	phydev->is_genphy_driven = 0;
 	d->driver = NULL;
 error_put_device:
@@ -3694,8 +3709,10 @@ static int phy_probe(struct device *dev)
 	/* Disable the interrupt if the PHY doesn't support it
 	 * but the interrupt is still a valid one
 	 */
-	if (!phy_drv_supports_irq(phydrv) && phy_interrupt_is_valid(phydev))
+	if (!phy_drv_supports_irq(phydrv) && phy_interrupt_is_valid(phydev)) {
+		phydev->irq_saved = phydev->irq;
 		phydev->irq = PHY_POLL;
+	}
 
 	if (phydrv->flags & PHY_IS_INTERNAL)
 		phydev->is_internal = true;
@@ -3820,6 +3837,8 @@ static int phy_probe(struct device *dev)
 	if (!phydev->is_on_sfp_module)
 		phy_led_triggers_unregister(phydev);
 
+	phy_restore_probe_irq(phydev);
+
 	/* Re-assert the reset signal on error */
 	phy_device_reset(phydev, 1);
 
@@ -3848,6 +3867,8 @@ static int phy_remove(struct device *dev)
 	if (phydev->drv && phydev->drv->remove)
 		phydev->drv->remove(phydev);
 
+	phy_restore_probe_irq(phydev);
+
 	/* Assert the reset signal */
 	phy_device_reset(phydev, 1);
 
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 5f8d65868e0f..d492417572a0 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -591,6 +591,8 @@ struct phy_oatc14_sqi_capability {
  *      - Bits [31:24] are reserved for defining generic
  *        PHY driver behavior.
  * @irq: IRQ number of the PHY's interrupt (-1 if none)
+ * @irq_saved: @irq as it was before phy_probe() replaced it with PHY_POLL,
+ *             or PHY_POLL when there is nothing to restore
  * @phylink: Pointer to phylink instance for this PHY
  * @sfp_bus_attached: Flag indicating whether the SFP bus has been attached
  * @sfp_bus: SFP bus attached to this PHY's fiber port
@@ -761,6 +763,7 @@ struct phy_device {
 	 * -1 means no interrupt
 	 */
 	int irq;
+	int irq_saved;
 
 	/* private data pointer */
 	/* For use by PHYs to maintain extra state */
-- 
2.53.0


  parent reply	other threads:[~2026-09-06 17:46 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-06 17:46 [PATCH net v5 0/2] net: fix a stale phylink PHY pointer and a lost PHY interrupt Aleksei Sviridkin
2026-09-06 17:46 ` [PATCH net v5 1/2] net: phylink: unwind the PHY binding when bringup fails late Aleksei Sviridkin
2026-09-09 12:49   ` netdev-bot+sashiko
2026-09-06 17:46 ` Aleksei Sviridkin [this message]
2026-09-08  1:09   ` [PATCH net v5 2/2] net: phy: restore the interrupt phy_probe() replaced with PHY_POLL Andrew Lunn
2026-09-09 12:49   ` netdev-bot+sashiko

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=20260906174643.4107607-3-f@lex.la \
    --to=f@lex.la \
    --cc=andrew@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=hkallweit1@gmail.com \
    --cc=horms@kernel.org \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@armlinux.org.uk \
    --cc=netdev@vger.kernel.org \
    --cc=olteanv@gmail.com \
    --cc=pabeni@redhat.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

all inboxes | Powered by JetHome®