]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
net: phy: Don't register LEDs for genphy
authorSean Anderson <sean.anderson@linux.dev>
Mon, 7 Jul 2025 19:58:03 +0000 (15:58 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 24 Jul 2025 06:56:31 +0000 (08:56 +0200)
[ Upstream commit f0f2b992d8185a0366be951685e08643aae17d6d ]

If a PHY has no driver, the genphy driver is probed/removed directly in
phy_attach/detach. If the PHY's ofnode has an "leds" subnode, then the
LEDs will be (un)registered when probing/removing the genphy driver.
This could occur if the leds are for a non-generic driver that isn't
loaded for whatever reason. Synchronously removing the PHY device in
phy_detach leads to the following deadlock:

rtnl_lock()
ndo_close()
    ...
    phy_detach()
        phy_remove()
            phy_leds_unregister()
                led_classdev_unregister()
                    led_trigger_set()
                        netdev_trigger_deactivate()
                            unregister_netdevice_notifier()
                                rtnl_lock()

There is a corresponding deadlock on the open/register side of things
(and that one is reported by lockdep), but it requires a race while this
one is deterministic.

Generic PHYs do not support LEDs anyway, so don't bother registering
them.

Fixes: 01e5b728e9e4 ("net: phy: Add a binding for PHY LEDs")
Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
Link: https://patch.msgid.link/20250707195803.666097-1-sean.anderson@linux.dev
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/net/phy/phy_device.c

index 13dea33d86ffa59e53eb9d90e913ff133f2015f8..834624a61060eae2b5f3004fb89e71fa2b4dce09 100644 (file)
@@ -3663,7 +3663,8 @@ static int phy_probe(struct device *dev)
        /* Get the LEDs from the device tree, and instantiate standard
         * LEDs for them.
         */
-       if (IS_ENABLED(CONFIG_PHYLIB_LEDS))
+       if (IS_ENABLED(CONFIG_PHYLIB_LEDS) && !phy_driver_is_genphy(phydev) &&
+           !phy_driver_is_genphy_10g(phydev))
                err = of_phy_leds(phydev);
 
 out:
@@ -3680,7 +3681,8 @@ static int phy_remove(struct device *dev)
 
        cancel_delayed_work_sync(&phydev->state_queue);
 
-       if (IS_ENABLED(CONFIG_PHYLIB_LEDS))
+       if (IS_ENABLED(CONFIG_PHYLIB_LEDS) && !phy_driver_is_genphy(phydev) &&
+           !phy_driver_is_genphy_10g(phydev))
                phy_leds_unregister(phydev);
 
        phydev->state = PHY_DOWN;