#include <linux/list.h>
#include <linux/delay.h>
#include <linux/sched.h>
+#include <linux/idr.h>
#include <net/net_namespace.h>
+#include <net/netns/generic.h>
/*
* Our network namespace constructor/destructor lists
struct net init_net;
EXPORT_SYMBOL(init_net);
+#define INITIAL_NET_GEN_PTRS 13 /* +1 for len +2 for rcu_head */
+
/*
* setup_net runs the initializers for the network namespace object.
*/
/* Must be called with net_mutex held */
struct pernet_operations *ops;
int error;
+ struct net_generic *ng;
atomic_set(&net->count, 1);
atomic_set(&net->use_count, 0);
+ error = -ENOMEM;
+ ng = kzalloc(sizeof(struct net_generic) +
+ INITIAL_NET_GEN_PTRS * sizeof(void *), GFP_KERNEL);
+ if (ng == NULL)
+ goto out;
+
+ ng->len = INITIAL_NET_GEN_PTRS;
+ INIT_RCU_HEAD(&ng->rcu);
+ rcu_assign_pointer(net->gen, ng);
+
error = 0;
list_for_each_entry(ops, &pernet_list, list) {
if (ops->init) {
}
rcu_barrier();
+ kfree(ng);
goto out;
}
#ifdef CONFIG_NET_NS
static struct kmem_cache *net_cachep;
+static struct workqueue_struct *netns_wq;
static struct net *net_alloc(void)
{
{
/* 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);
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);
}
#endif
+static DEFINE_IDA(net_generic_ids);
+
/**
* register_pernet_subsys - register a network namespace subsystem
* @ops: pernet operations structure for the subsystem
* @ops: pernet operations structure to manipulate
*
* Remove the pernet operations structure from the list to be
- * used when network namespaces are created or destoryed. In
+ * used when network namespaces are created or destroyed. In
* addition run the exit method for all existing network
* namespaces.
*/
}
EXPORT_SYMBOL_GPL(register_pernet_device);
+int register_pernet_gen_device(int *id, struct pernet_operations *ops)
+{
+ int error;
+ mutex_lock(&net_mutex);
+again:
+ error = ida_get_new_above(&net_generic_ids, 1, id);
+ if (error) {
+ if (error == -EAGAIN) {
+ ida_pre_get(&net_generic_ids, GFP_KERNEL);
+ goto again;
+ }
+ goto out;
+ }
+ error = register_pernet_operations(&pernet_list, ops);
+ if (error)
+ ida_remove(&net_generic_ids, *id);
+ else if (first_device == &pernet_list)
+ first_device = &ops->list;
+out:
+ mutex_unlock(&net_mutex);
+ return error;
+}
+EXPORT_SYMBOL_GPL(register_pernet_gen_device);
+
/**
* unregister_pernet_device - unregister a network namespace netdevice
* @ops: pernet operations structure to manipulate
*
* Remove the pernet operations structure from the list to be
- * used when network namespaces are created or destoryed. In
+ * used when network namespaces are created or destroyed. In
* addition run the exit method for all existing network
* namespaces.
*/
mutex_unlock(&net_mutex);
}
EXPORT_SYMBOL_GPL(unregister_pernet_device);
+
+void unregister_pernet_gen_device(int id, struct pernet_operations *ops)
+{
+ mutex_lock(&net_mutex);
+ if (&ops->list == first_device)
+ first_device = first_device->next;
+ unregister_pernet_operations(ops);
+ ida_remove(&net_generic_ids, id);
+ mutex_unlock(&net_mutex);
+}
+EXPORT_SYMBOL_GPL(unregister_pernet_gen_device);
+
+static void net_generic_release(struct rcu_head *rcu)
+{
+ struct net_generic *ng;
+
+ ng = container_of(rcu, struct net_generic, rcu);
+ kfree(ng);
+}
+
+int net_assign_generic(struct net *net, int id, void *data)
+{
+ struct net_generic *ng, *old_ng;
+
+ BUG_ON(!mutex_is_locked(&net_mutex));
+ BUG_ON(id == 0);
+
+ ng = old_ng = net->gen;
+ if (old_ng->len >= id)
+ goto assign;
+
+ ng = kzalloc(sizeof(struct net_generic) +
+ id * sizeof(void *), GFP_KERNEL);
+ if (ng == NULL)
+ return -ENOMEM;
+
+ /*
+ * Some synchronisation notes:
+ *
+ * The net_generic explores the net->gen array inside rcu
+ * read section. Besides once set the net->gen->ptr[x]
+ * pointer never changes (see rules in netns/generic.h).
+ *
+ * That said, we simply duplicate this array and schedule
+ * the old copy for kfree after a grace period.
+ */
+
+ ng->len = id;
+ INIT_RCU_HEAD(&ng->rcu);
+ memcpy(&ng->ptr, &old_ng->ptr, old_ng->len);
+
+ rcu_assign_pointer(net->gen, ng);
+ call_rcu(&old_ng->rcu, net_generic_release);
+assign:
+ ng->ptr[id - 1] = data;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(net_assign_generic);