Skip to content

Commit 19fa4f2

Browse files
hkallweitdavem330
authored andcommitted
r8169: fix LED-related deadlock on module removal
Binding devm_led_classdev_register() to the netdev is problematic because on module removal we get a RTNL-related deadlock. Fix this by avoiding the device-managed LED functions. Note: We can safely call led_classdev_unregister() for a LED even if registering it failed, because led_classdev_unregister() detects this and is a no-op in this case. Fixes: 18764b8 ("r8169: add support for LED's on RTL8168/RTL8101") Cc: [email protected] Reported-by: Lukas Wunner <[email protected]> Signed-off-by: Heiner Kallweit <[email protected]> Reviewed-by: Andrew Lunn <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 81665ad commit 19fa4f2

File tree

3 files changed

+33
-16
lines changed

3 files changed

+33
-16
lines changed

drivers/net/ethernet/realtek/r8169.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ enum mac_version {
7373
};
7474

7575
struct rtl8169_private;
76+
struct r8169_led_classdev;
7677

7778
void r8169_apply_firmware(struct rtl8169_private *tp);
7879
u16 rtl8168h_2_get_adc_bias_ioffset(struct rtl8169_private *tp);
@@ -84,7 +85,8 @@ void r8169_get_led_name(struct rtl8169_private *tp, int idx,
8485
char *buf, int buf_len);
8586
int rtl8168_get_led_mode(struct rtl8169_private *tp);
8687
int rtl8168_led_mod_ctrl(struct rtl8169_private *tp, u16 mask, u16 val);
87-
void rtl8168_init_leds(struct net_device *ndev);
88+
struct r8169_led_classdev *rtl8168_init_leds(struct net_device *ndev);
8889
int rtl8125_get_led_mode(struct rtl8169_private *tp, int index);
8990
int rtl8125_set_led_mode(struct rtl8169_private *tp, int index, u16 mode);
90-
void rtl8125_init_leds(struct net_device *ndev);
91+
struct r8169_led_classdev *rtl8125_init_leds(struct net_device *ndev);
92+
void r8169_remove_leds(struct r8169_led_classdev *leds);

drivers/net/ethernet/realtek/r8169_leds.c

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -146,22 +146,22 @@ static void rtl8168_setup_ldev(struct r8169_led_classdev *ldev,
146146
led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;
147147

148148
/* ignore errors */
149-
devm_led_classdev_register(&ndev->dev, led_cdev);
149+
led_classdev_register(&ndev->dev, led_cdev);
150150
}
151151

152-
void rtl8168_init_leds(struct net_device *ndev)
152+
struct r8169_led_classdev *rtl8168_init_leds(struct net_device *ndev)
153153
{
154-
/* bind resource mgmt to netdev */
155-
struct device *dev = &ndev->dev;
156154
struct r8169_led_classdev *leds;
157155
int i;
158156

159-
leds = devm_kcalloc(dev, RTL8168_NUM_LEDS, sizeof(*leds), GFP_KERNEL);
157+
leds = kcalloc(RTL8168_NUM_LEDS + 1, sizeof(*leds), GFP_KERNEL);
160158
if (!leds)
161-
return;
159+
return NULL;
162160

163161
for (i = 0; i < RTL8168_NUM_LEDS; i++)
164162
rtl8168_setup_ldev(leds + i, ndev, i);
163+
164+
return leds;
165165
}
166166

167167
static int rtl8125_led_hw_control_is_supported(struct led_classdev *led_cdev,
@@ -245,20 +245,31 @@ static void rtl8125_setup_led_ldev(struct r8169_led_classdev *ldev,
245245
led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;
246246

247247
/* ignore errors */
248-
devm_led_classdev_register(&ndev->dev, led_cdev);
248+
led_classdev_register(&ndev->dev, led_cdev);
249249
}
250250

251-
void rtl8125_init_leds(struct net_device *ndev)
251+
struct r8169_led_classdev *rtl8125_init_leds(struct net_device *ndev)
252252
{
253-
/* bind resource mgmt to netdev */
254-
struct device *dev = &ndev->dev;
255253
struct r8169_led_classdev *leds;
256254
int i;
257255

258-
leds = devm_kcalloc(dev, RTL8125_NUM_LEDS, sizeof(*leds), GFP_KERNEL);
256+
leds = kcalloc(RTL8125_NUM_LEDS + 1, sizeof(*leds), GFP_KERNEL);
259257
if (!leds)
260-
return;
258+
return NULL;
261259

262260
for (i = 0; i < RTL8125_NUM_LEDS; i++)
263261
rtl8125_setup_led_ldev(leds + i, ndev, i);
262+
263+
return leds;
264+
}
265+
266+
void r8169_remove_leds(struct r8169_led_classdev *leds)
267+
{
268+
if (!leds)
269+
return;
270+
271+
for (struct r8169_led_classdev *l = leds; l->ndev; l++)
272+
led_classdev_unregister(&l->led);
273+
274+
kfree(leds);
264275
}

drivers/net/ethernet/realtek/r8169_main.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,8 @@ struct rtl8169_private {
647647
const char *fw_name;
648648
struct rtl_fw *rtl_fw;
649649

650+
struct r8169_led_classdev *leds;
651+
650652
u32 ocp_base;
651653
};
652654

@@ -5044,6 +5046,8 @@ static void rtl_remove_one(struct pci_dev *pdev)
50445046

50455047
cancel_work_sync(&tp->wk.work);
50465048

5049+
r8169_remove_leds(tp->leds);
5050+
50475051
unregister_netdev(tp->dev);
50485052

50495053
if (tp->dash_type != RTL_DASH_NONE)
@@ -5501,9 +5505,9 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
55015505

55025506
if (IS_ENABLED(CONFIG_R8169_LEDS)) {
55035507
if (rtl_is_8125(tp))
5504-
rtl8125_init_leds(dev);
5508+
tp->leds = rtl8125_init_leds(dev);
55055509
else if (tp->mac_version > RTL_GIGA_MAC_VER_06)
5506-
rtl8168_init_leds(dev);
5510+
tp->leds = rtl8168_init_leds(dev);
55075511
}
55085512

55095513
netdev_info(dev, "%s, %pM, XID %03x, IRQ %d\n",

0 commit comments

Comments
 (0)