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. Signed-off-by: Kuniyuki Iwashima --- include/net/netns/ipv6.h | 1 + net/ipv6/ip6mr.c | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index df00567374f4..6453d70d5946 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -114,6 +114,7 @@ struct netns_ipv6 { #endif struct fib_notifier_ops *ip6mr_notifier_ops; atomic_t ipmr_seq; + 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 b1443fb65b40..e4c31d05744b 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -1256,7 +1256,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); @@ -1346,6 +1345,8 @@ static int __net_init ip6mr_net_init(struct net *net) LIST_HEAD(dev_kill_list); int err; + mutex_init(&net->ipv6.mfc_mutex); + err = ip6mr_notifier_init(net); if (err) return err; @@ -1474,7 +1475,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); @@ -1553,6 +1553,7 @@ static int ip6mr_mfc_add(struct net *net, struct mr_table *mrt, static void mroute_clean_tables(struct mr_table *mrt, int flags, struct list_head *dev_kill_list) { + struct net *net = read_pnet(&mrt->net); struct mr_mfc *c, *tmp; int i; @@ -1569,18 +1570,21 @@ 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))) continue; rhltable_remove(&mrt->mfc_hash, &c->mnode, ip6mr_rht_params); list_del_rcu(&c->list); - call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net), - FIB_EVENT_ENTRY_DEL, + call_ip6mr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, (struct mfc6_cache *)c, mrt->id); 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) { @@ -1763,15 +1767,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: -- 2.53.0.1213.gd9a14994de-goog