net: Introduce unregister_netdevice_many()
authorEric Dumazet <eric.dumazet@gmail.com>
Tue, 27 Oct 2009 07:04:19 +0000 (07:04 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 28 Oct 2009 09:22:06 +0000 (02:22 -0700)
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>
include/linux/netdevice.h
net/core/dev.c

index 0ded0a4..e7c227d 100644 (file)
@@ -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);
index ff94e2b..04d3e30 100644 (file)
@@ -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,
@@ -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