Merge branch 'ipv4-fib-remove-rtnl-in-fib_net_exit_batch'

Kuniyuki Iwashima says:

====================
ipv4: fib: Remove RTNL in fib_net_exit_batch().

Currently, we flush all IPv4 routes at ->exit_batch() during
netns dismantle, which requires an extra RTNL.

IPv4 routes are not added from the fast path unlike IPv6, so
we can flush routes before default_device_exit_batch().

However, there is implicit ordering between ip_fib_net_exit()
and default_device_exit_batch().

This series detangles it and moves ip_fib_net_exit() to
 ->exit_rtnl() to save the RTNL dance.

The same change for IPv6 will need more work.
====================

Link: https://patch.msgid.link/20260612063225.455191-1-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2026-06-15 11:49:31 -07:00
2 changed files with 20 additions and 27 deletions

View File

@@ -88,7 +88,8 @@ struct fib_table *fib_new_table(struct net *net, u32 id)
if (id == RT_TABLE_LOCAL && !net->ipv4.fib_has_custom_rules)
alias = fib_new_table(net, RT_TABLE_MAIN);
tb = fib_trie_table(id, alias);
if (check_net(net))
tb = fib_trie_table(id, alias);
if (!tb)
return NULL;
@@ -1606,7 +1607,7 @@ static void ip_fib_net_exit(struct net *net)
struct fib_table *tb;
hlist_for_each_entry_safe(tb, tmp, head, tb_hlist) {
hlist_del(&tb->tb_hlist);
hlist_del_rcu(&tb->tb_hlist);
fib_table_flush(net, tb, true);
fib_free_table(tb);
}
@@ -1615,9 +1616,6 @@ static void ip_fib_net_exit(struct net *net)
#ifdef CONFIG_IP_MULTIPLE_TABLES
fib4_rules_exit(net);
#endif
kfree(net->ipv4.fib_table_hash);
fib4_notifier_exit(net);
}
static int __net_init fib_net_init(struct net *net)
@@ -1653,35 +1651,36 @@ static int __net_init fib_net_init(struct net *net)
rtnl_net_lock(net);
ip_fib_net_exit(net);
rtnl_net_unlock(net);
kfree(net->ipv4.fib_table_hash);
fib4_notifier_exit(net);
goto out;
}
static void __net_exit fib_net_exit(struct net *net)
static void __net_exit fib_net_pre_exit(struct net *net)
{
fib_proc_exit(net);
nl_fib_lookup_exit(net);
}
static void __net_exit fib_net_exit_batch(struct list_head *net_list)
static void __net_exit fib_net_exit_rtnl(struct net *net,
struct list_head *dev_kill_list)
{
struct net *net;
ip_fib_net_exit(net);
}
rtnl_lock();
list_for_each_entry(net, net_list, exit_list) {
__rtnl_net_lock(net);
ip_fib_net_exit(net);
__rtnl_net_unlock(net);
}
rtnl_unlock();
list_for_each_entry(net, net_list, exit_list)
fib4_semantics_exit(net);
static void __net_exit fib_net_exit(struct net *net)
{
kfree(net->ipv4.fib_table_hash);
fib4_notifier_exit(net);
fib4_semantics_exit(net);
}
static struct pernet_operations fib_net_ops = {
.init = fib_net_init,
.pre_exit = fib_net_pre_exit,
.exit_rtnl = fib_net_exit_rtnl,
.exit = fib_net_exit,
.exit_batch = fib_net_exit_batch,
};
static const struct rtnl_msg_handler fib_rtnl_msg_handlers[] __initconst = {

View File

@@ -2046,18 +2046,12 @@ int fib_table_flush(struct net *net, struct fib_table *tb, bool flush_all)
hlist_for_each_entry_safe(fa, tmp, &n->leaf, fa_list) {
struct fib_info *fi = fa->fa_info;
if (!fi || tb->tb_id != fa->tb_id ||
(!(fi->fib_flags & RTNH_F_DEAD) &&
!fib_props[fa->fa_type].error)) {
if (!fi || tb->tb_id != fa->tb_id) {
slen = fa->fa_slen;
continue;
}
/* When not flushing the entire table, skip error
* routes that are not marked for deletion.
*/
if (!flush_all && fib_props[fa->fa_type].error &&
!(fi->fib_flags & RTNH_F_DEAD)) {
if (!flush_all && !(fi->fib_flags & RTNH_F_DEAD)) {
slen = fa->fa_slen;
continue;
}