From 26c913e44efb720252bf7420fca1aba0569a54cb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 27 Jan 2016 12:37:52 +0100 Subject: [PATCH] wext: fix message delay/ordering Beniamino reported that he was getting an RTM_NEWLINK message for a given interface, after the RTM_DELLINK for it. It turns out that the message is a wireless extensions message, which was sent because the interface had been connected and disconnection while it was deleted caused a wext message. It's strange that wext uses RTM_NEWLINK, and the message is somewhat malformed without any proper attributes, causing "ip monitor link" to print just rudimentary information: 5: wlan1: mtu 1500 qdisc mq state DOWN group default link/ether 02:00:00:00:01:00 brd ff:ff:ff:ff:ff:ff Deleted 5: wlan1: mtu 1500 qdisc noop state DOWN group default link/ether 02:00:00:00:01:00 brd ff:ff:ff:ff:ff:ff 5: wlan1: link/ether (from my hwsim reproduction) The reason for this is that wext schedules a worker to send out the messages, and the scheduling delay can cause the messages to get out to userspace after the RTM_DELLINK. To fix this, have wext register a netdevice notifier and flush out any pending messages when a netdevice is unregistered. Reported-by: Beniamino Galvani Signed-off-by: Johannes Berg --- net/wireless/wext-core.c | 48 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c index c8717c1d082e..4e4e1c5236cc 100644 --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c @@ -342,6 +342,38 @@ static const int compat_event_type_size[] = { /* IW event code */ +static void wireless_nlevent_flush(void) +{ + struct sk_buff *skb; + struct net *net; + + ASSERT_RTNL(); + + for_each_net(net) { + while ((skb = skb_dequeue(&net->wext_nlevents))) + rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, + GFP_KERNEL); + } +} + +static int wext_netdev_notifier_call(struct notifier_block *nb, + unsigned long state, void *ptr) +{ + /* + * When a netdev is unregistered, flush all pending messages + * to avoid them going out after the RTM_DELLINK, which can + * happen due to the schedule_work(). + */ + if (state == NETDEV_UNREGISTER) + wireless_nlevent_flush(); + + return NOTIFY_OK; +} + +static struct notifier_block wext_netdev_notifier = { + .notifier_call = wext_netdev_notifier_call, +}; + static int __net_init wext_pernet_init(struct net *net) { skb_queue_head_init(&net->wext_nlevents); @@ -360,6 +392,11 @@ static struct pernet_operations wext_pernet_ops = { static int __init wireless_nlevent_init(void) { + int err = register_netdevice_notifier(&wext_netdev_notifier); + + if (err) + return err; + return register_pernet_subsys(&wext_pernet_ops); } @@ -368,17 +405,8 @@ subsys_initcall(wireless_nlevent_init); /* Process events generated by the wireless layer or the driver. */ static void wireless_nlevent_process(struct work_struct *work) { - struct sk_buff *skb; - struct net *net; - rtnl_lock(); - - for_each_net(net) { - while ((skb = skb_dequeue(&net->wext_nlevents))) - rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, - GFP_KERNEL); - } - + wireless_nlevent_flush(); rtnl_unlock(); } -- 2.6.2