From: Kuniyuki Iwashima Date: Thu, 4 Jun 2026 22:46:32 +0000 (+0000) Subject: ip6mr: Replace RTNL with a dedicated mutex for MFC. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a4c98421720a79587ad6c528f81fe7976a45976b;p=thirdparty%2Flinux.git 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 Link: https://patch.msgid.link/20260604224712.3209821-15-kuniyu@google.com Signed-off-by: Jakub Kicinski --- diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 875916d60bfe4..668f10498c5c7 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -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; diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index b13ce9c2c463b..5a4816225fb1b 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -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: