net: Automatically allocate per namespace data.
To get the full benefit of batched network namespace cleanup netowrk device deletion needs to be performed by the generic code. When using register_pernet_gen_device and freeing the data in exit_net it is impossible to delay allocation until after exit_net has called as the device uninit methods are no longer safe. To correct this, and to simplify working with per network namespace data I have moved allocation and deletion of per network namespace data into the network namespace core. The core now frees the data only after all of the network namespace exit routines have run. Now it is only required to set the new fields .id and .size in the pernet_operations structure if you want network namespace data to be managed for you automatically. This makes the current register_pernet_gen_device and register_pernet_gen_subsys routines unnecessary. For the moment I have left them as compatibility wrappers in net_namespace.h They will be removed once all of the users have been updated. Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
2b035b3997
Коммит
f875bae065
|
@ -236,6 +236,8 @@ struct pernet_operations {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
int (*init)(struct net *net);
|
int (*init)(struct net *net);
|
||||||
void (*exit)(struct net *net);
|
void (*exit)(struct net *net);
|
||||||
|
int *id;
|
||||||
|
size_t size;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -259,12 +261,30 @@ struct pernet_operations {
|
||||||
*/
|
*/
|
||||||
extern int register_pernet_subsys(struct pernet_operations *);
|
extern int register_pernet_subsys(struct pernet_operations *);
|
||||||
extern void unregister_pernet_subsys(struct pernet_operations *);
|
extern void unregister_pernet_subsys(struct pernet_operations *);
|
||||||
extern int register_pernet_gen_subsys(int *id, struct pernet_operations *);
|
|
||||||
extern void unregister_pernet_gen_subsys(int id, struct pernet_operations *);
|
|
||||||
extern int register_pernet_device(struct pernet_operations *);
|
extern int register_pernet_device(struct pernet_operations *);
|
||||||
extern void unregister_pernet_device(struct pernet_operations *);
|
extern void unregister_pernet_device(struct pernet_operations *);
|
||||||
extern int register_pernet_gen_device(int *id, struct pernet_operations *);
|
|
||||||
extern void unregister_pernet_gen_device(int id, struct pernet_operations *);
|
static inline int register_pernet_gen_subsys(int *id, struct pernet_operations *ops)
|
||||||
|
{
|
||||||
|
ops->id = id;
|
||||||
|
return register_pernet_subsys(ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void unregister_pernet_gen_subsys(int id, struct pernet_operations *ops)
|
||||||
|
{
|
||||||
|
return unregister_pernet_subsys(ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int register_pernet_gen_device(int *id, struct pernet_operations *ops)
|
||||||
|
{
|
||||||
|
ops->id = id;
|
||||||
|
return register_pernet_device(ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void unregister_pernet_gen_device(int id, struct pernet_operations *ops)
|
||||||
|
{
|
||||||
|
return unregister_pernet_device(ops);
|
||||||
|
}
|
||||||
|
|
||||||
struct ctl_path;
|
struct ctl_path;
|
||||||
struct ctl_table;
|
struct ctl_table;
|
||||||
|
|
|
@ -43,13 +43,40 @@ static void unregister_netdevices(struct net *net, struct list_head *list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ops_init(const struct pernet_operations *ops, struct net *net)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
if (ops->id && ops->size) {
|
||||||
|
void *data = kzalloc(ops->size, GFP_KERNEL);
|
||||||
|
if (!data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
err = net_assign_generic(net, *ops->id, data);
|
||||||
|
if (err) {
|
||||||
|
kfree(data);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ops->init)
|
||||||
|
return ops->init(net);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ops_free(const struct pernet_operations *ops, struct net *net)
|
||||||
|
{
|
||||||
|
if (ops->id && ops->size) {
|
||||||
|
int id = *ops->id;
|
||||||
|
kfree(net_generic(net, id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* setup_net runs the initializers for the network namespace object.
|
* setup_net runs the initializers for the network namespace object.
|
||||||
*/
|
*/
|
||||||
static __net_init int setup_net(struct net *net)
|
static __net_init int setup_net(struct net *net)
|
||||||
{
|
{
|
||||||
/* Must be called with net_mutex held */
|
/* Must be called with net_mutex held */
|
||||||
struct pernet_operations *ops;
|
const struct pernet_operations *ops, *saved_ops;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
atomic_set(&net->count, 1);
|
atomic_set(&net->count, 1);
|
||||||
|
@ -59,11 +86,9 @@ static __net_init int setup_net(struct net *net)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
list_for_each_entry(ops, &pernet_list, list) {
|
list_for_each_entry(ops, &pernet_list, list) {
|
||||||
if (ops->init) {
|
error = ops_init(ops, net);
|
||||||
error = ops->init(net);
|
if (error < 0)
|
||||||
if (error < 0)
|
goto out_undo;
|
||||||
goto out_undo;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
return error;
|
return error;
|
||||||
|
@ -72,6 +97,7 @@ out_undo:
|
||||||
/* Walk through the list backwards calling the exit functions
|
/* Walk through the list backwards calling the exit functions
|
||||||
* for the pernet modules whose init functions did not fail.
|
* for the pernet modules whose init functions did not fail.
|
||||||
*/
|
*/
|
||||||
|
saved_ops = ops;
|
||||||
list_for_each_entry_continue_reverse(ops, &pernet_list, list) {
|
list_for_each_entry_continue_reverse(ops, &pernet_list, list) {
|
||||||
if (ops->exit)
|
if (ops->exit)
|
||||||
ops->exit(net);
|
ops->exit(net);
|
||||||
|
@ -83,6 +109,9 @@ out_undo:
|
||||||
rtnl_unlock();
|
rtnl_unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ops = saved_ops;
|
||||||
|
list_for_each_entry_continue_reverse(ops, &pernet_list, list)
|
||||||
|
ops_free(ops, net);
|
||||||
|
|
||||||
rcu_barrier();
|
rcu_barrier();
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -175,7 +204,7 @@ static LIST_HEAD(cleanup_list); /* Must hold cleanup_list_lock to touch */
|
||||||
|
|
||||||
static void cleanup_net(struct work_struct *work)
|
static void cleanup_net(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct pernet_operations *ops;
|
const struct pernet_operations *ops;
|
||||||
struct net *net, *tmp;
|
struct net *net, *tmp;
|
||||||
LIST_HEAD(net_kill_list);
|
LIST_HEAD(net_kill_list);
|
||||||
|
|
||||||
|
@ -214,6 +243,13 @@ static void cleanup_net(struct work_struct *work)
|
||||||
rtnl_unlock();
|
rtnl_unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* Free the net generic variables */
|
||||||
|
list_for_each_entry_reverse(ops, &pernet_list, list) {
|
||||||
|
if (ops->size && ops->id) {
|
||||||
|
list_for_each_entry(net, &net_kill_list, cleanup_list)
|
||||||
|
ops_free(ops, net);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mutex_unlock(&net_mutex);
|
mutex_unlock(&net_mutex);
|
||||||
|
|
||||||
|
@ -309,16 +345,16 @@ static int __init net_ns_init(void)
|
||||||
pure_initcall(net_ns_init);
|
pure_initcall(net_ns_init);
|
||||||
|
|
||||||
#ifdef CONFIG_NET_NS
|
#ifdef CONFIG_NET_NS
|
||||||
static int register_pernet_operations(struct list_head *list,
|
static int __register_pernet_operations(struct list_head *list,
|
||||||
struct pernet_operations *ops)
|
struct pernet_operations *ops)
|
||||||
{
|
{
|
||||||
struct net *net, *undo_net;
|
struct net *net, *undo_net;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
list_add_tail(&ops->list, list);
|
list_add_tail(&ops->list, list);
|
||||||
if (ops->init) {
|
if (ops->init || (ops->id && ops->size)) {
|
||||||
for_each_net(net) {
|
for_each_net(net) {
|
||||||
error = ops->init(net);
|
error = ops_init(ops, net);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_undo;
|
goto out_undo;
|
||||||
}
|
}
|
||||||
|
@ -336,10 +372,18 @@ out_undo:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
undone:
|
undone:
|
||||||
|
if (ops->size && ops->id) {
|
||||||
|
for_each_net(undo_net) {
|
||||||
|
if (net_eq(undo_net, net))
|
||||||
|
goto freed;
|
||||||
|
ops_free(ops, undo_net);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
freed:
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void unregister_pernet_operations(struct pernet_operations *ops)
|
static void __unregister_pernet_operations(struct pernet_operations *ops)
|
||||||
{
|
{
|
||||||
struct net *net;
|
struct net *net;
|
||||||
|
|
||||||
|
@ -347,26 +391,65 @@ static void unregister_pernet_operations(struct pernet_operations *ops)
|
||||||
if (ops->exit)
|
if (ops->exit)
|
||||||
for_each_net(net)
|
for_each_net(net)
|
||||||
ops->exit(net);
|
ops->exit(net);
|
||||||
|
if (ops->id && ops->size)
|
||||||
|
for_each_net(net)
|
||||||
|
ops_free(ops, net);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
static int __register_pernet_operations(struct list_head *list,
|
||||||
|
struct pernet_operations *ops)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
err = ops_init(ops, &init_net);
|
||||||
|
if (err)
|
||||||
|
ops_free(ops, &init_net);
|
||||||
|
return err;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __unregister_pernet_operations(struct pernet_operations *ops)
|
||||||
|
{
|
||||||
|
if (ops->exit)
|
||||||
|
ops->exit(&init_net);
|
||||||
|
ops_free(ops, &init_net);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_NET_NS */
|
||||||
|
|
||||||
|
static DEFINE_IDA(net_generic_ids);
|
||||||
|
|
||||||
static int register_pernet_operations(struct list_head *list,
|
static int register_pernet_operations(struct list_head *list,
|
||||||
struct pernet_operations *ops)
|
struct pernet_operations *ops)
|
||||||
{
|
{
|
||||||
if (ops->init == NULL)
|
int error;
|
||||||
return 0;
|
|
||||||
return ops->init(&init_net);
|
if (ops->id) {
|
||||||
|
again:
|
||||||
|
error = ida_get_new_above(&net_generic_ids, 1, ops->id);
|
||||||
|
if (error < 0) {
|
||||||
|
if (error == -EAGAIN) {
|
||||||
|
ida_pre_get(&net_generic_ids, GFP_KERNEL);
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error = __register_pernet_operations(list, ops);
|
||||||
|
if (error && ops->id)
|
||||||
|
ida_remove(&net_generic_ids, *ops->id);
|
||||||
|
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void unregister_pernet_operations(struct pernet_operations *ops)
|
static void unregister_pernet_operations(struct pernet_operations *ops)
|
||||||
{
|
{
|
||||||
if (ops->exit)
|
|
||||||
ops->exit(&init_net);
|
__unregister_pernet_operations(ops);
|
||||||
|
if (ops->id)
|
||||||
|
ida_remove(&net_generic_ids, *ops->id);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
static DEFINE_IDA(net_generic_ids);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* register_pernet_subsys - register a network namespace subsystem
|
* register_pernet_subsys - register a network namespace subsystem
|
||||||
|
@ -414,38 +497,6 @@ void unregister_pernet_subsys(struct pernet_operations *module)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(unregister_pernet_subsys);
|
EXPORT_SYMBOL_GPL(unregister_pernet_subsys);
|
||||||
|
|
||||||
int register_pernet_gen_subsys(int *id, struct pernet_operations *ops)
|
|
||||||
{
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
mutex_lock(&net_mutex);
|
|
||||||
again:
|
|
||||||
rv = ida_get_new_above(&net_generic_ids, 1, id);
|
|
||||||
if (rv < 0) {
|
|
||||||
if (rv == -EAGAIN) {
|
|
||||||
ida_pre_get(&net_generic_ids, GFP_KERNEL);
|
|
||||||
goto again;
|
|
||||||
}
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
rv = register_pernet_operations(first_device, ops);
|
|
||||||
if (rv < 0)
|
|
||||||
ida_remove(&net_generic_ids, *id);
|
|
||||||
out:
|
|
||||||
mutex_unlock(&net_mutex);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(register_pernet_gen_subsys);
|
|
||||||
|
|
||||||
void unregister_pernet_gen_subsys(int id, struct pernet_operations *ops)
|
|
||||||
{
|
|
||||||
mutex_lock(&net_mutex);
|
|
||||||
unregister_pernet_operations(ops);
|
|
||||||
ida_remove(&net_generic_ids, id);
|
|
||||||
mutex_unlock(&net_mutex);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(unregister_pernet_gen_subsys);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* register_pernet_device - register a network namespace device
|
* register_pernet_device - register a network namespace device
|
||||||
* @ops: pernet operations structure for the subsystem
|
* @ops: pernet operations structure for the subsystem
|
||||||
|
@ -477,30 +528,6 @@ int register_pernet_device(struct pernet_operations *ops)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(register_pernet_device);
|
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
|
* unregister_pernet_device - unregister a network namespace netdevice
|
||||||
* @ops: pernet operations structure to manipulate
|
* @ops: pernet operations structure to manipulate
|
||||||
|
@ -520,17 +547,6 @@ void unregister_pernet_device(struct pernet_operations *ops)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(unregister_pernet_device);
|
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)
|
static void net_generic_release(struct rcu_head *rcu)
|
||||||
{
|
{
|
||||||
struct net_generic *ng;
|
struct net_generic *ng;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче