net: Fix high overhead of vlan sub-device teardown.
When a networking device is taken down that has a non-trivial number of VLAN devices configured under it, we eat a full synchronize_net() for every such VLAN device. This is because of the call chain: NETDEV_DOWN notifier --> vlan_device_event() --> dev_change_flags() --> __dev_change_flags() --> __dev_close() --> __dev_close_many() --> dev_deactivate_many() --> synchronize_net() This is kind of rediculous because we already have infrastructure for batching doing operation X to a list of net devices so that we only incur one sync. So make use of that by exporting dev_close_many() and adjusting it's interfaace so that the caller can fully manage the batch list. Use this in vlan_device_event() and all the overhead goes away. Reported-by: Salam Noureddine <noureddine@arista.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
@@ -1385,7 +1385,7 @@ static int __dev_close(struct net_device *dev)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int dev_close_many(struct list_head *head)
|
||||
int dev_close_many(struct list_head *head, bool unlink)
|
||||
{
|
||||
struct net_device *dev, *tmp;
|
||||
|
||||
@@ -1399,11 +1399,13 @@ static int dev_close_many(struct list_head *head)
|
||||
list_for_each_entry_safe(dev, tmp, head, close_list) {
|
||||
rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING, GFP_KERNEL);
|
||||
call_netdevice_notifiers(NETDEV_DOWN, dev);
|
||||
list_del_init(&dev->close_list);
|
||||
if (unlink)
|
||||
list_del_init(&dev->close_list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(dev_close_many);
|
||||
|
||||
/**
|
||||
* dev_close - shutdown an interface.
|
||||
@@ -1420,7 +1422,7 @@ int dev_close(struct net_device *dev)
|
||||
LIST_HEAD(single);
|
||||
|
||||
list_add(&dev->close_list, &single);
|
||||
dev_close_many(&single);
|
||||
dev_close_many(&single, true);
|
||||
list_del(&single);
|
||||
}
|
||||
return 0;
|
||||
@@ -5986,7 +5988,7 @@ static void rollback_registered_many(struct list_head *head)
|
||||
/* If device is running, close it first. */
|
||||
list_for_each_entry(dev, head, unreg_list)
|
||||
list_add_tail(&dev->close_list, &close_head);
|
||||
dev_close_many(&close_head);
|
||||
dev_close_many(&close_head, true);
|
||||
|
||||
list_for_each_entry(dev, head, unreg_list) {
|
||||
/* And unlink it from device chain. */
|
||||
|
Reference in New Issue
Block a user