From fb214965c60bdc7f7ff139356e50916bdabda9be Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Tue, 2 Feb 2010 18:43:55 -0500 Subject: [PATCH] gre: Optimize multiple unregistration. Ports commit eef6dd "gre: Optimize multiple unregistration" from the mainline kernel. --- datapath/linux-2.6/Modules.mk | 1 + datapath/linux-2.6/compat-2.6/dev-ip_gre.c | 63 +++++++++++++++++++ .../compat-2.6/include/linux/netdevice.h | 6 ++ datapath/linux-2.6/compat-2.6/ip_gre.c | 15 +++-- 4 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 datapath/linux-2.6/compat-2.6/dev-ip_gre.c diff --git a/datapath/linux-2.6/Modules.mk b/datapath/linux-2.6/Modules.mk index dafbcad7..c15b735f 100644 --- a/datapath/linux-2.6/Modules.mk +++ b/datapath/linux-2.6/Modules.mk @@ -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 index 00000000..04d830e7 --- /dev/null +++ b/datapath/linux-2.6/compat-2.6/dev-ip_gre.c @@ -0,0 +1,63 @@ +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) + +#include +#include + +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 */ diff --git a/datapath/linux-2.6/compat-2.6/include/linux/netdevice.h b/datapath/linux-2.6/compat-2.6/include/linux/netdevice.h index 0a0e47ab..0cd91b9f 100644 --- a/datapath/linux-2.6/compat-2.6/include/linux/netdevice.h +++ b/datapath/linux-2.6/compat-2.6/include/linux/netdevice.h @@ -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 diff --git a/datapath/linux-2.6/compat-2.6/ip_gre.c b/datapath/linux-2.6/compat-2.6/ip_gre.c index 8df55551..7b23d1aa 100644 --- a/datapath/linux-2.6/compat-2.6/ip_gre.c +++ b/datapath/linux-2.6/compat-2.6/ip_gre.c @@ -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); } -- 2.30.2