From: Xuanqiang Luo <xuanqiang.luo@linux.dev>
To: netdev@vger.kernel.org, kuba@kernel.org, andrew@lunn.ch,
maxime.chevallier@bootlin.com
Cc: hkallweit1@gmail.com, linux@armlinux.org.uk, davem@davemloft.net,
edumazet@google.com, pabeni@redhat.com, chleroy@kernel.org,
linux-kernel@vger.kernel.org,
Xuanqiang Luo <luoxuanqiang@kylinos.cn>
Subject: [PATCH net v3 2/5] net: phy: unregister SFP upstream before port cleanup
Date: Wed, 19 Aug 2026 14:02:33 +0800 [thread overview]
Message-ID: <20260819060236.24665-3-xuanqiang.luo@linux.dev> (raw)
In-Reply-To: <20260819060236.24665-1-xuanqiang.luo@linux.dev>
From: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
Commit 4497f5028675 ("net: phy: Clean the phy_ports after unregistering
the downstream SFP bus") established that an SFP upstream must be
unregistered before its phy_ports are destroyed because SFP callbacks
may access these ports.
phy_setup_ports() does not follow this order when a later port setup
step fails after phy_sfp_probe() succeeds. It destroys the SFP phy_port
and leaves phy_probe() to unregister the upstream later, creating a race
between port destruction and SFP upstream callbacks.
The error unwind is also split across three functions. If
phy_setup_sfp_port() fails, phy_sfp_probe() leaves the upstream
registered and relies on phy_probe() to remove it after
phy_setup_ports() returns.
Make each layer unwind the resources it successfully set up. Unregister
only the upstream in phy_sfp_probe() when SFP port setup fails, since
the failed port has already been destroyed. Add phy_sfp_release() for a
successful SFP probe, and make phy_setup_ports() use it before cleaning
up the remaining ports. Once phy_setup_ports() has rolled back all port
setup, make phy_probe() skip this cleanup.
Fixes: 589e934d2735 ("net: phy: Introduce PHY ports representation")
Signed-off-by: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
---
drivers/net/phy/phy_device.c | 49 ++++++++++++++++++++++++++++--------
1 file changed, 38 insertions(+), 11 deletions(-)
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 59ee5a76af2ba..538e9ca44d093 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1723,12 +1723,41 @@ static int phy_sfp_probe(struct phy_device *phydev)
phydev->sfp_bus = NULL;
}
- if (!ret && phydev->sfp_bus)
+ if (!ret && phydev->sfp_bus) {
ret = phy_setup_sfp_port(phydev);
+ if (ret) {
+ sfp_bus_del_upstream(phydev->sfp_bus);
+ phydev->sfp_bus = NULL;
+ }
+ }
return ret;
}
+/**
+ * phy_sfp_release - release resources set up by phy_sfp_probe()
+ * @phydev: the PHY device
+ *
+ * Release the SFP resources set up by a successful phy_sfp_probe(). Unregister
+ * the upstream before destroying its phy_port, so SFP upstream callbacks cannot
+ * race with port destruction.
+ */
+static void phy_sfp_release(struct phy_device *phydev)
+{
+ struct phy_port *port, *tmp;
+
+ sfp_bus_del_upstream(phydev->sfp_bus);
+ phydev->sfp_bus = NULL;
+
+ list_for_each_entry_safe(port, tmp, &phydev->ports, head) {
+ if (!port->is_sfp)
+ continue;
+
+ phy_del_port(phydev, port);
+ phy_port_destroy(port);
+ }
+}
+
static bool phy_drv_supports_irq(const struct phy_driver *phydrv)
{
return phydrv->config_intr && phydrv->handle_interrupt;
@@ -3522,13 +3551,13 @@ static int phy_setup_ports(struct phy_device *phydev)
if (!phydev->is_genphy_driven) {
ret = phy_sfp_probe(phydev);
if (ret)
- goto out;
+ goto err_ports;
}
if (phydev->n_ports < phydev->max_n_ports) {
ret = phy_default_setup_single_port(phydev);
if (ret)
- goto out;
+ goto err_sfp;
}
linkmode_zero(ports_supported);
@@ -3555,7 +3584,9 @@ static int phy_setup_ports(struct phy_device *phydev)
return 0;
-out:
+err_sfp:
+ phy_sfp_release(phydev);
+err_ports:
phy_cleanup_ports(phydev);
return ret;
}
@@ -3719,7 +3750,7 @@ static int phy_probe(struct device *dev)
err = phy_setup_ports(phydev);
if (err)
- goto out_sfp_release;
+ goto out_reset;
phy_advertise_supported(phydev);
@@ -3791,9 +3822,7 @@ static int phy_probe(struct device *dev)
phy_led_triggers_unregister(phydev);
out_sfp_release:
- sfp_bus_del_upstream(phydev->sfp_bus);
- phydev->sfp_bus = NULL;
-
+ phy_sfp_release(phydev);
phy_cleanup_ports(phydev);
out_reset:
@@ -3817,9 +3846,7 @@ static int phy_remove(struct device *dev)
phydev->state = PHY_DOWN;
- sfp_bus_del_upstream(phydev->sfp_bus);
- phydev->sfp_bus = NULL;
-
+ phy_sfp_release(phydev);
phy_cleanup_ports(phydev);
if (phydev->drv && phydev->drv->remove)
--
2.43.0
next prev parent reply other threads:[~2026-08-19 6:03 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-19 6:02 [PATCH net v3 0/5] net: phy: fix cleanup after probe failure Xuanqiang Luo
2026-08-19 6:02 ` [PATCH net v3 1/5] net: phy: split phy_probe() error paths Xuanqiang Luo
2026-08-19 13:35 ` Andrew Lunn
2026-08-19 6:02 ` Xuanqiang Luo [this message]
2026-08-19 13:36 ` [PATCH net v3 2/5] net: phy: unregister SFP upstream before port cleanup Andrew Lunn
2026-08-19 6:02 ` [PATCH net v3 3/5] net: phy: set PHY_READY after LED setup Xuanqiang Luo
2026-08-19 13:37 ` Andrew Lunn
2026-08-19 6:02 ` [PATCH net v3 4/5] net: phy: call driver remove when core initialization fails Xuanqiang Luo
2026-08-19 13:37 ` Andrew Lunn
2026-08-19 6:02 ` [PATCH net v3 5/5] net: phy: propagate errors from default port setup Xuanqiang Luo
2026-08-19 9:27 ` Maxime Chevallier
2026-08-19 13:33 ` [PATCH net v3 0/5] net: phy: fix cleanup after probe failure Andrew Lunn
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=20260819060236.24665-3-xuanqiang.luo@linux.dev \
--to=xuanqiang.luo@linux.dev \
--cc=andrew@lunn.ch \
--cc=chleroy@kernel.org \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=hkallweit1@gmail.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@armlinux.org.uk \
--cc=luoxuanqiang@kylinos.cn \
--cc=maxime.chevallier@bootlin.com \
--cc=netdev@vger.kernel.org \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.