Skip to content

Commit

Permalink
net: Introduce unregister_netdevice_many()
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Eric Dumazet authored and davem330 committed Oct 28, 2009
1 parent 44a0873 commit 9b5e383
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 32 deletions.
1 change: 1 addition & 0 deletions include/linux/netdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -1119,6 +1119,7 @@ extern int dev_queue_xmit(struct sk_buff *skb);
extern int register_netdevice(struct net_device *dev);
extern void unregister_netdevice_queue(struct net_device *dev,
struct list_head *head);
extern void unregister_netdevice_many(struct list_head *head);
static inline void unregister_netdevice(struct net_device *dev)
{
unregister_netdevice_queue(dev, NULL);
Expand Down
97 changes: 65 additions & 32 deletions net/core/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -4637,59 +4637,76 @@ static void net_set_todo(struct net_device *dev)
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);
ASSERT_RTNL();

/* Some devices call without registering for initialization unwind. */
if (dev->reg_state == NETREG_UNINITIALIZED) {
printk(KERN_DEBUG "unregister_netdevice: device %s/%p never "
"was registered\n", dev->name, dev);
list_for_each_entry(dev, head, unreg_list) {
/* Some devices call without registering
* for initialization unwind.
*/
if (dev->reg_state == NETREG_UNINITIALIZED) {
pr_debug("unregister_netdevice: device %s/%p never "
"was registered\n", dev->name, dev);

WARN_ON(1);
return;
}
WARN_ON(1);
return;
}

BUG_ON(dev->reg_state != NETREG_REGISTERED);
BUG_ON(dev->reg_state != NETREG_REGISTERED);

/* If device is running, close it first. */
dev_close(dev);
/* If device is running, close it first. */
dev_close(dev);

/* And unlink it from device chain. */
unlist_netdevice(dev);
/* And unlink it from device chain. */
unlist_netdevice(dev);

dev->reg_state = NETREG_UNREGISTERING;
dev->reg_state = NETREG_UNREGISTERING;
}

synchronize_net();

/* Shutdown queueing discipline. */
dev_shutdown(dev);
list_for_each_entry(dev, head, unreg_list) {
/* Shutdown queueing discipline. */
dev_shutdown(dev);


/* Notify protocols, that we are about to destroy
this device. They should clean all the things.
*/
call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
/* Notify protocols, that we are about to destroy
this device. They should clean all the things.
*/
call_netdevice_notifiers(NETDEV_UNREGISTER, dev);

/*
* Flush the unicast and multicast chains
*/
dev_unicast_flush(dev);
dev_addr_discard(dev);
/*
* Flush the unicast and multicast chains
*/
dev_unicast_flush(dev);
dev_addr_discard(dev);

if (dev->netdev_ops->ndo_uninit)
dev->netdev_ops->ndo_uninit(dev);
if (dev->netdev_ops->ndo_uninit)
dev->netdev_ops->ndo_uninit(dev);

/* Notifier chain MUST detach us from master device. */
WARN_ON(dev->master);
/* Notifier chain MUST detach us from master device. */
WARN_ON(dev->master);

/* Remove entries from kobject tree */
netdev_unregister_kobject(dev);
/* Remove entries from kobject tree */
netdev_unregister_kobject(dev);
}

synchronize_net();

dev_put(dev);
list_for_each_entry(dev, head, unreg_list)
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,
Expand Down Expand Up @@ -5271,6 +5288,22 @@ void unregister_netdevice_queue(struct net_device *dev, struct list_head *head)
}
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
* @dev: device
Expand Down

0 comments on commit 9b5e383

Please sign in to comment.