diff options
author | Rafał Miłecki <rafal@milecki.pl> | 2019-03-06 06:00:00 +0100 |
---|---|---|
committer | Rafał Miłecki <rafal@milecki.pl> | 2019-03-06 06:08:15 +0100 |
commit | eea538204bb973d73d3bc3d38947d7f85214d486 (patch) | |
tree | 3ffe2b4db30664cdd5d3ed7b90b07f5bc9f0e6c5 /target | |
parent | f1803e349297d82fa7b7eaac302db653ed5dc868 (diff) | |
download | upstream-eea538204bb973d73d3bc3d38947d7f85214d486.tar.gz upstream-eea538204bb973d73d3bc3d38947d7f85214d486.tar.bz2 upstream-eea538204bb973d73d3bc3d38947d7f85214d486.zip |
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 <rafal@milecki.pl>
Diffstat (limited to 'target')
-rw-r--r-- | target/linux/generic/files/drivers/leds/ledtrig-netdev.c | 30 |
1 files changed, 13 insertions, 17 deletions
diff --git a/target/linux/generic/files/drivers/leds/ledtrig-netdev.c b/target/linux/generic/files/drivers/leds/ledtrig-netdev.c index 8d3249010d..1c7c1c123a 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; } |