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>
This commit is contained in:
Eric Dumazet 2009-10-27 07:04:19 +00:00 committed by David S. Miller
parent 44a0873d52
commit 9b5e383c11
2 changed files with 72 additions and 38 deletions

View File

@ -1119,6 +1119,7 @@ extern int dev_queue_xmit(struct sk_buff *skb);
extern int register_netdevice(struct net_device *dev); extern int register_netdevice(struct net_device *dev);
extern void unregister_netdevice_queue(struct net_device *dev, extern void unregister_netdevice_queue(struct net_device *dev,
struct list_head *head); struct list_head *head);
extern void unregister_netdevice_many(struct list_head *head);
static inline void unregister_netdevice(struct net_device *dev) static inline void unregister_netdevice(struct net_device *dev)
{ {
unregister_netdevice_queue(dev, NULL); unregister_netdevice_queue(dev, NULL);

View File

@ -4637,14 +4637,19 @@ static void net_set_todo(struct net_device *dev)
list_add_tail(&dev->todo_list, &net_todo_list); list_add_tail(&dev->todo_list, &net_todo_list);
} }
static void rollback_registered(struct net_device *dev) static void rollback_registered_many(struct list_head *head)
{ {
struct net_device *dev;
BUG_ON(dev_boot_phase); BUG_ON(dev_boot_phase);
ASSERT_RTNL(); ASSERT_RTNL();
/* Some devices call without registering for initialization unwind. */ list_for_each_entry(dev, head, unreg_list) {
/* Some devices call without registering
* for initialization unwind.
*/
if (dev->reg_state == NETREG_UNINITIALIZED) { if (dev->reg_state == NETREG_UNINITIALIZED) {
printk(KERN_DEBUG "unregister_netdevice: device %s/%p never " pr_debug("unregister_netdevice: device %s/%p never "
"was registered\n", dev->name, dev); "was registered\n", dev->name, dev);
WARN_ON(1); WARN_ON(1);
@ -4660,9 +4665,11 @@ static void rollback_registered(struct net_device *dev)
unlist_netdevice(dev); unlist_netdevice(dev);
dev->reg_state = NETREG_UNREGISTERING; dev->reg_state = NETREG_UNREGISTERING;
}
synchronize_net(); synchronize_net();
list_for_each_entry(dev, head, unreg_list) {
/* Shutdown queueing discipline. */ /* Shutdown queueing discipline. */
dev_shutdown(dev); dev_shutdown(dev);
@ -4686,12 +4693,22 @@ static void rollback_registered(struct net_device *dev)
/* Remove entries from kobject tree */ /* Remove entries from kobject tree */
netdev_unregister_kobject(dev); netdev_unregister_kobject(dev);
}
synchronize_net(); synchronize_net();
list_for_each_entry(dev, head, unreg_list)
dev_put(dev); dev_put(dev);
} }
static void rollback_registered(struct net_device *dev)
{
LIST_HEAD(single);
list_add(&dev->unreg_list, &single);
rollback_registered_many(&single);
}
static void __netdev_init_queue_locks_one(struct net_device *dev, static void __netdev_init_queue_locks_one(struct net_device *dev,
struct netdev_queue *dev_queue, struct netdev_queue *dev_queue,
void *_unused) void *_unused)
@ -5271,6 +5288,22 @@ void unregister_netdevice_queue(struct net_device *dev, struct list_head *head)
} }
EXPORT_SYMBOL(unregister_netdevice_queue); EXPORT_SYMBOL(unregister_netdevice_queue);
/**
* unregister_netdevice_many - unregister many devices
* @head: list of devices
*
*/
void unregister_netdevice_many(struct list_head *head)
{
struct net_device *dev;
if (!list_empty(head)) {
rollback_registered_many(head);
list_for_each_entry(dev, head, unreg_list)
net_set_todo(dev);
}
}
/** /**
* unregister_netdev - remove device from the kernel * unregister_netdev - remove device from the kernel
* @dev: device * @dev: device