diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2009-10-27 03:06:49 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-10-28 05:22:08 -0400 |
commit | 63c8099d90096db56ee1c66c31f05d4fcfbc1c69 (patch) | |
tree | 1c0393ab776d49476858a4f97b3d60597330f370 | |
parent | 23289a37e2b127dfc4de1313fba15bb4c9f0cd5b (diff) |
vlan: Optimize multiple unregistration
Use unregister_netdevice_many() to speedup master device unregister.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/if_vlan.h | 1 | ||||
-rw-r--r-- | net/8021q/vlan.c | 49 | ||||
-rw-r--r-- | net/core/dev.c | 1 |
3 files changed, 35 insertions, 16 deletions
diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 8898cbebcf34..71a4870c09a9 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h | |||
@@ -85,6 +85,7 @@ struct vlan_group { | |||
85 | * the vlan is attached to. | 85 | * the vlan is attached to. |
86 | */ | 86 | */ |
87 | unsigned int nr_vlans; | 87 | unsigned int nr_vlans; |
88 | int killall; | ||
88 | struct hlist_node hlist; /* linked list */ | 89 | struct hlist_node hlist; /* linked list */ |
89 | struct net_device **vlan_devices_arrays[VLAN_GROUP_ARRAY_SPLIT_PARTS]; | 90 | struct net_device **vlan_devices_arrays[VLAN_GROUP_ARRAY_SPLIT_PARTS]; |
90 | struct rcu_head rcu; | 91 | struct rcu_head rcu; |
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 6b5c9dddaa72..511afe72af31 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c | |||
@@ -159,11 +159,12 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) | |||
159 | if (real_dev->features & NETIF_F_HW_VLAN_FILTER) | 159 | if (real_dev->features & NETIF_F_HW_VLAN_FILTER) |
160 | ops->ndo_vlan_rx_kill_vid(real_dev, vlan_id); | 160 | ops->ndo_vlan_rx_kill_vid(real_dev, vlan_id); |
161 | 161 | ||
162 | vlan_group_set_device(grp, vlan_id, NULL); | ||
163 | grp->nr_vlans--; | 162 | grp->nr_vlans--; |
164 | 163 | ||
165 | synchronize_net(); | 164 | if (!grp->killall) { |
166 | 165 | vlan_group_set_device(grp, vlan_id, NULL); | |
166 | synchronize_net(); | ||
167 | } | ||
167 | unregister_netdevice_queue(dev, head); | 168 | unregister_netdevice_queue(dev, head); |
168 | 169 | ||
169 | /* If the group is now empty, kill off the group. */ | 170 | /* If the group is now empty, kill off the group. */ |
@@ -183,6 +184,34 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) | |||
183 | dev_put(real_dev); | 184 | dev_put(real_dev); |
184 | } | 185 | } |
185 | 186 | ||
187 | void unregister_vlan_dev_alls(struct vlan_group *grp) | ||
188 | { | ||
189 | LIST_HEAD(list); | ||
190 | int i; | ||
191 | struct net_device *vlandev; | ||
192 | struct vlan_group save; | ||
193 | |||
194 | memcpy(&save, grp, sizeof(save)); | ||
195 | memset(&grp->vlan_devices_arrays, 0, sizeof(grp->vlan_devices_arrays)); | ||
196 | grp->killall = 1; | ||
197 | |||
198 | synchronize_net(); | ||
199 | |||
200 | /* Delete all VLANs for this dev. */ | ||
201 | for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { | ||
202 | vlandev = vlan_group_get_device(&save, i); | ||
203 | if (!vlandev) | ||
204 | continue; | ||
205 | |||
206 | unregister_vlan_dev(vlandev, &list); | ||
207 | if (grp->nr_vlans == 0) | ||
208 | break; | ||
209 | } | ||
210 | unregister_netdevice_many(&list); | ||
211 | for (i = 0; i < VLAN_GROUP_ARRAY_SPLIT_PARTS; i++) | ||
212 | kfree(save.vlan_devices_arrays[i]); | ||
213 | } | ||
214 | |||
186 | static void vlan_transfer_operstate(const struct net_device *dev, | 215 | static void vlan_transfer_operstate(const struct net_device *dev, |
187 | struct net_device *vlandev) | 216 | struct net_device *vlandev) |
188 | { | 217 | { |
@@ -524,19 +553,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, | |||
524 | break; | 553 | break; |
525 | 554 | ||
526 | case NETDEV_UNREGISTER: | 555 | case NETDEV_UNREGISTER: |
527 | /* Delete all VLANs for this dev. */ | 556 | unregister_vlan_dev_alls(grp); |
528 | for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { | ||
529 | vlandev = vlan_group_get_device(grp, i); | ||
530 | if (!vlandev) | ||
531 | continue; | ||
532 | |||
533 | /* unregistration of last vlan destroys group, abort | ||
534 | * afterwards */ | ||
535 | if (grp->nr_vlans == 1) | ||
536 | i = VLAN_GROUP_ARRAY_LEN; | ||
537 | |||
538 | unregister_vlan_dev(vlandev, NULL); | ||
539 | } | ||
540 | break; | 557 | break; |
541 | } | 558 | } |
542 | 559 | ||
diff --git a/net/core/dev.c b/net/core/dev.c index 4513dfd5718e..09551cc143a9 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -5303,6 +5303,7 @@ void unregister_netdevice_many(struct list_head *head) | |||
5303 | net_set_todo(dev); | 5303 | net_set_todo(dev); |
5304 | } | 5304 | } |
5305 | } | 5305 | } |
5306 | EXPORT_SYMBOL(unregister_netdevice_many); | ||
5306 | 5307 | ||
5307 | /** | 5308 | /** |
5308 | * unregister_netdev - remove device from the kernel | 5309 | * unregister_netdev - remove device from the kernel |