diff options
-rw-r--r-- | include/net/net_namespace.h | 28 | ||||
-rw-r--r-- | net/core/net_namespace.c | 188 |
2 files changed, 126 insertions, 90 deletions
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index d69b4796030f..080774b9dbf3 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h | |||
@@ -236,6 +236,8 @@ struct pernet_operations { | |||
236 | struct list_head list; | 236 | struct list_head list; |
237 | int (*init)(struct net *net); | 237 | int (*init)(struct net *net); |
238 | void (*exit)(struct net *net); | 238 | void (*exit)(struct net *net); |
239 | int *id; | ||
240 | size_t size; | ||
239 | }; | 241 | }; |
240 | 242 | ||
241 | /* | 243 | /* |
@@ -259,12 +261,30 @@ struct pernet_operations { | |||
259 | */ | 261 | */ |
260 | extern int register_pernet_subsys(struct pernet_operations *); | 262 | extern int register_pernet_subsys(struct pernet_operations *); |
261 | extern void unregister_pernet_subsys(struct pernet_operations *); | 263 | extern void unregister_pernet_subsys(struct pernet_operations *); |
262 | extern int register_pernet_gen_subsys(int *id, struct pernet_operations *); | ||
263 | extern void unregister_pernet_gen_subsys(int id, struct pernet_operations *); | ||
264 | extern int register_pernet_device(struct pernet_operations *); | 264 | extern int register_pernet_device(struct pernet_operations *); |
265 | extern void unregister_pernet_device(struct pernet_operations *); | 265 | extern void unregister_pernet_device(struct pernet_operations *); |
266 | extern int register_pernet_gen_device(int *id, struct pernet_operations *); | 266 | |
267 | extern void unregister_pernet_gen_device(int id, struct pernet_operations *); | 267 | static inline int register_pernet_gen_subsys(int *id, struct pernet_operations *ops) |
268 | { | ||
269 | ops->id = id; | ||
270 | return register_pernet_subsys(ops); | ||
271 | } | ||
272 | |||
273 | static inline void unregister_pernet_gen_subsys(int id, struct pernet_operations *ops) | ||
274 | { | ||
275 | return unregister_pernet_subsys(ops); | ||
276 | } | ||
277 | |||
278 | static inline int register_pernet_gen_device(int *id, struct pernet_operations *ops) | ||
279 | { | ||
280 | ops->id = id; | ||
281 | return register_pernet_device(ops); | ||
282 | } | ||
283 | |||
284 | static inline void unregister_pernet_gen_device(int id, struct pernet_operations *ops) | ||
285 | { | ||
286 | return unregister_pernet_device(ops); | ||
287 | } | ||
268 | 288 | ||
269 | struct ctl_path; | 289 | struct ctl_path; |
270 | struct ctl_table; | 290 | struct ctl_table; |
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index a42caa2b909b..9679ad292da9 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c | |||
@@ -43,13 +43,40 @@ static void unregister_netdevices(struct net *net, struct list_head *list) | |||
43 | } | 43 | } |
44 | } | 44 | } |
45 | 45 | ||
46 | static int ops_init(const struct pernet_operations *ops, struct net *net) | ||
47 | { | ||
48 | int err; | ||
49 | if (ops->id && ops->size) { | ||
50 | void *data = kzalloc(ops->size, GFP_KERNEL); | ||
51 | if (!data) | ||
52 | return -ENOMEM; | ||
53 | |||
54 | err = net_assign_generic(net, *ops->id, data); | ||
55 | if (err) { | ||
56 | kfree(data); | ||
57 | return err; | ||
58 | } | ||
59 | } | ||
60 | if (ops->init) | ||
61 | return ops->init(net); | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | static void ops_free(const struct pernet_operations *ops, struct net *net) | ||
66 | { | ||
67 | if (ops->id && ops->size) { | ||
68 | int id = *ops->id; | ||
69 | kfree(net_generic(net, id)); | ||
70 | } | ||
71 | } | ||
72 | |||
46 | /* | 73 | /* |
47 | * setup_net runs the initializers for the network namespace object. | 74 | * setup_net runs the initializers for the network namespace object. |
48 | */ | 75 | */ |
49 | static __net_init int setup_net(struct net *net) | 76 | static __net_init int setup_net(struct net *net) |
50 | { | 77 | { |
51 | /* Must be called with net_mutex held */ | 78 | /* Must be called with net_mutex held */ |
52 | struct pernet_operations *ops; | 79 | const struct pernet_operations *ops, *saved_ops; |
53 | int error = 0; | 80 | int error = 0; |
54 | 81 | ||
55 | atomic_set(&net->count, 1); | 82 | atomic_set(&net->count, 1); |
@@ -59,11 +86,9 @@ static __net_init int setup_net(struct net *net) | |||
59 | #endif | 86 | #endif |
60 | 87 | ||
61 | list_for_each_entry(ops, &pernet_list, list) { | 88 | list_for_each_entry(ops, &pernet_list, list) { |
62 | if (ops->init) { | 89 | error = ops_init(ops, net); |
63 | error = ops->init(net); | 90 | if (error < 0) |
64 | if (error < 0) | 91 | goto out_undo; |
65 | goto out_undo; | ||
66 | } | ||
67 | } | 92 | } |
68 | out: | 93 | out: |
69 | return error; | 94 | return error; |
@@ -72,6 +97,7 @@ out_undo: | |||
72 | /* Walk through the list backwards calling the exit functions | 97 | /* Walk through the list backwards calling the exit functions |
73 | * for the pernet modules whose init functions did not fail. | 98 | * for the pernet modules whose init functions did not fail. |
74 | */ | 99 | */ |
100 | saved_ops = ops; | ||
75 | list_for_each_entry_continue_reverse(ops, &pernet_list, list) { | 101 | list_for_each_entry_continue_reverse(ops, &pernet_list, list) { |
76 | if (ops->exit) | 102 | if (ops->exit) |
77 | ops->exit(net); | 103 | ops->exit(net); |
@@ -83,6 +109,9 @@ out_undo: | |||
83 | rtnl_unlock(); | 109 | rtnl_unlock(); |
84 | } | 110 | } |
85 | } | 111 | } |
112 | ops = saved_ops; | ||
113 | list_for_each_entry_continue_reverse(ops, &pernet_list, list) | ||
114 | ops_free(ops, net); | ||
86 | 115 | ||
87 | rcu_barrier(); | 116 | rcu_barrier(); |
88 | goto out; | 117 | goto out; |
@@ -175,7 +204,7 @@ static LIST_HEAD(cleanup_list); /* Must hold cleanup_list_lock to touch */ | |||
175 | 204 | ||
176 | static void cleanup_net(struct work_struct *work) | 205 | static void cleanup_net(struct work_struct *work) |
177 | { | 206 | { |
178 | struct pernet_operations *ops; | 207 | const struct pernet_operations *ops; |
179 | struct net *net, *tmp; | 208 | struct net *net, *tmp; |
180 | LIST_HEAD(net_kill_list); | 209 | LIST_HEAD(net_kill_list); |
181 | 210 | ||
@@ -214,6 +243,13 @@ static void cleanup_net(struct work_struct *work) | |||
214 | rtnl_unlock(); | 243 | rtnl_unlock(); |
215 | } | 244 | } |
216 | } | 245 | } |
246 | /* Free the net generic variables */ | ||
247 | list_for_each_entry_reverse(ops, &pernet_list, list) { | ||
248 | if (ops->size && ops->id) { | ||
249 | list_for_each_entry(net, &net_kill_list, cleanup_list) | ||
250 | ops_free(ops, net); | ||
251 | } | ||
252 | } | ||
217 | 253 | ||
218 | mutex_unlock(&net_mutex); | 254 | mutex_unlock(&net_mutex); |
219 | 255 | ||
@@ -309,16 +345,16 @@ static int __init net_ns_init(void) | |||
309 | pure_initcall(net_ns_init); | 345 | pure_initcall(net_ns_init); |
310 | 346 | ||
311 | #ifdef CONFIG_NET_NS | 347 | #ifdef CONFIG_NET_NS |
312 | static int register_pernet_operations(struct list_head *list, | 348 | static int __register_pernet_operations(struct list_head *list, |
313 | struct pernet_operations *ops) | 349 | struct pernet_operations *ops) |
314 | { | 350 | { |
315 | struct net *net, *undo_net; | 351 | struct net *net, *undo_net; |
316 | int error; | 352 | int error; |
317 | 353 | ||
318 | list_add_tail(&ops->list, list); | 354 | list_add_tail(&ops->list, list); |
319 | if (ops->init) { | 355 | if (ops->init || (ops->id && ops->size)) { |
320 | for_each_net(net) { | 356 | for_each_net(net) { |
321 | error = ops->init(net); | 357 | error = ops_init(ops, net); |
322 | if (error) | 358 | if (error) |
323 | goto out_undo; | 359 | goto out_undo; |
324 | } | 360 | } |
@@ -336,10 +372,18 @@ out_undo: | |||
336 | } | 372 | } |
337 | } | 373 | } |
338 | undone: | 374 | undone: |
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 | } | ||
382 | freed: | ||
339 | return error; | 383 | return error; |
340 | } | 384 | } |
341 | 385 | ||
342 | static void unregister_pernet_operations(struct pernet_operations *ops) | 386 | static void __unregister_pernet_operations(struct pernet_operations *ops) |
343 | { | 387 | { |
344 | struct net *net; | 388 | struct net *net; |
345 | 389 | ||
@@ -347,27 +391,66 @@ static void unregister_pernet_operations(struct pernet_operations *ops) | |||
347 | if (ops->exit) | 391 | if (ops->exit) |
348 | for_each_net(net) | 392 | for_each_net(net) |
349 | ops->exit(net); | 393 | ops->exit(net); |
394 | if (ops->id && ops->size) | ||
395 | for_each_net(net) | ||
396 | ops_free(ops, net); | ||
350 | } | 397 | } |
351 | 398 | ||
352 | #else | 399 | #else |
353 | 400 | ||
354 | static int register_pernet_operations(struct list_head *list, | 401 | static int __register_pernet_operations(struct list_head *list, |
355 | struct pernet_operations *ops) | 402 | struct pernet_operations *ops) |
356 | { | 403 | { |
357 | if (ops->init == NULL) | 404 | int err = 0; |
358 | return 0; | 405 | err = ops_init(ops, &init_net); |
359 | return ops->init(&init_net); | 406 | if (err) |
407 | ops_free(ops, &init_net); | ||
408 | return err; | ||
409 | |||
360 | } | 410 | } |
361 | 411 | ||
362 | static void unregister_pernet_operations(struct pernet_operations *ops) | 412 | static void __unregister_pernet_operations(struct pernet_operations *ops) |
363 | { | 413 | { |
364 | if (ops->exit) | 414 | if (ops->exit) |
365 | ops->exit(&init_net); | 415 | ops->exit(&init_net); |
416 | ops_free(ops, &init_net); | ||
366 | } | 417 | } |
367 | #endif | 418 | |
419 | #endif /* CONFIG_NET_NS */ | ||
368 | 420 | ||
369 | static DEFINE_IDA(net_generic_ids); | 421 | static DEFINE_IDA(net_generic_ids); |
370 | 422 | ||
423 | static int register_pernet_operations(struct list_head *list, | ||
424 | struct pernet_operations *ops) | ||
425 | { | ||
426 | int error; | ||
427 | |||
428 | if (ops->id) { | ||
429 | again: | ||
430 | error = ida_get_new_above(&net_generic_ids, 1, ops->id); | ||
431 | if (error < 0) { | ||
432 | if (error == -EAGAIN) { | ||
433 | ida_pre_get(&net_generic_ids, GFP_KERNEL); | ||
434 | goto again; | ||
435 | } | ||
436 | return error; | ||
437 | } | ||
438 | } | ||
439 | error = __register_pernet_operations(list, ops); | ||
440 | if (error && ops->id) | ||
441 | ida_remove(&net_generic_ids, *ops->id); | ||
442 | |||
443 | return error; | ||
444 | } | ||
445 | |||
446 | static void unregister_pernet_operations(struct pernet_operations *ops) | ||
447 | { | ||
448 | |||
449 | __unregister_pernet_operations(ops); | ||
450 | if (ops->id) | ||
451 | ida_remove(&net_generic_ids, *ops->id); | ||
452 | } | ||
453 | |||
371 | /** | 454 | /** |
372 | * register_pernet_subsys - register a network namespace subsystem | 455 | * register_pernet_subsys - register a network namespace subsystem |
373 | * @ops: pernet operations structure for the subsystem | 456 | * @ops: pernet operations structure for the subsystem |
@@ -414,38 +497,6 @@ void unregister_pernet_subsys(struct pernet_operations *module) | |||
414 | } | 497 | } |
415 | EXPORT_SYMBOL_GPL(unregister_pernet_subsys); | 498 | EXPORT_SYMBOL_GPL(unregister_pernet_subsys); |
416 | 499 | ||
417 | int register_pernet_gen_subsys(int *id, struct pernet_operations *ops) | ||
418 | { | ||
419 | int rv; | ||
420 | |||
421 | mutex_lock(&net_mutex); | ||
422 | again: | ||
423 | rv = ida_get_new_above(&net_generic_ids, 1, id); | ||
424 | if (rv < 0) { | ||
425 | if (rv == -EAGAIN) { | ||
426 | ida_pre_get(&net_generic_ids, GFP_KERNEL); | ||
427 | goto again; | ||
428 | } | ||
429 | goto out; | ||
430 | } | ||
431 | rv = register_pernet_operations(first_device, ops); | ||
432 | if (rv < 0) | ||
433 | ida_remove(&net_generic_ids, *id); | ||
434 | out: | ||
435 | mutex_unlock(&net_mutex); | ||
436 | return rv; | ||
437 | } | ||
438 | EXPORT_SYMBOL_GPL(register_pernet_gen_subsys); | ||
439 | |||
440 | void unregister_pernet_gen_subsys(int id, struct pernet_operations *ops) | ||
441 | { | ||
442 | mutex_lock(&net_mutex); | ||
443 | unregister_pernet_operations(ops); | ||
444 | ida_remove(&net_generic_ids, id); | ||
445 | mutex_unlock(&net_mutex); | ||
446 | } | ||
447 | EXPORT_SYMBOL_GPL(unregister_pernet_gen_subsys); | ||
448 | |||
449 | /** | 500 | /** |
450 | * register_pernet_device - register a network namespace device | 501 | * register_pernet_device - register a network namespace device |
451 | * @ops: pernet operations structure for the subsystem | 502 | * @ops: pernet operations structure for the subsystem |
@@ -477,30 +528,6 @@ int register_pernet_device(struct pernet_operations *ops) | |||
477 | } | 528 | } |
478 | EXPORT_SYMBOL_GPL(register_pernet_device); | 529 | EXPORT_SYMBOL_GPL(register_pernet_device); |
479 | 530 | ||
480 | int register_pernet_gen_device(int *id, struct pernet_operations *ops) | ||
481 | { | ||
482 | int error; | ||
483 | mutex_lock(&net_mutex); | ||
484 | again: | ||
485 | error = ida_get_new_above(&net_generic_ids, 1, id); | ||
486 | if (error) { | ||
487 | if (error == -EAGAIN) { | ||
488 | ida_pre_get(&net_generic_ids, GFP_KERNEL); | ||
489 | goto again; | ||
490 | } | ||
491 | goto out; | ||
492 | } | ||
493 | error = register_pernet_operations(&pernet_list, ops); | ||
494 | if (error) | ||
495 | ida_remove(&net_generic_ids, *id); | ||
496 | else if (first_device == &pernet_list) | ||
497 | first_device = &ops->list; | ||
498 | out: | ||
499 | mutex_unlock(&net_mutex); | ||
500 | return error; | ||
501 | } | ||
502 | EXPORT_SYMBOL_GPL(register_pernet_gen_device); | ||
503 | |||
504 | /** | 531 | /** |
505 | * unregister_pernet_device - unregister a network namespace netdevice | 532 | * unregister_pernet_device - unregister a network namespace netdevice |
506 | * @ops: pernet operations structure to manipulate | 533 | * @ops: pernet operations structure to manipulate |
@@ -520,17 +547,6 @@ void unregister_pernet_device(struct pernet_operations *ops) | |||
520 | } | 547 | } |
521 | EXPORT_SYMBOL_GPL(unregister_pernet_device); | 548 | EXPORT_SYMBOL_GPL(unregister_pernet_device); |
522 | 549 | ||
523 | void unregister_pernet_gen_device(int id, struct pernet_operations *ops) | ||
524 | { | ||
525 | mutex_lock(&net_mutex); | ||
526 | if (&ops->list == first_device) | ||
527 | first_device = first_device->next; | ||
528 | unregister_pernet_operations(ops); | ||
529 | ida_remove(&net_generic_ids, id); | ||
530 | mutex_unlock(&net_mutex); | ||
531 | } | ||
532 | EXPORT_SYMBOL_GPL(unregister_pernet_gen_device); | ||
533 | |||
534 | static void net_generic_release(struct rcu_head *rcu) | 550 | static void net_generic_release(struct rcu_head *rcu) |
535 | { | 551 | { |
536 | struct net_generic *ng; | 552 | struct net_generic *ng; |