]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ip6mr: Replace RTNL with a dedicated mutex for MFC.
authorKuniyuki Iwashima <kuniyu@google.com>
Thu, 4 Jun 2026 22:46:32 +0000 (22:46 +0000)
committerJakub Kicinski <kuba@kernel.org>
Tue, 9 Jun 2026 00:06:24 +0000 (17:06 -0700)
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>
include/net/netns/ipv6.h
net/ipv6/ip6mr.c

index 875916d60bfe4e640d2fd834603f1896eb1b4842..668f10498c5c77110e4aba192942ff918238ef43 100644 (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;
index b13ce9c2c463b46631a582eb4b818f46518dac1f..5a4816225fb1b9fecb7fca93556ec10654c649d0 100644 (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: