From: Rafał Miłecki Date: Wed, 6 Mar 2019 05:00:00 +0000 (+0100) Subject: kernel: fix refcnt leak in LED netdev trigger on interface rename X-Git-Tag: v17.01.7~18 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=981f5f7e40cbf26bc0beb0a2aa5f3c562b83c85b;p=oweals%2Fopenwrt.git kernel: fix refcnt leak in LED netdev trigger on interface rename Renaming a netdev-trigger-tracked interface was resulting in an unbalanced dev_hold(). Example: > iw phy phy0 interface add foo type __ap > echo netdev > trigger > echo foo > device_name > ip link set foo name bar > iw dev bar del [ 237.355366] unregister_netdevice: waiting for bar to become free. Usage count = 1 [ 247.435362] unregister_netdevice: waiting for bar to become free. Usage count = 1 [ 257.545366] unregister_netdevice: waiting for bar to become free. Usage count = 1 Above problem was caused by trigger checking a dev->name which obviously changes after renaming an interface. It meant missing all further events including the NETDEV_UNREGISTER which is required for calling dev_put(). This change fixes that by: 1) Comparing device struct *address* for notification-filtering purposes 2) Dropping unneeded NETDEV_CHANGENAME code (no behavior change) Signed-off-by: Rafał Miłecki (cherry picked from commit eea538204bb973d73d3bc3d38947d7f85214d486) --- diff --git a/target/linux/generic/files/drivers/leds/ledtrig-netdev.c b/target/linux/generic/files/drivers/leds/ledtrig-netdev.c index 7246bcaf7e..2d83541fef 100644 --- a/target/linux/generic/files/drivers/leds/ledtrig-netdev.c +++ b/target/linux/generic/files/drivers/leds/ledtrig-netdev.c @@ -264,39 +264,35 @@ static int netdev_trig_notify(struct notifier_block *nb, struct net_device *dev = netdev_notifier_info_to_dev((struct netdev_notifier_info *) dv); struct led_netdev_data *trigger_data = container_of(nb, struct led_netdev_data, notifier); - if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER && evt != NETDEV_CHANGENAME) + if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER) return NOTIFY_DONE; - if (strcmp(dev->name, trigger_data->device_name)) + if (!(dev == trigger_data->net_dev || + (evt == NETDEV_REGISTER && !strcmp(dev->name, trigger_data->device_name)))) return NOTIFY_DONE; cancel_delayed_work_sync(&trigger_data->work); spin_lock_bh(&trigger_data->lock); - if (evt == NETDEV_REGISTER || evt == NETDEV_CHANGENAME) { - if (trigger_data->net_dev != NULL) - dev_put(trigger_data->net_dev); - + switch (evt) { + case NETDEV_REGISTER: dev_hold(dev); trigger_data->net_dev = dev; trigger_data->link_up = 0; - goto done; - } - - if (evt == NETDEV_UNREGISTER && trigger_data->net_dev != NULL) { + break; + case NETDEV_UNREGISTER: dev_put(trigger_data->net_dev); trigger_data->net_dev = NULL; - goto done; + break; + default: /* UP / DOWN / CHANGE */ + trigger_data->link_up = (evt != NETDEV_DOWN && netif_carrier_ok(dev)); + set_baseline_state(trigger_data); + break; } - /* UP / DOWN / CHANGE */ - - trigger_data->link_up = (evt != NETDEV_DOWN && netif_carrier_ok(dev)); - set_baseline_state(trigger_data); - -done: spin_unlock_bh(&trigger_data->lock); + return NOTIFY_DONE; }