gre: Optimize multiple unregistration.
authorJesse Gross <jesse@nicira.com>
Tue, 2 Feb 2010 23:43:55 +0000 (18:43 -0500)
committerJesse Gross <jesse@nicira.com>
Thu, 4 Feb 2010 21:08:43 +0000 (16:08 -0500)
Ports commit eef6dd "gre: Optimize multiple unregistration" from
the mainline kernel.

datapath/linux-2.6/Modules.mk
datapath/linux-2.6/compat-2.6/dev-ip_gre.c [new file with mode: 0644]
datapath/linux-2.6/compat-2.6/include/linux/netdevice.h
datapath/linux-2.6/compat-2.6/ip_gre.c

index dafbcad..c15b735 100644 (file)
@@ -51,6 +51,7 @@ veth_headers =
 dist_modules += ip_gre
 build_modules += $(if $(BUILD_GRE),ip_gre)
 ip_gre_sources = \
+       linux-2.6/compat-2.6/dev-ip_gre.c \
        linux-2.6/compat-2.6/ip_gre.c \
        linux-2.6/compat-2.6/ip_output-ip_gre.c \
        linux-2.6/compat-2.6/net_namespace-ip_gre.c
diff --git a/datapath/linux-2.6/compat-2.6/dev-ip_gre.c b/datapath/linux-2.6/compat-2.6/dev-ip_gre.c
new file mode 100644 (file)
index 0000000..04d830e
--- /dev/null
@@ -0,0 +1,63 @@
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
+
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+
+struct netdev_list {
+       struct list_head unreg_list;
+       struct net_device *dev;
+};
+
+/**
+ *     unregister_netdevice_queue - remove device from the kernel
+ *     @dev: device
+ *     @head: list
+
+ *     This function shuts down a device interface and removes it
+ *     from the kernel tables.
+ *     If head not NULL, device is queued to be unregistered later.
+ *
+ *     Callers must hold the rtnl semaphore.  You may want
+ *     unregister_netdev() instead of this.
+ */
+
+void unregister_netdevice_queue(struct net_device *dev, struct list_head *head)
+{
+       ASSERT_RTNL();
+
+       if (head) {
+               struct netdev_list *list_item = kmalloc(sizeof *list_item,
+                                                       GFP_KERNEL);
+               /* If we can't queue it, probably better to try to destroy it
+                * now.  Either could potentially be bad but this is probably
+                * less likely to cause problems. */
+               if (!list_item) {
+                       unregister_netdevice(dev);
+                       return;
+               }
+
+               list_item->dev = dev;
+               list_add_tail(&list_item->unreg_list, head);
+       } else
+               unregister_netdevice(dev);
+}
+
+/**
+ *     unregister_netdevice_many - unregister many devices
+ *     @head: list of devices
+ *
+ */
+void unregister_netdevice_many(struct list_head *head)
+{
+       if (!list_empty(head)) {
+               struct netdev_list *list_item, *next;
+
+               list_for_each_entry_safe(list_item, next, head, unreg_list) {
+                       unregister_netdevice(list_item->dev);
+                       kfree(list_item);
+               }
+       }
+}
+
+#endif /* kernel < 2.6.33 */
index 0a0e47a..0cd91b9 100644 (file)
@@ -67,4 +67,10 @@ typedef int netdev_tx_t;
 #define net_xmit_eval(e)       ((e) == NET_XMIT_CN? 0 : (e))
 #endif
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
+extern void unregister_netdevice_queue(struct net_device *dev,
+                                       struct list_head *head);
+extern void unregister_netdevice_many(struct list_head *head);
+#endif
+
 #endif
index 8df5555..7b23d1a 100644 (file)
@@ -1446,16 +1446,19 @@ static const struct net_protocol ipgre_protocol = {
 #endif
 };
 
-static void ipgre_destroy_tunnels(struct ipgre_net *ign)
+static void ipgre_destroy_tunnels(struct ipgre_net *ign, struct list_head *head)
 {
        int prio;
 
        for (prio = 0; prio < 4; prio++) {
                int h;
                for (h = 0; h < HASH_SIZE; h++) {
-                       struct ip_tunnel *t;
-                       while ((t = ign->tunnels[prio][h]) != NULL)
-                               unregister_netdevice(t->dev);
+                       struct ip_tunnel *t = ign->tunnels[prio][h];
+
+                       while (t != NULL) {
+                               unregister_netdevice_queue(t->dev, head);
+                               t = t->next;
+                       }
                }
        }
 }
@@ -1509,10 +1512,12 @@ err_alloc:
 static void ipgre_exit_net(struct net *net)
 {
        struct ipgre_net *ign;
+       LIST_HEAD(list);
 
        ign = net_generic(net, ipgre_net_id);
        rtnl_lock();
-       ipgre_destroy_tunnels(ign);
+       ipgre_destroy_tunnels(ign, &list);
+       unregister_netdevice_many(&list);
        rtnl_unlock();
        kfree(ign);
 }