diff options
Diffstat (limited to 'net/core/net_namespace.c')
| -rw-r--r-- | net/core/net_namespace.c | 91 | 
1 files changed, 56 insertions, 35 deletions
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 55cffad2f32..e3bebd36f05 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -32,24 +32,14 @@ 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; +	int error = 0;  	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; -	rcu_assign_pointer(net->gen, ng); - -	error = 0;  	list_for_each_entry(ops, &pernet_list, list) {  		if (ops->init) {  			error = ops->init(net); @@ -70,24 +60,50 @@ out_undo:  	}  	rcu_barrier(); -	kfree(ng);  	goto out;  } +static struct net_generic *net_alloc_generic(void) +{ +	struct net_generic *ng; +	size_t generic_size = sizeof(struct net_generic) + +		INITIAL_NET_GEN_PTRS * sizeof(void *); + +	ng = kzalloc(generic_size, GFP_KERNEL); +	if (ng) +		ng->len = INITIAL_NET_GEN_PTRS; + +	return ng; +} +  #ifdef CONFIG_NET_NS  static struct kmem_cache *net_cachep;  static struct workqueue_struct *netns_wq;  static struct net *net_alloc(void)  { -	return kmem_cache_zalloc(net_cachep, GFP_KERNEL); +	struct net *net = NULL; +	struct net_generic *ng; + +	ng = net_alloc_generic(); +	if (!ng) +		goto out; + +	net = kmem_cache_zalloc(net_cachep, GFP_KERNEL); +	if (!net) +		goto out_free; + +	rcu_assign_pointer(net->gen, ng); +out: +	return net; + +out_free: +	kfree(ng); +	goto out;  }  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", @@ -112,27 +128,28 @@ struct net *copy_net_ns(unsigned long flags, struct net *old_net)  	err = -ENOMEM;  	new_net = net_alloc();  	if (!new_net) -		goto out; +		goto out_err;  	mutex_lock(&net_mutex);  	err = setup_net(new_net); -	if (err) -		goto out_unlock; - -	rtnl_lock(); -	list_add_tail(&new_net->list, &net_namespace_list); -	rtnl_unlock(); - - -out_unlock: +	if (!err) { +		rtnl_lock(); +		list_add_tail(&new_net->list, &net_namespace_list); +		rtnl_unlock(); +	}  	mutex_unlock(&net_mutex); + +	if (err) +		goto out_free;  out:  	put_net(old_net); -	if (err) { -		net_free(new_net); -		new_net = ERR_PTR(err); -	}  	return new_net; + +out_free: +	net_free(new_net); +out_err: +	new_net = ERR_PTR(err); +	goto out;  }  static void cleanup_net(struct work_struct *work) @@ -140,9 +157,6 @@ 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); @@ -188,6 +202,7 @@ struct net *copy_net_ns(unsigned long flags, struct net *old_net)  static int __init net_ns_init(void)  { +	struct net_generic *ng;  	int err;  	printk(KERN_INFO "net_namespace: %zd bytes\n", sizeof(struct net)); @@ -202,6 +217,12 @@ static int __init net_ns_init(void)  		panic("Could not create netns workq");  #endif +	ng = net_alloc_generic(); +	if (!ng) +		panic("Could not allocate generic netns"); + +	rcu_assign_pointer(init_net.gen, ng); +  	mutex_lock(&net_mutex);  	err = setup_net(&init_net); @@ -341,8 +362,8 @@ again:  	rv = register_pernet_operations(first_device, ops);  	if (rv < 0)  		ida_remove(&net_generic_ids, *id); -	mutex_unlock(&net_mutex);  out: +	mutex_unlock(&net_mutex);  	return rv;  }  EXPORT_SYMBOL_GPL(register_pernet_gen_subsys);  | 
