aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2009-12-02 21:29:03 -0500
committerDavid S. Miller <davem@davemloft.net>2009-12-03 15:22:01 -0500
commit72ad937abd0a43b7cf2c557ba1f2ec75e608c516 (patch)
treeac81af750df170a46ed001fb5647b83dc20f8422
parent8153a10c08f1312af563bb92532002e46d3f504a (diff)
net: Add support for batching network namespace cleanups
- Add exit_list to struct net to support building lists of network namespaces to cleanup. - Add exit_batch to pernet_operations to allow running operations only once during a network namespace exit. Instead of once per network namespace. - Factor opt ops_exit_list and ops_exit_free so the logic with cleanup up a network namespace does not need to be duplicated. Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/net_namespace.h2
-rw-r--r--net/core/net_namespace.c122
2 files changed, 63 insertions, 61 deletions
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index 24a8c5591f63..f307e133d14c 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -43,6 +43,7 @@ struct net {
43#endif 43#endif
44 struct list_head list; /* list of network namespaces */ 44 struct list_head list; /* list of network namespaces */
45 struct list_head cleanup_list; /* namespaces on death row */ 45 struct list_head cleanup_list; /* namespaces on death row */
46 struct list_head exit_list; /* Use only net_mutex */
46 47
47 struct proc_dir_entry *proc_net; 48 struct proc_dir_entry *proc_net;
48 struct proc_dir_entry *proc_net_stat; 49 struct proc_dir_entry *proc_net_stat;
@@ -236,6 +237,7 @@ struct pernet_operations {
236 struct list_head list; 237 struct list_head list;
237 int (*init)(struct net *net); 238 int (*init)(struct net *net);
238 void (*exit)(struct net *net); 239 void (*exit)(struct net *net);
240 void (*exit_batch)(struct list_head *net_exit_list);
239 int *id; 241 int *id;
240 size_t size; 242 size_t size;
241}; 243};
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 9679ad292da9..6c7f6e04dbbf 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -70,6 +70,36 @@ static void ops_free(const struct pernet_operations *ops, struct net *net)
70 } 70 }
71} 71}
72 72
73static void ops_exit_list(const struct pernet_operations *ops,
74 struct list_head *net_exit_list)
75{
76 struct net *net;
77 if (ops->exit) {
78 list_for_each_entry(net, net_exit_list, exit_list)
79 ops->exit(net);
80 }
81 if (&ops->list == first_device) {
82 LIST_HEAD(dev_kill_list);
83 rtnl_lock();
84 list_for_each_entry(net, net_exit_list, exit_list)
85 unregister_netdevices(net, &dev_kill_list);
86 unregister_netdevice_many(&dev_kill_list);
87 rtnl_unlock();
88 }
89 if (ops->exit_batch)
90 ops->exit_batch(net_exit_list);
91}
92
93static void ops_free_list(const struct pernet_operations *ops,
94 struct list_head *net_exit_list)
95{
96 struct net *net;
97 if (ops->size && ops->id) {
98 list_for_each_entry(net, net_exit_list, exit_list)
99 ops_free(ops, net);
100 }
101}
102
73/* 103/*
74 * setup_net runs the initializers for the network namespace object. 104 * setup_net runs the initializers for the network namespace object.
75 */ 105 */
@@ -78,6 +108,7 @@ static __net_init int setup_net(struct net *net)
78 /* Must be called with net_mutex held */ 108 /* Must be called with net_mutex held */
79 const struct pernet_operations *ops, *saved_ops; 109 const struct pernet_operations *ops, *saved_ops;
80 int error = 0; 110 int error = 0;
111 LIST_HEAD(net_exit_list);
81 112
82 atomic_set(&net->count, 1); 113 atomic_set(&net->count, 1);
83 114
@@ -97,21 +128,14 @@ out_undo:
97 /* Walk through the list backwards calling the exit functions 128 /* Walk through the list backwards calling the exit functions
98 * for the pernet modules whose init functions did not fail. 129 * for the pernet modules whose init functions did not fail.
99 */ 130 */
131 list_add(&net->exit_list, &net_exit_list);
100 saved_ops = ops; 132 saved_ops = ops;
101 list_for_each_entry_continue_reverse(ops, &pernet_list, list) { 133 list_for_each_entry_continue_reverse(ops, &pernet_list, list)
102 if (ops->exit) 134 ops_exit_list(ops, &net_exit_list);
103 ops->exit(net); 135
104 if (&ops->list == first_device) {
105 LIST_HEAD(dev_kill_list);
106 rtnl_lock();
107 unregister_netdevices(net, &dev_kill_list);
108 unregister_netdevice_many(&dev_kill_list);
109 rtnl_unlock();
110 }
111 }
112 ops = saved_ops; 136 ops = saved_ops;
113 list_for_each_entry_continue_reverse(ops, &pernet_list, list) 137 list_for_each_entry_continue_reverse(ops, &pernet_list, list)
114 ops_free(ops, net); 138 ops_free_list(ops, &net_exit_list);
115 139
116 rcu_barrier(); 140 rcu_barrier();
117 goto out; 141 goto out;
@@ -207,6 +231,7 @@ static void cleanup_net(struct work_struct *work)
207 const struct pernet_operations *ops; 231 const struct pernet_operations *ops;
208 struct net *net, *tmp; 232 struct net *net, *tmp;
209 LIST_HEAD(net_kill_list); 233 LIST_HEAD(net_kill_list);
234 LIST_HEAD(net_exit_list);
210 235
211 /* Atomically snapshot the list of namespaces to cleanup */ 236 /* Atomically snapshot the list of namespaces to cleanup */
212 spin_lock_irq(&cleanup_list_lock); 237 spin_lock_irq(&cleanup_list_lock);
@@ -217,8 +242,10 @@ static void cleanup_net(struct work_struct *work)
217 242
218 /* Don't let anyone else find us. */ 243 /* Don't let anyone else find us. */
219 rtnl_lock(); 244 rtnl_lock();
220 list_for_each_entry(net, &net_kill_list, cleanup_list) 245 list_for_each_entry(net, &net_kill_list, cleanup_list) {
221 list_del_rcu(&net->list); 246 list_del_rcu(&net->list);
247 list_add_tail(&net->exit_list, &net_exit_list);
248 }
222 rtnl_unlock(); 249 rtnl_unlock();
223 250
224 /* 251 /*
@@ -229,27 +256,12 @@ static void cleanup_net(struct work_struct *work)
229 synchronize_rcu(); 256 synchronize_rcu();
230 257
231 /* Run all of the network namespace exit methods */ 258 /* Run all of the network namespace exit methods */
232 list_for_each_entry_reverse(ops, &pernet_list, list) { 259 list_for_each_entry_reverse(ops, &pernet_list, list)
233 if (ops->exit) { 260 ops_exit_list(ops, &net_exit_list);
234 list_for_each_entry(net, &net_kill_list, cleanup_list) 261
235 ops->exit(net);
236 }
237 if (&ops->list == first_device) {
238 LIST_HEAD(dev_kill_list);
239 rtnl_lock();
240 list_for_each_entry(net, &net_kill_list, cleanup_list)
241 unregister_netdevices(net, &dev_kill_list);
242 unregister_netdevice_many(&dev_kill_list);
243 rtnl_unlock();
244 }
245 }
246 /* Free the net generic variables */ 262 /* Free the net generic variables */
247 list_for_each_entry_reverse(ops, &pernet_list, list) { 263 list_for_each_entry_reverse(ops, &pernet_list, list)
248 if (ops->size && ops->id) { 264 ops_free_list(ops, &net_exit_list);
249 list_for_each_entry(net, &net_kill_list, cleanup_list)
250 ops_free(ops, net);
251 }
252 }
253 265
254 mutex_unlock(&net_mutex); 266 mutex_unlock(&net_mutex);
255 267
@@ -259,8 +271,8 @@ static void cleanup_net(struct work_struct *work)
259 rcu_barrier(); 271 rcu_barrier();
260 272
261 /* Finally it is safe to free my network namespace structure */ 273 /* Finally it is safe to free my network namespace structure */
262 list_for_each_entry_safe(net, tmp, &net_kill_list, cleanup_list) { 274 list_for_each_entry_safe(net, tmp, &net_exit_list, exit_list) {
263 list_del_init(&net->cleanup_list); 275 list_del_init(&net->exit_list);
264 net_free(net); 276 net_free(net);
265 } 277 }
266} 278}
@@ -348,8 +360,9 @@ pure_initcall(net_ns_init);
348static int __register_pernet_operations(struct list_head *list, 360static int __register_pernet_operations(struct list_head *list,
349 struct pernet_operations *ops) 361 struct pernet_operations *ops)
350{ 362{
351 struct net *net, *undo_net; 363 struct net *net;
352 int error; 364 int error;
365 LIST_HEAD(net_exit_list);
353 366
354 list_add_tail(&ops->list, list); 367 list_add_tail(&ops->list, list);
355 if (ops->init || (ops->id && ops->size)) { 368 if (ops->init || (ops->id && ops->size)) {
@@ -357,6 +370,7 @@ static int __register_pernet_operations(struct list_head *list,
357 error = ops_init(ops, net); 370 error = ops_init(ops, net);
358 if (error) 371 if (error)
359 goto out_undo; 372 goto out_undo;
373 list_add_tail(&net->exit_list, &net_exit_list);
360 } 374 }
361 } 375 }
362 return 0; 376 return 0;
@@ -364,36 +378,21 @@ static int __register_pernet_operations(struct list_head *list,
364out_undo: 378out_undo:
365 /* If I have an error cleanup all namespaces I initialized */ 379 /* If I have an error cleanup all namespaces I initialized */
366 list_del(&ops->list); 380 list_del(&ops->list);
367 if (ops->exit) { 381 ops_exit_list(ops, &net_exit_list);
368 for_each_net(undo_net) { 382 ops_free_list(ops, &net_exit_list);
369 if (net_eq(undo_net, net))
370 goto undone;
371 ops->exit(undo_net);
372 }
373 }
374undone:
375 if (ops->size && ops->id) {
376 for_each_net(undo_net) {
377 if (net_eq(undo_net, net))
378 goto freed;
379 ops_free(ops, undo_net);
380 }
381 }
382freed:
383 return error; 383 return error;
384} 384}
385 385
386static void __unregister_pernet_operations(struct pernet_operations *ops) 386static void __unregister_pernet_operations(struct pernet_operations *ops)
387{ 387{
388 struct net *net; 388 struct net *net;
389 LIST_HEAD(net_exit_list);
389 390
390 list_del(&ops->list); 391 list_del(&ops->list);
391 if (ops->exit) 392 for_each_net(net)
392 for_each_net(net) 393 list_add_tail(&net->exit_list, &net_exit_list);
393 ops->exit(net); 394 ops_exit_list(ops, &net_exit_list);
394 if (ops->id && ops->size) 395 ops_free_list(ops, &net_exit_list);
395 for_each_net(net)
396 ops_free(ops, net);
397} 396}
398 397
399#else 398#else
@@ -411,9 +410,10 @@ static int __register_pernet_operations(struct list_head *list,
411 410
412static void __unregister_pernet_operations(struct pernet_operations *ops) 411static void __unregister_pernet_operations(struct pernet_operations *ops)
413{ 412{
414 if (ops->exit) 413 LIST_HEAD(net_exit_list);
415 ops->exit(&init_net); 414 list_add(&init_net.exit_list, &net_exit_list);
416 ops_free(ops, &init_net); 415 ops_exit_list(ops, &net_exit_list);
416 ops_free_list(ops, &net_exit_list);
417} 417}
418 418
419#endif /* CONFIG_NET_NS */ 419#endif /* CONFIG_NET_NS */