aboutsummaryrefslogtreecommitdiffstats
path: root/net/core
diff options
context:
space:
mode:
authorEric W. Biederman <eric@conroxe.ebiederm.org>2009-11-29 17:25:27 -0500
committerDavid S. Miller <davem@davemloft.net>2009-12-01 19:15:51 -0500
commit2b035b39970740722598f7a9d548835f9bdd730f (patch)
treea240b595c380b50f783fe8b3d3d4e9cb612cbb30 /net/core
parentdcbccbd4f1f6ad0f0e169d4b2e816e42bde06f82 (diff)
net: Batch network namespace destruction.
It is fairly common to kill several network namespaces at once. Either because they are nested one inside the other or because they are cooperating in multiple machine networking experiments. As the network stack control logic does not parallelize easily batch up multiple network namespaces existing together. To get the full benefit of batching the virtual network devices to be removed must be all removed in one batch. For that purpose I have added a loop after the last network device operations have run that batches up all remaining network devices and deletes them. An extra benefit is that the reorganization slightly shrinks the size of the per network namespace data structures replaceing a work_struct with a list_head. In a trivial test with 4K namespaces this change reduced the cost of a destroying 4K namespaces from 7+ minutes (at 12% cpu) to 44 seconds (at 60% cpu). The bulk of that 44s was spent in inet_twsk_purge. Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core')
-rw-r--r--net/core/net_namespace.c66
1 files changed, 58 insertions, 8 deletions
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 86ed7f44d083..a42caa2b909b 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -8,8 +8,10 @@
8#include <linux/idr.h> 8#include <linux/idr.h>
9#include <linux/rculist.h> 9#include <linux/rculist.h>
10#include <linux/nsproxy.h> 10#include <linux/nsproxy.h>
11#include <linux/netdevice.h>
11#include <net/net_namespace.h> 12#include <net/net_namespace.h>
12#include <net/netns/generic.h> 13#include <net/netns/generic.h>
14#include <net/rtnetlink.h>
13 15
14/* 16/*
15 * Our network namespace constructor/destructor lists 17 * Our network namespace constructor/destructor lists
@@ -27,6 +29,20 @@ EXPORT_SYMBOL(init_net);
27 29
28#define INITIAL_NET_GEN_PTRS 13 /* +1 for len +2 for rcu_head */ 30#define INITIAL_NET_GEN_PTRS 13 /* +1 for len +2 for rcu_head */
29 31
32static void unregister_netdevices(struct net *net, struct list_head *list)
33{
34 struct net_device *dev;
35 /* At exit all network devices most be removed from a network
36 * namespace. Do this in the reverse order of registeration.
37 */
38 for_each_netdev_reverse(net, dev) {
39 if (dev->rtnl_link_ops)
40 dev->rtnl_link_ops->dellink(dev, list);
41 else
42 unregister_netdevice_queue(dev, list);
43 }
44}
45
30/* 46/*
31 * setup_net runs the initializers for the network namespace object. 47 * setup_net runs the initializers for the network namespace object.
32 */ 48 */
@@ -59,6 +75,13 @@ out_undo:
59 list_for_each_entry_continue_reverse(ops, &pernet_list, list) { 75 list_for_each_entry_continue_reverse(ops, &pernet_list, list) {
60 if (ops->exit) 76 if (ops->exit)
61 ops->exit(net); 77 ops->exit(net);
78 if (&ops->list == first_device) {
79 LIST_HEAD(dev_kill_list);
80 rtnl_lock();
81 unregister_netdevices(net, &dev_kill_list);
82 unregister_netdevice_many(&dev_kill_list);
83 rtnl_unlock();
84 }
62 } 85 }
63 86
64 rcu_barrier(); 87 rcu_barrier();
@@ -147,18 +170,26 @@ struct net *copy_net_ns(unsigned long flags, struct net *old_net)
147 return net_create(); 170 return net_create();
148} 171}
149 172
173static DEFINE_SPINLOCK(cleanup_list_lock);
174static LIST_HEAD(cleanup_list); /* Must hold cleanup_list_lock to touch */
175
150static void cleanup_net(struct work_struct *work) 176static void cleanup_net(struct work_struct *work)
151{ 177{
152 struct pernet_operations *ops; 178 struct pernet_operations *ops;
153 struct net *net; 179 struct net *net, *tmp;
180 LIST_HEAD(net_kill_list);
154 181
155 net = container_of(work, struct net, work); 182 /* Atomically snapshot the list of namespaces to cleanup */
183 spin_lock_irq(&cleanup_list_lock);
184 list_replace_init(&cleanup_list, &net_kill_list);
185 spin_unlock_irq(&cleanup_list_lock);
156 186
157 mutex_lock(&net_mutex); 187 mutex_lock(&net_mutex);
158 188
159 /* Don't let anyone else find us. */ 189 /* Don't let anyone else find us. */
160 rtnl_lock(); 190 rtnl_lock();
161 list_del_rcu(&net->list); 191 list_for_each_entry(net, &net_kill_list, cleanup_list)
192 list_del_rcu(&net->list);
162 rtnl_unlock(); 193 rtnl_unlock();
163 194
164 /* 195 /*
@@ -170,8 +201,18 @@ static void cleanup_net(struct work_struct *work)
170 201
171 /* Run all of the network namespace exit methods */ 202 /* Run all of the network namespace exit methods */
172 list_for_each_entry_reverse(ops, &pernet_list, list) { 203 list_for_each_entry_reverse(ops, &pernet_list, list) {
173 if (ops->exit) 204 if (ops->exit) {
174 ops->exit(net); 205 list_for_each_entry(net, &net_kill_list, cleanup_list)
206 ops->exit(net);
207 }
208 if (&ops->list == first_device) {
209 LIST_HEAD(dev_kill_list);
210 rtnl_lock();
211 list_for_each_entry(net, &net_kill_list, cleanup_list)
212 unregister_netdevices(net, &dev_kill_list);
213 unregister_netdevice_many(&dev_kill_list);
214 rtnl_unlock();
215 }
175 } 216 }
176 217
177 mutex_unlock(&net_mutex); 218 mutex_unlock(&net_mutex);
@@ -182,14 +223,23 @@ static void cleanup_net(struct work_struct *work)
182 rcu_barrier(); 223 rcu_barrier();
183 224
184 /* Finally it is safe to free my network namespace structure */ 225 /* Finally it is safe to free my network namespace structure */
185 net_free(net); 226 list_for_each_entry_safe(net, tmp, &net_kill_list, cleanup_list) {
227 list_del_init(&net->cleanup_list);
228 net_free(net);
229 }
186} 230}
231static DECLARE_WORK(net_cleanup_work, cleanup_net);
187 232
188void __put_net(struct net *net) 233void __put_net(struct net *net)
189{ 234{
190 /* Cleanup the network namespace in process context */ 235 /* Cleanup the network namespace in process context */
191 INIT_WORK(&net->work, cleanup_net); 236 unsigned long flags;
192 queue_work(netns_wq, &net->work); 237
238 spin_lock_irqsave(&cleanup_list_lock, flags);
239 list_add(&net->cleanup_list, &cleanup_list);
240 spin_unlock_irqrestore(&cleanup_list_lock, flags);
241
242 queue_work(netns_wq, &net_cleanup_work);
193} 243}
194EXPORT_SYMBOL_GPL(__put_net); 244EXPORT_SYMBOL_GPL(__put_net);
195 245