aboutsummaryrefslogtreecommitdiffstats
path: root/net/8021q
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2015-03-18 22:52:33 -0400
committerDavid S. Miller <davem@davemloft.net>2015-03-18 22:52:56 -0400
commit99c4a26a159b28fa46a3e746a9b41b297e73d261 (patch)
tree38f5c5d424b7596b77e1f40d7c5f1ce33cfa115e /net/8021q
parent738e6d30d392fb75933a5eb4b481811598038786 (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.c16
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) {