|
@@ -8,8 +8,10 @@
|
|
|
#include <linux/idr.h>
|
|
|
#include <linux/rculist.h>
|
|
|
#include <linux/nsproxy.h>
|
|
|
+#include <linux/netdevice.h>
|
|
|
#include <net/net_namespace.h>
|
|
|
#include <net/netns/generic.h>
|
|
|
+#include <net/rtnetlink.h>
|
|
|
|
|
|
/*
|
|
|
* Our network namespace constructor/destructor lists
|
|
@@ -27,6 +29,20 @@ EXPORT_SYMBOL(init_net);
|
|
|
|
|
|
#define INITIAL_NET_GEN_PTRS 13 /* +1 for len +2 for rcu_head */
|
|
|
|
|
|
+static void unregister_netdevices(struct net *net, struct list_head *list)
|
|
|
+{
|
|
|
+ struct net_device *dev;
|
|
|
+ /* At exit all network devices most be removed from a network
|
|
|
+ * namespace. Do this in the reverse order of registeration.
|
|
|
+ */
|
|
|
+ for_each_netdev_reverse(net, dev) {
|
|
|
+ if (dev->rtnl_link_ops)
|
|
|
+ dev->rtnl_link_ops->dellink(dev, list);
|
|
|
+ else
|
|
|
+ unregister_netdevice_queue(dev, list);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* setup_net runs the initializers for the network namespace object.
|
|
|
*/
|
|
@@ -59,6 +75,13 @@ out_undo:
|
|
|
list_for_each_entry_continue_reverse(ops, &pernet_list, list) {
|
|
|
if (ops->exit)
|
|
|
ops->exit(net);
|
|
|
+ if (&ops->list == first_device) {
|
|
|
+ LIST_HEAD(dev_kill_list);
|
|
|
+ rtnl_lock();
|
|
|
+ unregister_netdevices(net, &dev_kill_list);
|
|
|
+ unregister_netdevice_many(&dev_kill_list);
|
|
|
+ rtnl_unlock();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
rcu_barrier();
|
|
@@ -147,18 +170,26 @@ struct net *copy_net_ns(unsigned long flags, struct net *old_net)
|
|
|
return net_create();
|
|
|
}
|
|
|
|
|
|
+static DEFINE_SPINLOCK(cleanup_list_lock);
|
|
|
+static LIST_HEAD(cleanup_list); /* Must hold cleanup_list_lock to touch */
|
|
|
+
|
|
|
static void cleanup_net(struct work_struct *work)
|
|
|
{
|
|
|
struct pernet_operations *ops;
|
|
|
- struct net *net;
|
|
|
+ struct net *net, *tmp;
|
|
|
+ LIST_HEAD(net_kill_list);
|
|
|
|
|
|
- net = container_of(work, struct net, work);
|
|
|
+ /* Atomically snapshot the list of namespaces to cleanup */
|
|
|
+ spin_lock_irq(&cleanup_list_lock);
|
|
|
+ list_replace_init(&cleanup_list, &net_kill_list);
|
|
|
+ spin_unlock_irq(&cleanup_list_lock);
|
|
|
|
|
|
mutex_lock(&net_mutex);
|
|
|
|
|
|
/* Don't let anyone else find us. */
|
|
|
rtnl_lock();
|
|
|
- list_del_rcu(&net->list);
|
|
|
+ list_for_each_entry(net, &net_kill_list, cleanup_list)
|
|
|
+ list_del_rcu(&net->list);
|
|
|
rtnl_unlock();
|
|
|
|
|
|
/*
|
|
@@ -170,8 +201,18 @@ static void cleanup_net(struct work_struct *work)
|
|
|
|
|
|
/* Run all of the network namespace exit methods */
|
|
|
list_for_each_entry_reverse(ops, &pernet_list, list) {
|
|
|
- if (ops->exit)
|
|
|
- ops->exit(net);
|
|
|
+ if (ops->exit) {
|
|
|
+ list_for_each_entry(net, &net_kill_list, cleanup_list)
|
|
|
+ ops->exit(net);
|
|
|
+ }
|
|
|
+ if (&ops->list == first_device) {
|
|
|
+ LIST_HEAD(dev_kill_list);
|
|
|
+ rtnl_lock();
|
|
|
+ list_for_each_entry(net, &net_kill_list, cleanup_list)
|
|
|
+ unregister_netdevices(net, &dev_kill_list);
|
|
|
+ unregister_netdevice_many(&dev_kill_list);
|
|
|
+ rtnl_unlock();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
mutex_unlock(&net_mutex);
|
|
@@ -182,14 +223,23 @@ static void cleanup_net(struct work_struct *work)
|
|
|
rcu_barrier();
|
|
|
|
|
|
/* Finally it is safe to free my network namespace structure */
|
|
|
- net_free(net);
|
|
|
+ list_for_each_entry_safe(net, tmp, &net_kill_list, cleanup_list) {
|
|
|
+ list_del_init(&net->cleanup_list);
|
|
|
+ net_free(net);
|
|
|
+ }
|
|
|
}
|
|
|
+static DECLARE_WORK(net_cleanup_work, cleanup_net);
|
|
|
|
|
|
void __put_net(struct net *net)
|
|
|
{
|
|
|
/* Cleanup the network namespace in process context */
|
|
|
- INIT_WORK(&net->work, cleanup_net);
|
|
|
- queue_work(netns_wq, &net->work);
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&cleanup_list_lock, flags);
|
|
|
+ list_add(&net->cleanup_list, &cleanup_list);
|
|
|
+ spin_unlock_irqrestore(&cleanup_list_lock, flags);
|
|
|
+
|
|
|
+ queue_work(netns_wq, &net_cleanup_work);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(__put_net);
|
|
|
|