#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
#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.
*/
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();
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();
/*
/* 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);
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);