]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ipmr: Add dedicated mutex for mrt->{mfc_hash,mfc_cache_list}.
authorKuniyuki Iwashima <kuniyu@google.com>
Sat, 28 Feb 2026 22:17:32 +0000 (22:17 +0000)
committerJakub Kicinski <kuba@kernel.org>
Tue, 3 Mar 2026 02:49:41 +0000 (18:49 -0800)
We will no longer hold RTNL for ipmr_rtm_route() to modify the
MFC hash table.

Only __dev_get_by_index() in rtm_to_ipmr_mfcc() is the RTNL
dependant, otherwise, we just need protection for mrt->mfc_hash
and mrt->mfc_cache_list.

Let's add a new mutex for ipmr_mfc_add(), ipmr_mfc_delete(),
and mroute_clean_tables() (setsockopt(MRT_FLUSH or MRT_DONE)).

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20260228221800.1082070-15-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/net/netns/ipv4.h
net/ipv4/ipmr.c

index 94dca64fec416946eb2367067240344f44c12764..4c249aeaf7f12f8237232a6f3d3f3ed56d0a53d4 100644 (file)
@@ -281,6 +281,7 @@ struct netns_ipv4 {
 #endif
        struct fib_notifier_ops *ipmr_notifier_ops;
        atomic_t                ipmr_seq;
+       struct mutex            mfc_mutex;
 #endif
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
        struct sysctl_fib_multipath_hash_seed sysctl_fib_multipath_hash_seed;
index 6ec73796d84da81d0621b0f9bbeed10d83589113..d4983d8a9b2a06c6a813169b383bf46bbd731e9b 100644 (file)
@@ -1329,6 +1329,8 @@ static void mroute_clean_tables(struct mr_table *mrt, int flags,
 
        /* Wipe the cache */
        if (flags & (MRT_FLUSH_MFC | MRT_FLUSH_MFC_STATIC)) {
+               mutex_lock(&net->ipv4.mfc_mutex);
+
                list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) {
                        if (((c->mfc_flags & MFC_STATIC) && !(flags & MRT_FLUSH_MFC_STATIC)) ||
                            (!(c->mfc_flags & MFC_STATIC) && !(flags & MRT_FLUSH_MFC)))
@@ -1341,6 +1343,8 @@ static void mroute_clean_tables(struct mr_table *mrt, int flags,
                        mroute_netlink_event(mrt, cache, RTM_DELROUTE);
                        mr_cache_put(c);
                }
+
+               mutex_unlock(&net->ipv4.mfc_mutex);
        }
 
        if (flags & MRT_FLUSH_MFC) {
@@ -1498,12 +1502,17 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval,
                }
                if (parent == 0)
                        parent = mfc.mfcc_parent;
+
+               mutex_lock(&net->ipv4.mfc_mutex);
+
                if (optname == MRT_DEL_MFC || optname == MRT_DEL_MFC_PROXY)
                        ret = ipmr_mfc_delete(mrt, &mfc, parent);
                else
                        ret = ipmr_mfc_add(net, mrt, &mfc,
                                           sk == rtnl_dereference(mrt->mroute_sk),
                                           parent);
+
+               mutex_unlock(&net->ipv4.mfc_mutex);
                break;
        case MRT_FLUSH: {
                LIST_HEAD(dev_kill_list);
@@ -2913,21 +2922,26 @@ static int ipmr_rtm_route(struct sk_buff *skb, struct nlmsghdr *nlh,
                          struct netlink_ext_ack *extack)
 {
        struct net *net = sock_net(skb->sk);
-       int ret, mrtsock, parent;
-       struct mr_table *tbl;
+       int ret, mrtsock = 0, parent;
+       struct mr_table *tbl = NULL;
        struct mfcctl mfcc;
 
-       mrtsock = 0;
-       tbl = NULL;
        ret = rtm_to_ipmr_mfcc(net, nlh, &mfcc, &mrtsock, &tbl, extack);
        if (ret < 0)
                return ret;
 
        parent = ret ? mfcc.mfcc_parent : -1;
+
+       mutex_lock(&net->ipv4.mfc_mutex);
+
        if (nlh->nlmsg_type == RTM_NEWROUTE)
-               return ipmr_mfc_add(net, tbl, &mfcc, mrtsock, parent);
+               ret = ipmr_mfc_add(net, tbl, &mfcc, mrtsock, parent);
        else
-               return ipmr_mfc_delete(tbl, &mfcc, parent);
+               ret = ipmr_mfc_delete(tbl, &mfcc, parent);
+
+       mutex_unlock(&net->ipv4.mfc_mutex);
+
+       return ret;
 }
 
 static bool ipmr_fill_table(struct mr_table *mrt, struct sk_buff *skb)
@@ -3269,6 +3283,8 @@ static int __net_init ipmr_net_init(struct net *net)
        LIST_HEAD(dev_kill_list);
        int err;
 
+       mutex_init(&net->ipv4.mfc_mutex);
+
        err = ipmr_notifier_init(net);
        if (err)
                goto ipmr_notifier_fail;