diff options
| -rw-r--r-- | net/core/net_namespace.c | 86 |
1 files changed, 55 insertions, 31 deletions
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 55151faaf90c..b0767abf23e5 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) | |||
| 32 | { | 32 | { |
| 33 | /* Must be called with net_mutex held */ | 33 | /* Must be called with net_mutex held */ |
| 34 | struct pernet_operations *ops; | 34 | struct pernet_operations *ops; |
| 35 | int error; | 35 | int error = 0; |
| 36 | struct net_generic *ng; | ||
| 37 | 36 | ||
| 38 | atomic_set(&net->count, 1); | 37 | atomic_set(&net->count, 1); |
| 38 | |||
| 39 | #ifdef NETNS_REFCNT_DEBUG | 39 | #ifdef NETNS_REFCNT_DEBUG |
| 40 | atomic_set(&net->use_count, 0); | 40 | atomic_set(&net->use_count, 0); |
| 41 | #endif | 41 | #endif |
| 42 | 42 | ||
| 43 | error = -ENOMEM; | ||
| 44 | ng = kzalloc(sizeof(struct net_generic) + | ||
| 45 | INITIAL_NET_GEN_PTRS * sizeof(void *), GFP_KERNEL); | ||
| 46 | if (ng == NULL) | ||
| 47 | goto out; | ||
| 48 | |||
| 49 | ng->len = INITIAL_NET_GEN_PTRS; | ||
| 50 | rcu_assign_pointer(net->gen, ng); | ||
| 51 | |||
| 52 | error = 0; | ||
| 53 | list_for_each_entry(ops, &pernet_list, list) { | 43 | list_for_each_entry(ops, &pernet_list, list) { |
| 54 | if (ops->init) { | 44 | if (ops->init) { |
| 55 | error = ops->init(net); | 45 | error = ops->init(net); |
| @@ -70,7 +60,6 @@ out_undo: | |||
| 70 | } | 60 | } |
| 71 | 61 | ||
| 72 | rcu_barrier(); | 62 | rcu_barrier(); |
| 73 | kfree(ng); | ||
| 74 | goto out; | 63 | goto out; |
| 75 | } | 64 | } |
| 76 | 65 | ||
| @@ -78,16 +67,43 @@ out_undo: | |||
| 78 | static struct kmem_cache *net_cachep; | 67 | static struct kmem_cache *net_cachep; |
| 79 | static struct workqueue_struct *netns_wq; | 68 | static struct workqueue_struct *netns_wq; |
| 80 | 69 | ||
| 81 | static struct net *net_alloc(void) | 70 | static struct net_generic *net_alloc_generic(void) |
| 82 | { | 71 | { |
| 83 | return kmem_cache_zalloc(net_cachep, GFP_KERNEL); | 72 | struct net_generic *ng; |
| 73 | size_t generic_size = sizeof(struct net_generic) + | ||
| 74 | INITIAL_NET_GEN_PTRS * sizeof(void *); | ||
| 75 | |||
| 76 | ng = kzalloc(generic_size, GFP_KERNEL); | ||
| 77 | if (ng) | ||
| 78 | ng->len = INITIAL_NET_GEN_PTRS; | ||
| 79 | |||
| 80 | return ng; | ||
| 84 | } | 81 | } |
| 85 | 82 | ||
| 86 | static void net_free(struct net *net) | 83 | static struct net *net_alloc(void) |
| 87 | { | 84 | { |
| 85 | struct net *net = NULL; | ||
| 86 | struct net_generic *ng; | ||
| 87 | |||
| 88 | ng = net_alloc_generic(); | ||
| 89 | if (!ng) | ||
| 90 | goto out; | ||
| 91 | |||
| 92 | net = kmem_cache_zalloc(net_cachep, GFP_KERNEL); | ||
| 88 | if (!net) | 93 | if (!net) |
| 89 | return; | 94 | goto out_free; |
| 95 | |||
| 96 | rcu_assign_pointer(net->gen, ng); | ||
| 97 | out: | ||
| 98 | return net; | ||
| 99 | |||
| 100 | out_free: | ||
| 101 | kfree(ng); | ||
| 102 | goto out; | ||
| 103 | } | ||
| 90 | 104 | ||
| 105 | static void net_free(struct net *net) | ||
| 106 | { | ||
| 91 | #ifdef NETNS_REFCNT_DEBUG | 107 | #ifdef NETNS_REFCNT_DEBUG |
| 92 | if (unlikely(atomic_read(&net->use_count) != 0)) { | 108 | if (unlikely(atomic_read(&net->use_count) != 0)) { |
| 93 | printk(KERN_EMERG "network namespace not free! Usage: %d\n", | 109 | 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) | |||
| 112 | err = -ENOMEM; | 128 | err = -ENOMEM; |
| 113 | new_net = net_alloc(); | 129 | new_net = net_alloc(); |
| 114 | if (!new_net) | 130 | if (!new_net) |
| 115 | goto out; | 131 | goto out_err; |
| 116 | 132 | ||
| 117 | mutex_lock(&net_mutex); | 133 | mutex_lock(&net_mutex); |
| 118 | err = setup_net(new_net); | 134 | err = setup_net(new_net); |
| 119 | if (err) | 135 | if (!err) { |
| 120 | goto out_unlock; | 136 | rtnl_lock(); |
| 121 | 137 | list_add_tail(&new_net->list, &net_namespace_list); | |
| 122 | rtnl_lock(); | 138 | rtnl_unlock(); |
| 123 | list_add_tail(&new_net->list, &net_namespace_list); | 139 | } |
| 124 | rtnl_unlock(); | ||
| 125 | |||
| 126 | |||
| 127 | out_unlock: | ||
| 128 | mutex_unlock(&net_mutex); | 140 | mutex_unlock(&net_mutex); |
| 141 | |||
| 142 | if (err) | ||
| 143 | goto out_free; | ||
| 129 | out: | 144 | out: |
| 130 | put_net(old_net); | 145 | put_net(old_net); |
| 131 | if (err) { | ||
| 132 | net_free(new_net); | ||
| 133 | new_net = ERR_PTR(err); | ||
| 134 | } | ||
| 135 | return new_net; | 146 | return new_net; |
| 147 | |||
| 148 | out_free: | ||
| 149 | net_free(new_net); | ||
| 150 | out_err: | ||
| 151 | new_net = ERR_PTR(err); | ||
| 152 | goto out; | ||
| 136 | } | 153 | } |
| 137 | 154 | ||
| 138 | static void cleanup_net(struct work_struct *work) | 155 | static void cleanup_net(struct work_struct *work) |
| @@ -188,6 +205,7 @@ struct net *copy_net_ns(unsigned long flags, struct net *old_net) | |||
| 188 | 205 | ||
| 189 | static int __init net_ns_init(void) | 206 | static int __init net_ns_init(void) |
| 190 | { | 207 | { |
| 208 | struct net_generic *ng; | ||
| 191 | int err; | 209 | int err; |
| 192 | 210 | ||
| 193 | printk(KERN_INFO "net_namespace: %zd bytes\n", sizeof(struct net)); | 211 | printk(KERN_INFO "net_namespace: %zd bytes\n", sizeof(struct net)); |
| @@ -202,6 +220,12 @@ static int __init net_ns_init(void) | |||
| 202 | panic("Could not create netns workq"); | 220 | panic("Could not create netns workq"); |
| 203 | #endif | 221 | #endif |
| 204 | 222 | ||
| 223 | ng = net_alloc_generic(); | ||
| 224 | if (!ng) | ||
| 225 | panic("Could not allocate generic netns"); | ||
| 226 | |||
| 227 | rcu_assign_pointer(init_net.gen, ng); | ||
| 228 | |||
| 205 | mutex_lock(&net_mutex); | 229 | mutex_lock(&net_mutex); |
| 206 | err = setup_net(&init_net); | 230 | err = setup_net(&init_net); |
| 207 | 231 | ||
