6.8-stable review patch. If anyone has any objections, please let me know.
------------------
From: Heiner Kallweit hkallweit1@gmail.com
commit 19fa4f2a85d777a8052e869c1b892a2f7556569d upstream.
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: 18764b883e15 ("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 Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- drivers/net/ethernet/realtek/r8169.h | 4 +++- drivers/net/ethernet/realtek/r8169_leds.c | 23 +++++++++++++++++------ drivers/net/ethernet/realtek/r8169_main.c | 6 +++++- 3 files changed, 25 insertions(+), 8 deletions(-)
--- a/drivers/net/ethernet/realtek/r8169.h +++ b/drivers/net/ethernet/realtek/r8169.h @@ -72,6 +72,7 @@ enum mac_version { };
struct rtl8169_private; +struct r8169_led_classdev;
void r8169_apply_firmware(struct rtl8169_private *tp); u16 rtl8168h_2_get_adc_bias_ioffset(struct rtl8169_private *tp); @@ -83,4 +84,5 @@ void r8169_get_led_name(struct rtl8169_p char *buf, int buf_len); int rtl8168_get_led_mode(struct rtl8169_private *tp); 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); +void r8169_remove_leds(struct r8169_led_classdev *leds); --- a/drivers/net/ethernet/realtek/r8169_leds.c +++ b/drivers/net/ethernet/realtek/r8169_leds.c @@ -138,20 +138,31 @@ static void rtl8168_setup_ldev(struct r8 led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;
/* 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; 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) - return; + return NULL;
for (i = 0; i < RTL8168_NUM_LEDS; i++) rtl8168_setup_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); } --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -634,6 +634,8 @@ struct rtl8169_private { const char *fw_name; struct rtl_fw *rtl_fw;
+ struct r8169_led_classdev *leds; + u32 ocp_base; };
@@ -4930,6 +4932,8 @@ static void rtl_remove_one(struct pci_de
cancel_work_sync(&tp->wk.work);
+ r8169_remove_leds(tp->leds); + unregister_netdev(tp->dev);
if (tp->dash_type != RTL_DASH_NONE) @@ -5391,7 +5395,7 @@ static int rtl_init_one(struct pci_dev * if (IS_ENABLED(CONFIG_R8169_LEDS) && tp->mac_version > RTL_GIGA_MAC_VER_06 && tp->mac_version < RTL_GIGA_MAC_VER_61) - rtl8168_init_leds(dev); + tp->leds = rtl8168_init_leds(dev);
netdev_info(dev, "%s, %pM, XID %03x, IRQ %d\n", rtl_chip_infos[chipset].name, dev->dev_addr, xid, tp->irq);