ip6mr: Replace RTNL with a dedicated mutex for MFC.

ip6mr does not have rtnetlink interface for MFC unlike ipmr,
which uses dev_get_by_index_rcu() to set struct mfcctl.mfcc_parent.

ip6mr_mfc_add() and ip6mr_mfc_delete() are called under RTNL
from ip6_mroute_setsockopt() only.

There are no RTNL dependant, but ip6_mroute_setsockopt() reuses
RTNL just for mrt->mfc_hash and mrt->mfc_cache_list.

Let's replace RTNL with a new per-netns mutex.

Later, ip6mr_notifier_ops and ipmr_seq will be moved under
CONFIG_IPV6_MROUTE.

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Link: https://patch.msgid.link/20260604224712.3209821-15-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Kuniyuki Iwashima 2026-06-04 22:46:32 +00:00 committed by Jakub Kicinski
parent 2290727b2f
commit a4c9842172
2 changed files with 13 additions and 5 deletions

View File

@ -112,6 +112,7 @@ struct netns_ipv6 {
struct list_head mr6_tables;
struct fib_rules_ops *mr6_rules_ops;
#endif
struct mutex mfc_mutex;
#endif
atomic_t dev_addr_genid;
atomic_t fib6_sernum;

View File

@ -1266,7 +1266,6 @@ static int ip6mr_mfc_delete(struct mr_table *mrt, struct mf6cctl *mfc,
{
struct mfc6_cache *c;
/* The entries are added/deleted only under RTNL */
rcu_read_lock();
c = ip6mr_cache_find_parent(mrt, &mfc->mf6cc_origin.sin6_addr,
&mfc->mf6cc_mcastgrp.sin6_addr, parent);
@ -1358,6 +1357,8 @@ static int __net_init ip6mr_net_init(struct net *net)
#endif
int err;
mutex_init(&net->ipv6.mfc_mutex);
err = ip6mr_notifier_init(net);
if (err)
return err;
@ -1486,7 +1487,6 @@ static int ip6mr_mfc_add(struct net *net, struct mr_table *mrt,
ttls[i] = 1;
}
/* The entries are added/deleted only under RTNL */
rcu_read_lock();
c = ip6mr_cache_find_parent(mrt, &mfc->mf6cc_origin.sin6_addr,
&mfc->mf6cc_mcastgrp.sin6_addr, parent);
@ -1581,6 +1581,8 @@ static void mroute_clean_tables(struct mr_table *mrt, int flags,
/* Wipe the cache */
if (flags & (MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC)) {
mutex_lock(&net->ipv6.mfc_mutex);
list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) {
if (((c->mfc_flags & MFC_STATIC) && !(flags & MRT6_FLUSH_MFC_STATIC)) ||
(!(c->mfc_flags & MFC_STATIC) && !(flags & MRT6_FLUSH_MFC)))
@ -1592,6 +1594,8 @@ static void mroute_clean_tables(struct mr_table *mrt, int flags,
mr6_netlink_event(mrt, (struct mfc6_cache *)c, RTM_DELROUTE);
mr_cache_put(c);
}
mutex_unlock(&net->ipv6.mfc_mutex);
}
if (flags & MRT6_FLUSH_MFC) {
@ -1775,15 +1779,18 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval,
return -EFAULT;
if (parent == 0)
parent = mfc.mf6cc_parent;
rtnl_lock();
mutex_lock(&net->ipv6.mfc_mutex);
if (optname == MRT6_DEL_MFC || optname == MRT6_DEL_MFC_PROXY)
ret = ip6mr_mfc_delete(mrt, &mfc, parent);
else
ret = ip6mr_mfc_add(net, mrt, &mfc,
sk ==
rtnl_dereference(mrt->mroute_sk),
rcu_access_pointer(mrt->mroute_sk),
parent);
rtnl_unlock();
mutex_unlock(&net->ipv6.mfc_mutex);
return ret;
case MRT6_FLUSH: