diff options
-rw-r--r-- | include/net/net_namespace.h | 2 | ||||
-rw-r--r-- | net/core/net_namespace.c | 66 |
2 files changed, 59 insertions, 9 deletions
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 0addd45038ac..d69b4796030f 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h | |||
@@ -42,7 +42,7 @@ struct net { | |||
42 | */ | 42 | */ |
43 | #endif | 43 | #endif |
44 | struct list_head list; /* list of network namespaces */ | 44 | struct list_head list; /* list of network namespaces */ |
45 | struct work_struct work; /* work struct for freeing */ | 45 | struct list_head cleanup_list; /* namespaces on death row */ |
46 | 46 | ||
47 | struct proc_dir_entry *proc_net; | 47 | struct proc_dir_entry *proc_net; |
48 | struct proc_dir_entry *proc_net_stat; | 48 | struct proc_dir_entry *proc_net_stat; |
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 86ed7f44d083..a42caa2b909b 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c | |||
@@ -8,8 +8,10 @@ | |||
8 | #include <linux/idr.h> | 8 | #include <linux/idr.h> |
9 | #include <linux/rculist.h> | 9 | #include <linux/rculist.h> |
10 | #include <linux/nsproxy.h> | 10 | #include <linux/nsproxy.h> |
11 | #include <linux/netdevice.h> | ||
11 | #include <net/net_namespace.h> | 12 | #include <net/net_namespace.h> |
12 | #include <net/netns/generic.h> | 13 | #include <net/netns/generic.h> |
14 | #include <net/rtnetlink.h> | ||
13 | 15 | ||
14 | /* | 16 | /* |
15 | * Our network namespace constructor/destructor lists | 17 | * Our network namespace constructor/destructor lists |
@@ -27,6 +29,20 @@ EXPORT_SYMBOL(init_net); | |||
27 | 29 | ||
28 | #define INITIAL_NET_GEN_PTRS 13 /* +1 for len +2 for rcu_head */ | 30 | #define INITIAL_NET_GEN_PTRS 13 /* +1 for len +2 for rcu_head */ |
29 | 31 | ||
32 | static void unregister_netdevices(struct net *net, struct list_head *list) | ||
33 | { | ||
34 | struct net_device *dev; | ||
35 | /* At exit all network devices most be removed from a network | ||
36 | * namespace. Do this in the reverse order of registeration. | ||
37 | */ | ||
38 | for_each_netdev_reverse(net, dev) { | ||
39 | if (dev->rtnl_link_ops) | ||
40 | dev->rtnl_link_ops->dellink(dev, list); | ||
41 | else | ||
42 | unregister_netdevice_queue(dev, list); | ||
43 | } | ||
44 | } | ||
45 | |||
30 | /* | 46 | /* |
31 | * setup_net runs the initializers for the network namespace object. | 47 | * setup_net runs the initializers for the network namespace object. |
32 | */ | 48 | */ |
@@ -59,6 +75,13 @@ out_undo: | |||
59 | list_for_each_entry_continue_reverse(ops, &pernet_list, list) { | 75 | list_for_each_entry_continue_reverse(ops, &pernet_list, list) { |
60 | if (ops->exit) | 76 | if (ops->exit) |
61 | ops->exit(net); | 77 | ops->exit(net); |
78 | if (&ops->list == first_device) { | ||
79 | LIST_HEAD(dev_kill_list); | ||
80 | rtnl_lock(); | ||
81 | unregister_netdevices(net, &dev_kill_list); | ||
82 | unregister_netdevice_many(&dev_kill_list); | ||
83 | rtnl_unlock(); | ||
84 | } | ||
62 | } | 85 | } |
63 | 86 | ||
64 | rcu_barrier(); | 87 | rcu_barrier(); |
@@ -147,18 +170,26 @@ struct net *copy_net_ns(unsigned long flags, struct net *old_net) | |||
147 | return net_create(); | 170 | return net_create(); |
148 | } | 171 | } |
149 | 172 | ||
173 | static DEFINE_SPINLOCK(cleanup_list_lock); | ||
174 | static LIST_HEAD(cleanup_list); /* Must hold cleanup_list_lock to touch */ | ||
175 | |||
150 | static void cleanup_net(struct work_struct *work) | 176 | static void cleanup_net(struct work_struct *work) |
151 | { | 177 | { |
152 | struct pernet_operations *ops; | 178 | struct pernet_operations *ops; |
153 | struct net *net; | 179 | struct net *net, *tmp; |
180 | LIST_HEAD(net_kill_list); | ||
154 | 181 | ||
155 | net = container_of(work, struct net, work); | 182 | /* Atomically snapshot the list of namespaces to cleanup */ |
183 | spin_lock_irq(&cleanup_list_lock); | ||
184 | list_replace_init(&cleanup_list, &net_kill_list); | ||
185 | spin_unlock_irq(&cleanup_list_lock); | ||
156 | 186 | ||
157 | mutex_lock(&net_mutex); | 187 | mutex_lock(&net_mutex); |
158 | 188 | ||
159 | /* Don't let anyone else find us. */ | 189 | /* Don't let anyone else find us. */ |
160 | rtnl_lock(); | 190 | rtnl_lock(); |
161 | list_del_rcu(&net->list); | 191 | list_for_each_entry(net, &net_kill_list, cleanup_list) |
192 | list_del_rcu(&net->list); | ||
162 | rtnl_unlock(); | 193 | rtnl_unlock(); |
163 | 194 | ||
164 | /* | 195 | /* |
@@ -170,8 +201,18 @@ static void cleanup_net(struct work_struct *work) | |||
170 | 201 | ||
171 | /* Run all of the network namespace exit methods */ | 202 | /* Run all of the network namespace exit methods */ |
172 | list_for_each_entry_reverse(ops, &pernet_list, list) { | 203 | list_for_each_entry_reverse(ops, &pernet_list, list) { |
173 | if (ops->exit) | 204 | if (ops->exit) { |
174 | ops->exit(net); | 205 | list_for_each_entry(net, &net_kill_list, cleanup_list) |
206 | ops->exit(net); | ||
207 | } | ||
208 | if (&ops->list == first_device) { | ||
209 | LIST_HEAD(dev_kill_list); | ||
210 | rtnl_lock(); | ||
211 | list_for_each_entry(net, &net_kill_list, cleanup_list) | ||
212 | unregister_netdevices(net, &dev_kill_list); | ||
213 | unregister_netdevice_many(&dev_kill_list); | ||
214 | rtnl_unlock(); | ||
215 | } | ||
175 | } | 216 | } |
176 | 217 | ||
177 | mutex_unlock(&net_mutex); | 218 | mutex_unlock(&net_mutex); |
@@ -182,14 +223,23 @@ static void cleanup_net(struct work_struct *work) | |||
182 | rcu_barrier(); | 223 | rcu_barrier(); |
183 | 224 | ||
184 | /* Finally it is safe to free my network namespace structure */ | 225 | /* Finally it is safe to free my network namespace structure */ |
185 | net_free(net); | 226 | list_for_each_entry_safe(net, tmp, &net_kill_list, cleanup_list) { |
227 | list_del_init(&net->cleanup_list); | ||
228 | net_free(net); | ||
229 | } | ||
186 | } | 230 | } |
231 | static DECLARE_WORK(net_cleanup_work, cleanup_net); | ||
187 | 232 | ||
188 | void __put_net(struct net *net) | 233 | void __put_net(struct net *net) |
189 | { | 234 | { |
190 | /* Cleanup the network namespace in process context */ | 235 | /* Cleanup the network namespace in process context */ |
191 | INIT_WORK(&net->work, cleanup_net); | 236 | unsigned long flags; |
192 | queue_work(netns_wq, &net->work); | 237 | |
238 | spin_lock_irqsave(&cleanup_list_lock, flags); | ||
239 | list_add(&net->cleanup_list, &cleanup_list); | ||
240 | spin_unlock_irqrestore(&cleanup_list_lock, flags); | ||
241 | |||
242 | queue_work(netns_wq, &net_cleanup_work); | ||
193 | } | 243 | } |
194 | EXPORT_SYMBOL_GPL(__put_net); | 244 | EXPORT_SYMBOL_GPL(__put_net); |
195 | 245 | ||