ipmr: Add dedicated mutex for mrt->{mfc_hash,mfc_cache_list}.

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>
This commit is contained in:
Kuniyuki Iwashima
2026-03-02 18:49:41 -08:00
committed by Jakub Kicinski
parent 4480d5fa1f
commit 3c1e53e554
2 changed files with 23 additions and 6 deletions
+1
View 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;
+22 -6
View 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;