04939deb6e33afeb95cf8bb7437b066ba0d6f604
[openwrt/staging/mans0n.git] /
1 From c938ab4da0eb1620ae3243b0b24c572ddfc318fc Mon Sep 17 00:00:00 2001
2 From: Andrew Lunn <andrew@lunn.ch>
3 Date: Sat, 17 Jun 2023 17:55:00 +0200
4 Subject: [PATCH] net: phy: Manual remove LEDs to ensure correct ordering
5
6 If the core is left to remove the LEDs via devm_, it is performed too
7 late, after the PHY driver is removed from the PHY. This results in
8 dereferencing a NULL pointer when the LED core tries to turn the LED
9 off before destroying the LED.
10
11 Manually unregister the LEDs at a safe point in phy_remove.
12
13 Cc: stable@vger.kernel.org
14 Reported-by: Florian Fainelli <f.fainelli@gmail.com>
15 Suggested-by: Florian Fainelli <f.fainelli@gmail.com>
16 Fixes: 01e5b728e9e4 ("net: phy: Add a binding for PHY LEDs")
17 Signed-off-by: Andrew Lunn <andrew@lunn.ch>
18 Signed-off-by: David S. Miller <davem@davemloft.net>
19 ---
20 drivers/net/phy/phy_device.c | 15 ++++++++++++++-
21 1 file changed, 14 insertions(+), 1 deletion(-)
22
23 --- a/drivers/net/phy/phy_device.c
24 +++ b/drivers/net/phy/phy_device.c
25 @@ -2964,6 +2964,15 @@ static int phy_led_blink_set(struct led_
26 return err;
27 }
28
29 +static void phy_leds_unregister(struct phy_device *phydev)
30 +{
31 + struct phy_led *phyled;
32 +
33 + list_for_each_entry(phyled, &phydev->leds, list) {
34 + led_classdev_unregister(&phyled->led_cdev);
35 + }
36 +}
37 +
38 static int of_phy_led(struct phy_device *phydev,
39 struct device_node *led)
40 {
41 @@ -2997,7 +3006,7 @@ static int of_phy_led(struct phy_device
42 init_data.fwnode = of_fwnode_handle(led);
43 init_data.devname_mandatory = true;
44
45 - err = devm_led_classdev_register_ext(dev, cdev, &init_data);
46 + err = led_classdev_register_ext(dev, cdev, &init_data);
47 if (err)
48 return err;
49
50 @@ -3026,6 +3035,7 @@ static int of_phy_leds(struct phy_device
51 err = of_phy_led(phydev, led);
52 if (err) {
53 of_node_put(led);
54 + phy_leds_unregister(phydev);
55 return err;
56 }
57 }
58 @@ -3229,6 +3239,9 @@ static int phy_remove(struct device *dev
59
60 cancel_delayed_work_sync(&phydev->state_queue);
61
62 + if (IS_ENABLED(CONFIG_PHYLIB_LEDS))
63 + phy_leds_unregister(phydev);
64 +
65 phydev->state = PHY_DOWN;
66
67 sfp_bus_del_upstream(phydev->sfp_bus);