diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2009-10-27 03:04:19 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-10-28 05:22:06 -0400 |
commit | 9b5e383c11b08784eb0087617f880077982ef769 (patch) | |
tree | 090e01601641c39bfb160aa6772336e363628503 | |
parent | 44a0873d52282f24b1894c58c0f157e0f626ddc9 (diff) |
net: Introduce unregister_netdevice_many()
Introduce rollback_registered_many() and unregister_netdevice_many()
rollback_registered_many() is able to perform necessary steps at device dismantle
time, factorizing two expensive synchronize_net() calls.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/netdevice.h | 1 | ||||
-rw-r--r-- | net/core/dev.c | 97 |
2 files changed, 66 insertions, 32 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0ded0a4768a0..e7c227d7cb98 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
@@ -1119,6 +1119,7 @@ extern int dev_queue_xmit(struct sk_buff *skb); | |||
1119 | extern int register_netdevice(struct net_device *dev); | 1119 | extern int register_netdevice(struct net_device *dev); |
1120 | extern void unregister_netdevice_queue(struct net_device *dev, | 1120 | extern void unregister_netdevice_queue(struct net_device *dev, |
1121 | struct list_head *head); | 1121 | struct list_head *head); |
1122 | extern void unregister_netdevice_many(struct list_head *head); | ||
1122 | static inline void unregister_netdevice(struct net_device *dev) | 1123 | static inline void unregister_netdevice(struct net_device *dev) |
1123 | { | 1124 | { |
1124 | unregister_netdevice_queue(dev, NULL); | 1125 | unregister_netdevice_queue(dev, NULL); |
diff --git a/net/core/dev.c b/net/core/dev.c index ff94e2b8df7f..04d3e3014020 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -4637,59 +4637,76 @@ static void net_set_todo(struct net_device *dev) | |||
4637 | list_add_tail(&dev->todo_list, &net_todo_list); | 4637 | list_add_tail(&dev->todo_list, &net_todo_list); |
4638 | } | 4638 | } |
4639 | 4639 | ||
4640 | static void rollback_registered(struct net_device *dev) | 4640 | static void rollback_registered_many(struct list_head *head) |
4641 | { | 4641 | { |
4642 | struct net_device *dev; | ||
4643 | |||
4642 | BUG_ON(dev_boot_phase); | 4644 | BUG_ON(dev_boot_phase); |
4643 | ASSERT_RTNL(); | 4645 | ASSERT_RTNL(); |
4644 | 4646 | ||
4645 | /* Some devices call without registering for initialization unwind. */ | 4647 | list_for_each_entry(dev, head, unreg_list) { |
4646 | if (dev->reg_state == NETREG_UNINITIALIZED) { | 4648 | /* Some devices call without registering |
4647 | printk(KERN_DEBUG "unregister_netdevice: device %s/%p never " | 4649 | * for initialization unwind. |
4648 | "was registered\n", dev->name, dev); | 4650 | */ |
4651 | if (dev->reg_state == NETREG_UNINITIALIZED) { | ||
4652 | pr_debug("unregister_netdevice: device %s/%p never " | ||
4653 | "was registered\n", dev->name, dev); | ||
4649 | 4654 | ||
4650 | WARN_ON(1); | 4655 | WARN_ON(1); |
4651 | return; | 4656 | return; |
4652 | } | 4657 | } |
4653 | 4658 | ||
4654 | BUG_ON(dev->reg_state != NETREG_REGISTERED); | 4659 | BUG_ON(dev->reg_state != NETREG_REGISTERED); |
4655 | 4660 | ||
4656 | /* If device is running, close it first. */ | 4661 | /* If device is running, close it first. */ |
4657 | dev_close(dev); | 4662 | dev_close(dev); |
4658 | 4663 | ||
4659 | /* And unlink it from device chain. */ | 4664 | /* And unlink it from device chain. */ |
4660 | unlist_netdevice(dev); | 4665 | unlist_netdevice(dev); |
4661 | 4666 | ||
4662 | dev->reg_state = NETREG_UNREGISTERING; | 4667 | dev->reg_state = NETREG_UNREGISTERING; |
4668 | } | ||
4663 | 4669 | ||
4664 | synchronize_net(); | 4670 | synchronize_net(); |
4665 | 4671 | ||
4666 | /* Shutdown queueing discipline. */ | 4672 | list_for_each_entry(dev, head, unreg_list) { |
4667 | dev_shutdown(dev); | 4673 | /* Shutdown queueing discipline. */ |
4674 | dev_shutdown(dev); | ||
4668 | 4675 | ||
4669 | 4676 | ||
4670 | /* Notify protocols, that we are about to destroy | 4677 | /* Notify protocols, that we are about to destroy |
4671 | this device. They should clean all the things. | 4678 | this device. They should clean all the things. |
4672 | */ | 4679 | */ |
4673 | call_netdevice_notifiers(NETDEV_UNREGISTER, dev); | 4680 | call_netdevice_notifiers(NETDEV_UNREGISTER, dev); |
4674 | 4681 | ||
4675 | /* | 4682 | /* |
4676 | * Flush the unicast and multicast chains | 4683 | * Flush the unicast and multicast chains |
4677 | */ | 4684 | */ |
4678 | dev_unicast_flush(dev); | 4685 | dev_unicast_flush(dev); |
4679 | dev_addr_discard(dev); | 4686 | dev_addr_discard(dev); |
4680 | 4687 | ||
4681 | if (dev->netdev_ops->ndo_uninit) | 4688 | if (dev->netdev_ops->ndo_uninit) |
4682 | dev->netdev_ops->ndo_uninit(dev); | 4689 | dev->netdev_ops->ndo_uninit(dev); |
4683 | 4690 | ||
4684 | /* Notifier chain MUST detach us from master device. */ | 4691 | /* Notifier chain MUST detach us from master device. */ |
4685 | WARN_ON(dev->master); | 4692 | WARN_ON(dev->master); |
4686 | 4693 | ||
4687 | /* Remove entries from kobject tree */ | 4694 | /* Remove entries from kobject tree */ |
4688 | netdev_unregister_kobject(dev); | 4695 | netdev_unregister_kobject(dev); |
4696 | } | ||
4689 | 4697 | ||
4690 | synchronize_net(); | 4698 | synchronize_net(); |
4691 | 4699 | ||
4692 | dev_put(dev); | 4700 | list_for_each_entry(dev, head, unreg_list) |
4701 | dev_put(dev); | ||
4702 | } | ||
4703 | |||
4704 | static void rollback_registered(struct net_device *dev) | ||
4705 | { | ||
4706 | LIST_HEAD(single); | ||
4707 | |||
4708 | list_add(&dev->unreg_list, &single); | ||
4709 | rollback_registered_many(&single); | ||
4693 | } | 4710 | } |
4694 | 4711 | ||
4695 | static void __netdev_init_queue_locks_one(struct net_device *dev, | 4712 | static void __netdev_init_queue_locks_one(struct net_device *dev, |
@@ -5272,6 +5289,22 @@ void unregister_netdevice_queue(struct net_device *dev, struct list_head *head) | |||
5272 | EXPORT_SYMBOL(unregister_netdevice_queue); | 5289 | EXPORT_SYMBOL(unregister_netdevice_queue); |
5273 | 5290 | ||
5274 | /** | 5291 | /** |
5292 | * unregister_netdevice_many - unregister many devices | ||
5293 | * @head: list of devices | ||
5294 | * | ||
5295 | */ | ||
5296 | void unregister_netdevice_many(struct list_head *head) | ||
5297 | { | ||
5298 | struct net_device *dev; | ||
5299 | |||
5300 | if (!list_empty(head)) { | ||
5301 | rollback_registered_many(head); | ||
5302 | list_for_each_entry(dev, head, unreg_list) | ||
5303 | net_set_todo(dev); | ||
5304 | } | ||
5305 | } | ||
5306 | |||
5307 | /** | ||
5275 | * unregister_netdev - remove device from the kernel | 5308 | * unregister_netdev - remove device from the kernel |
5276 | * @dev: device | 5309 | * @dev: device |
5277 | * | 5310 | * |