fib_rules_unregister() removes ops from net->rules_ops under
spinlock, calls ops->delete() for each rule, and frees the ops.
ipmr_rules_ops_template does not have ->delete(), and any
operation does not require RTNL there.
Let's move fib_rules_unregister() from ipmr_rules_exit_rtnl()
to ipmr_net_exit().
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
net/ipv4/ipmr.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 537be40af9e0..0dd683932d2b 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -282,6 +282,11 @@ static int __net_init ipmr_rules_init(struct net *net)
return err;
}
+static void __net_exit ipmr_rules_exit(struct net *net)
+{
+ fib_rules_unregister(net->ipv4.mr_rules_ops);
+}
+
static void __net_exit ipmr_rules_exit_rtnl(struct net *net,
struct list_head *dev_kill_list)
{@@ -291,8 +296,6 @@ static void __net_exit ipmr_rules_exit_rtnl(struct net *net,
list_del(&mrt->list);
ipmr_free_table(mrt, dev_kill_list);
}
-
- fib_rules_unregister(net->ipv4.mr_rules_ops);
}
static int ipmr_rules_dump(struct net *net, struct notifier_block *nb,
@@ -348,6 +351,10 @@ static int __net_init ipmr_rules_init(struct net *net)
return 0;
}
+static void __net_exit ipmr_rules_exit(struct net *net)
+{
+}
+
static void __net_exit ipmr_rules_exit_rtnl(struct net *net,
struct list_head *dev_kill_list)
{@@ -3284,6 +3291,7 @@ static int __net_init ipmr_net_init(struct net *net)
remove_proc_entry("ip_mr_vif", net->proc_net);
proc_vif_fail:
ipmr_rules_exit_rtnl(net, &dev_kill_list);
+ ipmr_rules_exit(net);
#endif
ipmr_rules_fail:
ipmr_notifier_exit(net);@@ -3297,6 +3305,7 @@ static void __net_exit ipmr_net_exit(struct net *net)
remove_proc_entry("ip_mr_cache", net->proc_net);
remove_proc_entry("ip_mr_vif", net->proc_net);
#endif
+ ipmr_rules_exit(net);
ipmr_notifier_exit(net);
}
--
2.53.0.414.gf7e9f6c205-goog