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: 18764b883e
("r8169: add support for LED's on RTL8168/RTL8101")
Cc: stable@vger.kernel.org
Reported-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
81665adf25
commit
19fa4f2a85
3 changed files with 33 additions and 16 deletions
|
@ -73,6 +73,7 @@ enum mac_version {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct rtl8169_private;
|
struct rtl8169_private;
|
||||||
|
struct r8169_led_classdev;
|
||||||
|
|
||||||
void r8169_apply_firmware(struct rtl8169_private *tp);
|
void r8169_apply_firmware(struct rtl8169_private *tp);
|
||||||
u16 rtl8168h_2_get_adc_bias_ioffset(struct rtl8169_private *tp);
|
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,
|
||||||
char *buf, int buf_len);
|
char *buf, int buf_len);
|
||||||
int rtl8168_get_led_mode(struct rtl8169_private *tp);
|
int rtl8168_get_led_mode(struct rtl8169_private *tp);
|
||||||
int rtl8168_led_mod_ctrl(struct rtl8169_private *tp, u16 mask, u16 val);
|
int rtl8168_led_mod_ctrl(struct rtl8169_private *tp, u16 mask, u16 val);
|
||||||
void rtl8168_init_leds(struct net_device *ndev);
|
struct r8169_led_classdev *rtl8168_init_leds(struct net_device *ndev);
|
||||||
int rtl8125_get_led_mode(struct rtl8169_private *tp, int index);
|
int rtl8125_get_led_mode(struct rtl8169_private *tp, int index);
|
||||||
int rtl8125_set_led_mode(struct rtl8169_private *tp, int index, u16 mode);
|
int rtl8125_set_led_mode(struct rtl8169_private *tp, int index, u16 mode);
|
||||||
void rtl8125_init_leds(struct net_device *ndev);
|
struct r8169_led_classdev *rtl8125_init_leds(struct net_device *ndev);
|
||||||
|
void r8169_remove_leds(struct r8169_led_classdev *leds);
|
||||||
|
|
|
@ -146,22 +146,22 @@ static void rtl8168_setup_ldev(struct r8169_led_classdev *ldev,
|
||||||
led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;
|
led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;
|
||||||
|
|
||||||
/* ignore errors */
|
/* ignore errors */
|
||||||
devm_led_classdev_register(&ndev->dev, led_cdev);
|
led_classdev_register(&ndev->dev, led_cdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rtl8168_init_leds(struct net_device *ndev)
|
struct r8169_led_classdev *rtl8168_init_leds(struct net_device *ndev)
|
||||||
{
|
{
|
||||||
/* bind resource mgmt to netdev */
|
|
||||||
struct device *dev = &ndev->dev;
|
|
||||||
struct r8169_led_classdev *leds;
|
struct r8169_led_classdev *leds;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
leds = devm_kcalloc(dev, RTL8168_NUM_LEDS, sizeof(*leds), GFP_KERNEL);
|
leds = kcalloc(RTL8168_NUM_LEDS + 1, sizeof(*leds), GFP_KERNEL);
|
||||||
if (!leds)
|
if (!leds)
|
||||||
return;
|
return NULL;
|
||||||
|
|
||||||
for (i = 0; i < RTL8168_NUM_LEDS; i++)
|
for (i = 0; i < RTL8168_NUM_LEDS; i++)
|
||||||
rtl8168_setup_ldev(leds + i, ndev, i);
|
rtl8168_setup_ldev(leds + i, ndev, i);
|
||||||
|
|
||||||
|
return leds;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rtl8125_led_hw_control_is_supported(struct led_classdev *led_cdev,
|
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,
|
||||||
led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;
|
led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;
|
||||||
|
|
||||||
/* ignore errors */
|
/* ignore errors */
|
||||||
devm_led_classdev_register(&ndev->dev, led_cdev);
|
led_classdev_register(&ndev->dev, led_cdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rtl8125_init_leds(struct net_device *ndev)
|
struct r8169_led_classdev *rtl8125_init_leds(struct net_device *ndev)
|
||||||
{
|
{
|
||||||
/* bind resource mgmt to netdev */
|
|
||||||
struct device *dev = &ndev->dev;
|
|
||||||
struct r8169_led_classdev *leds;
|
struct r8169_led_classdev *leds;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
leds = devm_kcalloc(dev, RTL8125_NUM_LEDS, sizeof(*leds), GFP_KERNEL);
|
leds = kcalloc(RTL8125_NUM_LEDS + 1, sizeof(*leds), GFP_KERNEL);
|
||||||
if (!leds)
|
if (!leds)
|
||||||
return;
|
return NULL;
|
||||||
|
|
||||||
for (i = 0; i < RTL8125_NUM_LEDS; i++)
|
for (i = 0; i < RTL8125_NUM_LEDS; i++)
|
||||||
rtl8125_setup_led_ldev(leds + i, ndev, i);
|
rtl8125_setup_led_ldev(leds + i, ndev, i);
|
||||||
|
|
||||||
|
return leds;
|
||||||
|
}
|
||||||
|
|
||||||
|
void r8169_remove_leds(struct r8169_led_classdev *leds)
|
||||||
|
{
|
||||||
|
if (!leds)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (struct r8169_led_classdev *l = leds; l->ndev; l++)
|
||||||
|
led_classdev_unregister(&l->led);
|
||||||
|
|
||||||
|
kfree(leds);
|
||||||
}
|
}
|
||||||
|
|
|
@ -647,6 +647,8 @@ struct rtl8169_private {
|
||||||
const char *fw_name;
|
const char *fw_name;
|
||||||
struct rtl_fw *rtl_fw;
|
struct rtl_fw *rtl_fw;
|
||||||
|
|
||||||
|
struct r8169_led_classdev *leds;
|
||||||
|
|
||||||
u32 ocp_base;
|
u32 ocp_base;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5044,6 +5046,8 @@ static void rtl_remove_one(struct pci_dev *pdev)
|
||||||
|
|
||||||
cancel_work_sync(&tp->wk.work);
|
cancel_work_sync(&tp->wk.work);
|
||||||
|
|
||||||
|
r8169_remove_leds(tp->leds);
|
||||||
|
|
||||||
unregister_netdev(tp->dev);
|
unregister_netdev(tp->dev);
|
||||||
|
|
||||||
if (tp->dash_type != RTL_DASH_NONE)
|
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)
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_R8169_LEDS)) {
|
if (IS_ENABLED(CONFIG_R8169_LEDS)) {
|
||||||
if (rtl_is_8125(tp))
|
if (rtl_is_8125(tp))
|
||||||
rtl8125_init_leds(dev);
|
tp->leds = rtl8125_init_leds(dev);
|
||||||
else if (tp->mac_version > RTL_GIGA_MAC_VER_06)
|
else if (tp->mac_version > RTL_GIGA_MAC_VER_06)
|
||||||
rtl8168_init_leds(dev);
|
tp->leds = rtl8168_init_leds(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
netdev_info(dev, "%s, %pM, XID %03x, IRQ %d\n",
|
netdev_info(dev, "%s, %pM, XID %03x, IRQ %d\n",
|
||||||
|
|
Loading…
Add table
Reference in a new issue