diff options
author | David S. Miller <davem@davemloft.net> | 2015-03-18 22:52:33 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-03-18 22:52:56 -0400 |
commit | 99c4a26a159b28fa46a3e746a9b41b297e73d261 (patch) | |
tree | 38f5c5d424b7596b77e1f40d7c5f1ce33cfa115e /net/8021q | |
parent | 738e6d30d392fb75933a5eb4b481811598038786 (diff) |
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>
Diffstat (limited to 'net/8021q')
-rw-r--r-- | net/8021q/vlan.c | 16 |
1 files changed, 13 insertions, 3 deletions
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 64c6bed4a3d3..98a30a5b8664 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c | |||
@@ -413,7 +413,10 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, | |||
413 | vlan_transfer_features(dev, vlandev); | 413 | vlan_transfer_features(dev, vlandev); |
414 | break; | 414 | break; |
415 | 415 | ||
416 | case NETDEV_DOWN: | 416 | case NETDEV_DOWN: { |
417 | struct net_device *tmp; | ||
418 | LIST_HEAD(close_list); | ||
419 | |||
417 | if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) | 420 | if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) |
418 | vlan_vid_del(dev, htons(ETH_P_8021Q), 0); | 421 | vlan_vid_del(dev, htons(ETH_P_8021Q), 0); |
419 | 422 | ||
@@ -425,11 +428,18 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, | |||
425 | 428 | ||
426 | vlan = vlan_dev_priv(vlandev); | 429 | vlan = vlan_dev_priv(vlandev); |
427 | if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) | 430 | if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) |
428 | dev_change_flags(vlandev, flgs & ~IFF_UP); | 431 | list_add(&vlandev->close_list, &close_list); |
432 | } | ||
433 | |||
434 | dev_close_many(&close_list, false); | ||
435 | |||
436 | list_for_each_entry_safe(vlandev, tmp, &close_list, close_list) { | ||
429 | netif_stacked_transfer_operstate(dev, vlandev); | 437 | netif_stacked_transfer_operstate(dev, vlandev); |
438 | list_del_init(&vlandev->close_list); | ||
430 | } | 439 | } |
440 | list_del(&close_list); | ||
431 | break; | 441 | break; |
432 | 442 | } | |
433 | case NETDEV_UP: | 443 | case NETDEV_UP: |
434 | /* Put all VLANs for this dev in the up state too. */ | 444 | /* Put all VLANs for this dev in the up state too. */ |
435 | vlan_group_for_each_dev(grp, i, vlandev) { | 445 | vlan_group_for_each_dev(grp, i, vlandev) { |