Skip to content

Commit 99c4a26

Browse files
committed
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>
1 parent 738e6d3 commit 99c4a26

File tree

3 files changed

+20
-7
lines changed

3 files changed

+20
-7
lines changed

include/linux/netdevice.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2156,6 +2156,7 @@ struct net_device *__dev_get_by_name(struct net *net, const char *name);
21562156
int dev_alloc_name(struct net_device *dev, const char *name);
21572157
int dev_open(struct net_device *dev);
21582158
int dev_close(struct net_device *dev);
2159+
int dev_close_many(struct list_head *head, bool unlink);
21592160
void dev_disable_lro(struct net_device *dev);
21602161
int dev_loopback_xmit(struct sk_buff *newskb);
21612162
int dev_queue_xmit(struct sk_buff *skb);

net/8021q/vlan.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,10 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
413413
vlan_transfer_features(dev, vlandev);
414414
break;
415415

416-
case NETDEV_DOWN:
416+
case NETDEV_DOWN: {
417+
struct net_device *tmp;
418+
LIST_HEAD(close_list);
419+
417420
if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
418421
vlan_vid_del(dev, htons(ETH_P_8021Q), 0);
419422

@@ -425,11 +428,18 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
425428

426429
vlan = vlan_dev_priv(vlandev);
427430
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) {
429437
netif_stacked_transfer_operstate(dev, vlandev);
438+
list_del_init(&vlandev->close_list);
430439
}
440+
list_del(&close_list);
431441
break;
432-
442+
}
433443
case NETDEV_UP:
434444
/* Put all VLANs for this dev in the up state too. */
435445
vlan_group_for_each_dev(grp, i, vlandev) {

net/core/dev.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,7 +1385,7 @@ static int __dev_close(struct net_device *dev)
13851385
return retval;
13861386
}
13871387

1388-
static int dev_close_many(struct list_head *head)
1388+
int dev_close_many(struct list_head *head, bool unlink)
13891389
{
13901390
struct net_device *dev, *tmp;
13911391

@@ -1399,11 +1399,13 @@ static int dev_close_many(struct list_head *head)
13991399
list_for_each_entry_safe(dev, tmp, head, close_list) {
14001400
rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING, GFP_KERNEL);
14011401
call_netdevice_notifiers(NETDEV_DOWN, dev);
1402-
list_del_init(&dev->close_list);
1402+
if (unlink)
1403+
list_del_init(&dev->close_list);
14031404
}
14041405

14051406
return 0;
14061407
}
1408+
EXPORT_SYMBOL(dev_close_many);
14071409

14081410
/**
14091411
* dev_close - shutdown an interface.
@@ -1420,7 +1422,7 @@ int dev_close(struct net_device *dev)
14201422
LIST_HEAD(single);
14211423

14221424
list_add(&dev->close_list, &single);
1423-
dev_close_many(&single);
1425+
dev_close_many(&single, true);
14241426
list_del(&single);
14251427
}
14261428
return 0;
@@ -5986,7 +5988,7 @@ static void rollback_registered_many(struct list_head *head)
59865988
/* If device is running, close it first. */
59875989
list_for_each_entry(dev, head, unreg_list)
59885990
list_add_tail(&dev->close_list, &close_head);
5989-
dev_close_many(&close_head);
5991+
dev_close_many(&close_head, true);
59905992

59915993
list_for_each_entry(dev, head, unreg_list) {
59925994
/* And unlink it from device chain. */

0 commit comments

Comments
 (0)