net: introduce NETDEV_UNREGISTER_PERNET
authorOctavian Purdila <opurdila@ixiacom.com>
Mon, 16 Nov 2009 13:49:35 +0000 (13:49 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 18 Nov 2009 13:03:03 +0000 (05:03 -0800)
This new event is called once for each unique net namespace in batched
unregister operations (with the argument set to a random device from
that namespace) and once per device in non-batched unregister
operations.

It allows us to factorize some device unregister work such as clearing the
routing cache.

Signed-off-by: Octavian Purdila <opurdila@ixiacom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/notifier.h
net/core/dev.c

index 29714b8..b0c3671 100644 (file)
@@ -202,6 +202,7 @@ static inline int notifier_to_errno(int ret)
 #define NETDEV_BONDING_OLDTYPE  0x000E
 #define NETDEV_BONDING_NEWTYPE  0x000F
 #define NETDEV_POST_INIT       0x0010
+#define NETDEV_UNREGISTER_PERNET 0x0011
 
 #define SYS_DOWN       0x0001  /* Notify of system down */
 #define SYS_RESTART    SYS_DOWN
index c3e0578..e25fe5d 100644 (file)
@@ -1344,6 +1344,7 @@ rollback:
                                nb->notifier_call(nb, NETDEV_DOWN, dev);
                        }
                        nb->notifier_call(nb, NETDEV_UNREGISTER, dev);
+                       nb->notifier_call(nb, NETDEV_UNREGISTER_PERNET, dev);
                }
        }
 
@@ -4721,7 +4722,8 @@ static void net_set_todo(struct net_device *dev)
 
 static void rollback_registered_many(struct list_head *head)
 {
-       struct net_device *dev;
+       struct net_device *dev, *aux, *fdev;
+       LIST_HEAD(pernet_list);
 
        BUG_ON(dev_boot_phase);
        ASSERT_RTNL();
@@ -4779,8 +4781,24 @@ static void rollback_registered_many(struct list_head *head)
 
        synchronize_net();
 
-       list_for_each_entry(dev, head, unreg_list)
+       list_for_each_entry_safe(dev, aux, head, unreg_list) {
+               int new_net = 1;
+               list_for_each_entry(fdev, &pernet_list, unreg_list) {
+                       if (dev_net(dev) == dev_net(fdev)) {
+                               new_net = 0;
+                               dev_put(dev);
+                               break;
+                       }
+               }
+               if (new_net)
+                       list_move(&dev->unreg_list, &pernet_list);
+       }
+
+       list_for_each_entry_safe(dev, aux, &pernet_list, unreg_list) {
+               call_netdevice_notifiers(NETDEV_UNREGISTER_PERNET, dev);
+               list_move(&dev->unreg_list, head);
                dev_put(dev);
+       }
 }
 
 static void rollback_registered(struct net_device *dev)
@@ -5074,6 +5092,8 @@ static void netdev_wait_allrefs(struct net_device *dev)
 
                        /* Rebroadcast unregister notification */
                        call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
+                       /* don't resend NETDEV_UNREGISTER_PERNET, _PERNET users
+                        * should have already handle it the first time */
 
                        if (test_bit(__LINK_STATE_LINKWATCH_PENDING,
                                     &dev->state)) {
@@ -5385,6 +5405,10 @@ EXPORT_SYMBOL(unregister_netdevice_queue);
  *     unregister_netdevice_many - unregister many devices
  *     @head: list of devices
  *
+ *     WARNING: Calling this modifies the given list
+ *     (in rollback_registered_many). It may change the order of the elements
+ *     in the list. However, you can assume it does not add or delete elements
+ *     to/from the list.
  */
 void unregister_netdevice_many(struct list_head *head)
 {
@@ -5504,6 +5528,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
           this device. They should clean all the things.
        */
        call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
+       call_netdevice_notifiers(NETDEV_UNREGISTER_PERNET, dev);
 
        /*
         *      Flush the unicast and multicast chains