X-Git-Url: https://git.karo-electronics.de/?a=blobdiff_plain;f=net%2Fcore%2Fnet_namespace.c;h=7c52fe277b62441966f758b25fad9acef18a10d0;hb=ee5c2ab09b79d3aba3515a3eae7c5cf1e2cbc569;hp=383252b50411b89c5c18835bcbe4c9837ed16ad0;hpb=d06fc1d9b5518582f4982f908e820f2303d1eece;p=karo-tx-linux.git diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 383252b50411..7c52fe277b62 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -5,7 +5,9 @@ #include #include #include +#include #include +#include /* * Our network namespace constructor/destructor lists @@ -18,7 +20,9 @@ static DEFINE_MUTEX(net_mutex); LIST_HEAD(net_namespace_list); struct net init_net; -EXPORT_SYMBOL_GPL(init_net); +EXPORT_SYMBOL(init_net); + +#define INITIAL_NET_GEN_PTRS 13 /* +1 for len +2 for rcu_head */ /* * setup_net runs the initializers for the network namespace object. @@ -28,9 +32,22 @@ static __net_init int setup_net(struct net *net) /* Must be called with net_mutex held */ struct pernet_operations *ops; int error; + struct net_generic *ng; atomic_set(&net->count, 1); +#ifdef NETNS_REFCNT_DEBUG atomic_set(&net->use_count, 0); +#endif + + error = -ENOMEM; + ng = kzalloc(sizeof(struct net_generic) + + INITIAL_NET_GEN_PTRS * sizeof(void *), GFP_KERNEL); + if (ng == NULL) + goto out; + + ng->len = INITIAL_NET_GEN_PTRS; + INIT_RCU_HEAD(&ng->rcu); + rcu_assign_pointer(net->gen, ng); error = 0; list_for_each_entry(ops, &pernet_list, list) { @@ -53,11 +70,13 @@ out_undo: } rcu_barrier(); + kfree(ng); goto out; } #ifdef CONFIG_NET_NS static struct kmem_cache *net_cachep; +static struct workqueue_struct *netns_wq; static struct net *net_alloc(void) { @@ -69,11 +88,13 @@ static void net_free(struct net *net) if (!net) return; +#ifdef NETNS_REFCNT_DEBUG if (unlikely(atomic_read(&net->use_count) != 0)) { printk(KERN_EMERG "network namespace not free! Usage: %d\n", atomic_read(&net->use_count)); return; } +#endif kmem_cache_free(net_cachep, net); } @@ -119,6 +140,9 @@ static void cleanup_net(struct work_struct *work) struct pernet_operations *ops; struct net *net; + /* Be very certain incoming network packets will not find us */ + rcu_barrier(); + net = container_of(work, struct net, work); mutex_lock(&net_mutex); @@ -149,7 +173,7 @@ void __put_net(struct net *net) { /* Cleanup the network namespace in process context */ INIT_WORK(&net->work, cleanup_net); - schedule_work(&net->work); + queue_work(netns_wq, &net->work); } EXPORT_SYMBOL_GPL(__put_net); @@ -171,7 +195,13 @@ static int __init net_ns_init(void) net_cachep = kmem_cache_create("net_namespace", sizeof(struct net), SMP_CACHE_BYTES, SLAB_PANIC, NULL); + + /* Create workqueue for cleanup */ + netns_wq = create_singlethread_workqueue("netns"); + if (!netns_wq) + panic("Could not create netns workq"); #endif + mutex_lock(&net_mutex); err = setup_net(&init_net); @@ -246,6 +276,8 @@ static void unregister_pernet_operations(struct pernet_operations *ops) } #endif +static DEFINE_IDA(net_generic_ids); + /** * register_pernet_subsys - register a network namespace subsystem * @ops: pernet operations structure for the subsystem @@ -280,7 +312,7 @@ EXPORT_SYMBOL_GPL(register_pernet_subsys); * @ops: pernet operations structure to manipulate * * Remove the pernet operations structure from the list to be - * used when network namespaces are created or destoryed. In + * used when network namespaces are created or destroyed. In * addition run the exit method for all existing network * namespaces. */ @@ -323,12 +355,36 @@ int register_pernet_device(struct pernet_operations *ops) } EXPORT_SYMBOL_GPL(register_pernet_device); +int register_pernet_gen_device(int *id, struct pernet_operations *ops) +{ + int error; + mutex_lock(&net_mutex); +again: + error = ida_get_new_above(&net_generic_ids, 1, id); + if (error) { + if (error == -EAGAIN) { + ida_pre_get(&net_generic_ids, GFP_KERNEL); + goto again; + } + goto out; + } + error = register_pernet_operations(&pernet_list, ops); + if (error) + ida_remove(&net_generic_ids, *id); + else if (first_device == &pernet_list) + first_device = &ops->list; +out: + mutex_unlock(&net_mutex); + return error; +} +EXPORT_SYMBOL_GPL(register_pernet_gen_device); + /** * unregister_pernet_device - unregister a network namespace netdevice * @ops: pernet operations structure to manipulate * * Remove the pernet operations structure from the list to be - * used when network namespaces are created or destoryed. In + * used when network namespaces are created or destroyed. In * addition run the exit method for all existing network * namespaces. */ @@ -341,3 +397,61 @@ void unregister_pernet_device(struct pernet_operations *ops) mutex_unlock(&net_mutex); } EXPORT_SYMBOL_GPL(unregister_pernet_device); + +void unregister_pernet_gen_device(int id, struct pernet_operations *ops) +{ + mutex_lock(&net_mutex); + if (&ops->list == first_device) + first_device = first_device->next; + unregister_pernet_operations(ops); + ida_remove(&net_generic_ids, id); + mutex_unlock(&net_mutex); +} +EXPORT_SYMBOL_GPL(unregister_pernet_gen_device); + +static void net_generic_release(struct rcu_head *rcu) +{ + struct net_generic *ng; + + ng = container_of(rcu, struct net_generic, rcu); + kfree(ng); +} + +int net_assign_generic(struct net *net, int id, void *data) +{ + struct net_generic *ng, *old_ng; + + BUG_ON(!mutex_is_locked(&net_mutex)); + BUG_ON(id == 0); + + ng = old_ng = net->gen; + if (old_ng->len >= id) + goto assign; + + ng = kzalloc(sizeof(struct net_generic) + + id * sizeof(void *), GFP_KERNEL); + if (ng == NULL) + return -ENOMEM; + + /* + * Some synchronisation notes: + * + * The net_generic explores the net->gen array inside rcu + * read section. Besides once set the net->gen->ptr[x] + * pointer never changes (see rules in netns/generic.h). + * + * That said, we simply duplicate this array and schedule + * the old copy for kfree after a grace period. + */ + + ng->len = id; + INIT_RCU_HEAD(&ng->rcu); + memcpy(&ng->ptr, &old_ng->ptr, old_ng->len); + + rcu_assign_pointer(net->gen, ng); + call_rcu(&old_ng->rcu, net_generic_release); +assign: + ng->ptr[id - 1] = data; + return 0; +} +EXPORT_SYMBOL_GPL(net_assign_generic);