cb031d8f34887aab850cc47d00ce863f87e76a49
[openwrt/staging/blocktrron.git] /
1 From 19fa4f2a85d777a8052e869c1b892a2f7556569d Mon Sep 17 00:00:00 2001
2 From: Heiner Kallweit <hkallweit1@gmail.com>
3 Date: Mon, 8 Apr 2024 20:47:40 +0200
4 Subject: [PATCH] r8169: fix LED-related deadlock on module removal
5
6 Binding devm_led_classdev_register() to the netdev is problematic
7 because on module removal we get a RTNL-related deadlock. Fix this
8 by avoiding the device-managed LED functions.
9
10 Note: We can safely call led_classdev_unregister() for a LED even
11 if registering it failed, because led_classdev_unregister() detects
12 this and is a no-op in this case.
13
14 Fixes: 18764b883e15 ("r8169: add support for LED's on RTL8168/RTL8101")
15 Cc: stable@vger.kernel.org
16 Reported-by: Lukas Wunner <lukas@wunner.de>
17 Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
18 Reviewed-by: Andrew Lunn <andrew@lunn.ch>
19 Signed-off-by: David S. Miller <davem@davemloft.net>
20 ---
21 drivers/net/ethernet/realtek/r8169.h | 6 ++--
22 drivers/net/ethernet/realtek/r8169_leds.c | 35 +++++++++++++++--------
23 drivers/net/ethernet/realtek/r8169_main.c | 8 ++++--
24 3 files changed, 33 insertions(+), 16 deletions(-)
25
26 --- a/drivers/net/ethernet/realtek/r8169.h
27 +++ b/drivers/net/ethernet/realtek/r8169.h
28 @@ -73,6 +73,7 @@ enum mac_version {
29 };
30
31 struct rtl8169_private;
32 +struct r8169_led_classdev;
33
34 void r8169_apply_firmware(struct rtl8169_private *tp);
35 u16 rtl8168h_2_get_adc_bias_ioffset(struct rtl8169_private *tp);
36 @@ -84,7 +85,8 @@ void r8169_get_led_name(struct rtl8169_p
37 char *buf, int buf_len);
38 int rtl8168_get_led_mode(struct rtl8169_private *tp);
39 int rtl8168_led_mod_ctrl(struct rtl8169_private *tp, u16 mask, u16 val);
40 -void rtl8168_init_leds(struct net_device *ndev);
41 +struct r8169_led_classdev *rtl8168_init_leds(struct net_device *ndev);
42 int rtl8125_get_led_mode(struct rtl8169_private *tp, int index);
43 int rtl8125_set_led_mode(struct rtl8169_private *tp, int index, u16 mode);
44 -void rtl8125_init_leds(struct net_device *ndev);
45 +struct r8169_led_classdev *rtl8125_init_leds(struct net_device *ndev);
46 +void r8169_remove_leds(struct r8169_led_classdev *leds);
47 --- a/drivers/net/ethernet/realtek/r8169_leds.c
48 +++ b/drivers/net/ethernet/realtek/r8169_leds.c
49 @@ -147,22 +147,22 @@ static void rtl8168_setup_ldev(struct r8
50 led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;
51
52 /* ignore errors */
53 - devm_led_classdev_register(&ndev->dev, led_cdev);
54 + led_classdev_register(&ndev->dev, led_cdev);
55 }
56
57 -void rtl8168_init_leds(struct net_device *ndev)
58 +struct r8169_led_classdev *rtl8168_init_leds(struct net_device *ndev)
59 {
60 - /* bind resource mgmt to netdev */
61 - struct device *dev = &ndev->dev;
62 struct r8169_led_classdev *leds;
63 int i;
64
65 - leds = devm_kcalloc(dev, RTL8168_NUM_LEDS, sizeof(*leds), GFP_KERNEL);
66 + leds = kcalloc(RTL8168_NUM_LEDS + 1, sizeof(*leds), GFP_KERNEL);
67 if (!leds)
68 - return;
69 + return NULL;
70
71 for (i = 0; i < RTL8168_NUM_LEDS; i++)
72 rtl8168_setup_ldev(leds + i, ndev, i);
73 +
74 + return leds;
75 }
76
77 static int rtl8125_led_hw_control_is_supported(struct led_classdev *led_cdev,
78 @@ -246,20 +246,31 @@ static void rtl8125_setup_led_ldev(struc
79 led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;
80
81 /* ignore errors */
82 - devm_led_classdev_register(&ndev->dev, led_cdev);
83 + led_classdev_register(&ndev->dev, led_cdev);
84 }
85
86 -void rtl8125_init_leds(struct net_device *ndev)
87 +struct r8169_led_classdev *rtl8125_init_leds(struct net_device *ndev)
88 {
89 - /* bind resource mgmt to netdev */
90 - struct device *dev = &ndev->dev;
91 struct r8169_led_classdev *leds;
92 int i;
93
94 - leds = devm_kcalloc(dev, RTL8125_NUM_LEDS, sizeof(*leds), GFP_KERNEL);
95 + leds = kcalloc(RTL8125_NUM_LEDS + 1, sizeof(*leds), GFP_KERNEL);
96 if (!leds)
97 - return;
98 + return NULL;
99
100 for (i = 0; i < RTL8125_NUM_LEDS; i++)
101 rtl8125_setup_led_ldev(leds + i, ndev, i);
102 +
103 + return leds;
104 +}
105 +
106 +void r8169_remove_leds(struct r8169_led_classdev *leds)
107 +{
108 + if (!leds)
109 + return;
110 +
111 + for (struct r8169_led_classdev *l = leds; l->ndev; l++)
112 + led_classdev_unregister(&l->led);
113 +
114 + kfree(leds);
115 }
116 --- a/drivers/net/ethernet/realtek/r8169_main.c
117 +++ b/drivers/net/ethernet/realtek/r8169_main.c
118 @@ -674,6 +674,8 @@ struct rtl8169_private {
119 const char *fw_name;
120 struct rtl_fw *rtl_fw;
121
122 + struct r8169_led_classdev *leds;
123 +
124 u32 ocp_base;
125 };
126
127 @@ -5069,6 +5071,8 @@ static void rtl_remove_one(struct pci_de
128
129 cancel_work_sync(&tp->wk.work);
130
131 + r8169_remove_leds(tp->leds);
132 +
133 unregister_netdev(tp->dev);
134
135 if (tp->dash_type != RTL_DASH_NONE)
136 @@ -5527,9 +5531,9 @@ static int rtl_init_one(struct pci_dev *
137
138 if (IS_ENABLED(CONFIG_R8169_LEDS)) {
139 if (rtl_is_8125(tp))
140 - rtl8125_init_leds(dev);
141 + tp->leds = rtl8125_init_leds(dev);
142 else if (tp->mac_version > RTL_GIGA_MAC_VER_06)
143 - rtl8168_init_leds(dev);
144 + tp->leds = rtl8168_init_leds(dev);
145 }
146
147 netdev_info(dev, "%s, %pM, XID %03x, IRQ %d\n",