#include <linux/slab.h>
#include <linux/list.h>
#include <linux/delay.h>
+#include <linux/sched.h>
#include <net/net_namespace.h>
/*
static struct list_head *first_device = &pernet_list;
static DEFINE_MUTEX(net_mutex);
-static DEFINE_MUTEX(net_list_mutex);
LIST_HEAD(net_namespace_list);
-static struct kmem_cache *net_cachep;
-
struct net init_net;
-EXPORT_SYMBOL_GPL(init_net);
+EXPORT_SYMBOL(init_net);
-void net_lock(void)
+/*
+ * setup_net runs the initializers for the network namespace object.
+ */
+static __net_init int setup_net(struct net *net)
{
- mutex_lock(&net_list_mutex);
-}
+ /* Must be called with net_mutex held */
+ struct pernet_operations *ops;
+ int error;
-void net_unlock(void)
-{
- mutex_unlock(&net_list_mutex);
+ atomic_set(&net->count, 1);
+ atomic_set(&net->use_count, 0);
+
+ error = 0;
+ list_for_each_entry(ops, &pernet_list, list) {
+ if (ops->init) {
+ error = ops->init(net);
+ if (error < 0)
+ goto out_undo;
+ }
+ }
+out:
+ return error;
+
+out_undo:
+ /* Walk through the list backwards calling the exit functions
+ * for the pernet modules whose init functions did not fail.
+ */
+ list_for_each_entry_continue_reverse(ops, &pernet_list, list) {
+ if (ops->exit)
+ ops->exit(net);
+ }
+
+ rcu_barrier();
+ goto out;
}
-#if 0
+#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_alloc(net_cachep, GFP_KERNEL);
+ return kmem_cache_zalloc(net_cachep, GFP_KERNEL);
}
-#endif
static void net_free(struct net *net)
{
kmem_cache_free(net_cachep, net);
}
+struct net *copy_net_ns(unsigned long flags, struct net *old_net)
+{
+ struct net *new_net = NULL;
+ int err;
+
+ get_net(old_net);
+
+ if (!(flags & CLONE_NEWNET))
+ return old_net;
+
+ err = -ENOMEM;
+ new_net = net_alloc();
+ if (!new_net)
+ goto out;
+
+ 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:
+ mutex_unlock(&net_mutex);
+out:
+ put_net(old_net);
+ if (err) {
+ net_free(new_net);
+ new_net = ERR_PTR(err);
+ }
+ return new_net;
+}
+
static void cleanup_net(struct work_struct *work)
{
struct pernet_operations *ops;
mutex_lock(&net_mutex);
/* Don't let anyone else find us. */
- net_lock();
+ rtnl_lock();
list_del(&net->list);
- net_unlock();
+ rtnl_unlock();
/* Run all of the network namespace exit methods */
list_for_each_entry_reverse(ops, &pernet_list, list) {
net_free(net);
}
-
void __put_net(struct net *net)
{
/* Cleanup the network namespace in process context */
INIT_WORK(&net->work, cleanup_net);
- schedule_work(&net->work);
+ queue_work(netns_wq, &net->work);
}
EXPORT_SYMBOL_GPL(__put_net);
-/*
- * setup_net runs the initializers for the network namespace object.
- */
-static int setup_net(struct net *net)
+#else
+struct net *copy_net_ns(unsigned long flags, struct net *old_net)
{
- /* Must be called with net_mutex held */
- struct pernet_operations *ops;
- int error;
-
- memset(net, 0, sizeof(struct net));
- atomic_set(&net->count, 1);
- atomic_set(&net->use_count, 0);
-
- error = 0;
- list_for_each_entry(ops, &pernet_list, list) {
- if (ops->init) {
- error = ops->init(net);
- if (error < 0)
- goto out_undo;
- }
- }
-out:
- return error;
-
-out_undo:
- /* Walk through the list backwards calling the exit functions
- * for the pernet modules whose init functions did not fail.
- */
- list_for_each_entry_continue_reverse(ops, &pernet_list, list) {
- if (ops->exit)
- ops->exit(net);
- }
- goto out;
+ if (flags & CLONE_NEWNET)
+ return ERR_PTR(-EINVAL);
+ return old_net;
}
+#endif
static int __init net_ns_init(void)
{
int err;
printk(KERN_INFO "net_namespace: %zd bytes\n", sizeof(struct net));
+#ifdef CONFIG_NET_NS
net_cachep = kmem_cache_create("net_namespace", sizeof(struct net),
SMP_CACHE_BYTES,
SLAB_PANIC, NULL);
+
+ /* Create workqueue for cleanup */
+ netns_wq = create_singlethread_workqueue("netns");
+ if (!netns_wq)
+ panic("Could not create netns workq");
+#endif
+
mutex_lock(&net_mutex);
err = setup_net(&init_net);
- net_lock();
+ rtnl_lock();
list_add_tail(&init_net.list, &net_namespace_list);
- net_unlock();
+ rtnl_unlock();
mutex_unlock(&net_mutex);
if (err)
pure_initcall(net_ns_init);
+#ifdef CONFIG_NET_NS
static int register_pernet_operations(struct list_head *list,
struct pernet_operations *ops)
{
struct net *net, *undo_net;
int error;
- error = 0;
list_add_tail(&ops->list, list);
- for_each_net(net) {
- if (ops->init) {
+ if (ops->init) {
+ for_each_net(net) {
error = ops->init(net);
if (error)
goto out_undo;
}
}
-out:
- return error;
+ return 0;
out_undo:
/* If I have an error cleanup all namespaces I initialized */
list_del(&ops->list);
- for_each_net(undo_net) {
- if (undo_net == net)
- goto undone;
- if (ops->exit)
+ if (ops->exit) {
+ for_each_net(undo_net) {
+ if (undo_net == net)
+ goto undone;
ops->exit(undo_net);
+ }
}
undone:
- goto out;
+ return error;
}
static void unregister_pernet_operations(struct pernet_operations *ops)
struct net *net;
list_del(&ops->list);
- for_each_net(net)
- if (ops->exit)
+ if (ops->exit)
+ for_each_net(net)
ops->exit(net);
}
+#else
+
+static int register_pernet_operations(struct list_head *list,
+ struct pernet_operations *ops)
+{
+ if (ops->init == NULL)
+ return 0;
+ return ops->init(&init_net);
+}
+
+static void unregister_pernet_operations(struct pernet_operations *ops)
+{
+ if (ops->exit)
+ ops->exit(&init_net);
+}
+#endif
+
/**
* register_pernet_subsys - register a network namespace subsystem
* @ops: pernet operations structure for the subsystem