summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/net/net_namespace.h2
-rw-r--r--net/core/net_namespace.c53
2 files changed, 41 insertions, 14 deletions
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index bd57d8fb54f1..b071e6eed9d5 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -475,6 +475,8 @@ struct pernet_operations {
void (*exit)(struct net *net);
void (*exit_batch)(struct list_head *net_exit_list);
/* Following method is called with RTNL held. */
+ void (*exit_rtnl)(struct net *net,
+ struct list_head *dev_kill_list);
void (*exit_batch_rtnl)(struct list_head *net_exit_list,
struct list_head *dev_kill_list);
unsigned int * const id;
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 37026776ae4e..afaa3d1bda8d 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -163,16 +163,51 @@ static void ops_pre_exit_list(const struct pernet_operations *ops,
}
}
+static void ops_exit_rtnl_list(const struct list_head *ops_list,
+ const struct pernet_operations *ops,
+ struct list_head *net_exit_list)
+{
+ const struct pernet_operations *saved_ops = ops;
+ LIST_HEAD(dev_kill_list);
+ struct net *net;
+
+ rtnl_lock();
+
+ list_for_each_entry(net, net_exit_list, exit_list) {
+ __rtnl_net_lock(net);
+
+ ops = saved_ops;
+ list_for_each_entry_continue_reverse(ops, ops_list, list) {
+ if (ops->exit_rtnl)
+ ops->exit_rtnl(net, &dev_kill_list);
+ }
+
+ __rtnl_net_unlock(net);
+ }
+
+ ops = saved_ops;
+ list_for_each_entry_continue_reverse(ops, ops_list, list) {
+ if (ops->exit_batch_rtnl)
+ ops->exit_batch_rtnl(net_exit_list, &dev_kill_list);
+ }
+
+ unregister_netdevice_many(&dev_kill_list);
+
+ rtnl_unlock();
+}
+
static void ops_exit_list(const struct pernet_operations *ops,
struct list_head *net_exit_list)
{
- struct net *net;
if (ops->exit) {
+ struct net *net;
+
list_for_each_entry(net, net_exit_list, exit_list) {
ops->exit(net);
cond_resched();
}
}
+
if (ops->exit_batch)
ops->exit_batch(net_exit_list);
}
@@ -213,18 +248,8 @@ static void ops_undo_list(const struct list_head *ops_list,
else
synchronize_rcu();
- if (hold_rtnl) {
- LIST_HEAD(dev_kill_list);
-
- ops = saved_ops;
- rtnl_lock();
- list_for_each_entry_continue_reverse(ops, ops_list, list) {
- if (ops->exit_batch_rtnl)
- ops->exit_batch_rtnl(net_exit_list, &dev_kill_list);
- }
- unregister_netdevice_many(&dev_kill_list);
- rtnl_unlock();
- }
+ if (hold_rtnl)
+ ops_exit_rtnl_list(ops_list, saved_ops, net_exit_list);
ops = saved_ops;
list_for_each_entry_continue_reverse(ops, ops_list, list)
@@ -238,7 +263,7 @@ static void ops_undo_list(const struct list_head *ops_list,
static void ops_undo_single(struct pernet_operations *ops,
struct list_head *net_exit_list)
{
- bool hold_rtnl = !!ops->exit_batch_rtnl;
+ bool hold_rtnl = ops->exit_rtnl || ops->exit_batch_rtnl;
LIST_HEAD(ops_list);
list_add(&ops->list, &ops_list);