OSDN Git Service

net: phy: Manual remove LEDs to ensure correct ordering
authorAndrew Lunn <andrew@lunn.ch>
Sat, 17 Jun 2023 15:55:00 +0000 (17:55 +0200)
committerDavid S. Miller <davem@davemloft.net>
Sun, 18 Jun 2023 16:33:05 +0000 (17:33 +0100)
If the core is left to remove the LEDs via devm_, it is performed too
late, after the PHY driver is removed from the PHY. This results in
dereferencing a NULL pointer when the LED core tries to turn the LED
off before destroying the LED.

Manually unregister the LEDs at a safe point in phy_remove.

Cc: stable@vger.kernel.org
Reported-by: Florian Fainelli <f.fainelli@gmail.com>
Suggested-by: Florian Fainelli <f.fainelli@gmail.com>
Fixes: 01e5b728e9e4 ("net: phy: Add a binding for PHY LEDs")
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/phy_device.c

index 17d0d05..5359821 100644 (file)
@@ -3021,6 +3021,15 @@ static int phy_led_blink_set(struct led_classdev *led_cdev,
        return err;
 }
 
+static void phy_leds_unregister(struct phy_device *phydev)
+{
+       struct phy_led *phyled;
+
+       list_for_each_entry(phyled, &phydev->leds, list) {
+               led_classdev_unregister(&phyled->led_cdev);
+       }
+}
+
 static int of_phy_led(struct phy_device *phydev,
                      struct device_node *led)
 {
@@ -3054,7 +3063,7 @@ static int of_phy_led(struct phy_device *phydev,
        init_data.fwnode = of_fwnode_handle(led);
        init_data.devname_mandatory = true;
 
-       err = devm_led_classdev_register_ext(dev, cdev, &init_data);
+       err = led_classdev_register_ext(dev, cdev, &init_data);
        if (err)
                return err;
 
@@ -3083,6 +3092,7 @@ static int of_phy_leds(struct phy_device *phydev)
                err = of_phy_led(phydev, led);
                if (err) {
                        of_node_put(led);
+                       phy_leds_unregister(phydev);
                        return err;
                }
        }
@@ -3305,6 +3315,9 @@ static int phy_remove(struct device *dev)
 
        cancel_delayed_work_sync(&phydev->state_queue);
 
+       if (IS_ENABLED(CONFIG_PHYLIB_LEDS))
+               phy_leds_unregister(phydev);
+
        phydev->state = PHY_DOWN;
 
        sfp_bus_del_upstream(phydev->sfp_bus);