Skip to content
Permalink
Browse files
mld: convert ifmcaddr6 to RCU
The ifmcaddr6 has been protected by inet6_dev->lock(rwlock) so that
the critical section is atomic context. In order to switch this context,
changing locking is needed. The ifmcaddr6 actually already protected by
RTNL So if it's converted to use RCU, its control path context can be
switched to sleepable.

Because of this conversion, the locking scenario can be changed.
So, the locking scenario is changed to the following.
1. ifmcaddr6->mca_lock only protects following resources.
   a) ifmcaddr6->mca_flags
   b) ifmcaddr6->mca_work.
   c) ifmcaddr6->mca_sources->sf_gsresp
2. inet6_dev->lock only protects following resources.
   a) inet6_dev->mc_gq_running
   b) inet6_dev->mc_gq_work
   c) inet6_dev->mc_ifc_count
   d) inet6_dev->mc_ifc_work
   e) inet6_dev->mc_delerec_work
3. Other resources are protected by RTNL and RCU.

There are only two atomic context locks, they are ifmcaddr6->mca_lock
and inet6_dev->lock. These locks are protecting resources, they are
written on the datapath.
RTNL can't be used on the datapath, these locks are used.

Suggested-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: Taehee Yoo <ap420073@gmail.com>
  • Loading branch information
TaeheeYoo authored and intel-lab-lkp committed Feb 13, 2021
1 parent 50d689e commit 05d3b96bdcaec52bc3f2ef1f96e571dc17bf9f5a
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 201 deletions.
@@ -1098,8 +1098,9 @@ static int qeth_l3_add_mcast_rtnl(struct net_device *dev, int vid, void *arg)
tmp.disp_flag = QETH_DISP_ADDR_ADD;
tmp.is_multicast = 1;

read_lock_bh(&in6_dev->lock);
for (im6 = in6_dev->mc_list; im6 != NULL; im6 = im6->next) {
for (im6 = rtnl_dereference(in6_dev->mc_list);
im6;
im6 = rtnl_dereference(im6->next)) {
tmp.u.a6.addr = im6->mca_addr;

ipm = qeth_l3_find_addr_by_ip(card, &tmp);
@@ -1117,7 +1118,6 @@ static int qeth_l3_add_mcast_rtnl(struct net_device *dev, int vid, void *arg)
qeth_l3_ipaddr_hash(ipm));

}
read_unlock_bh(&in6_dev->lock);

out:
return 0;
@@ -113,7 +113,7 @@ struct ip6_sf_list {
struct ifmcaddr6 {
struct in6_addr mca_addr;
struct inet6_dev *idev;
struct ifmcaddr6 *next;
struct ifmcaddr6 __rcu *next;
struct ip6_sf_list __rcu *mca_sources;
struct ip6_sf_list *mca_tomb;
unsigned int mca_sfmode;
@@ -128,6 +128,7 @@ struct ifmcaddr6 {
spinlock_t mca_lock;
unsigned long mca_cstamp;
unsigned long mca_tstamp;
struct rcu_head rcu;
};

/* Anycast stuff */
@@ -166,7 +167,7 @@ struct inet6_dev {

struct list_head addr_list;

struct ifmcaddr6 *mc_list;
struct ifmcaddr6 __rcu *mc_list;
struct ifmcaddr6 *mc_tomb;

unsigned char mc_qrv; /* Query Robustness Variable */
@@ -454,8 +454,9 @@ batadv_mcast_mla_softif_get_ipv6(struct net_device *dev,
return 0;
}

read_lock_bh(&in6_dev->lock);
for (pmc6 = in6_dev->mc_list; pmc6; pmc6 = pmc6->next) {
for (pmc6 = rcu_dereference(in6_dev->mc_list);
pmc6;
pmc6 = rcu_dereference(pmc6->next)) {
if (IPV6_ADDR_MC_SCOPE(&pmc6->mca_addr) <
IPV6_ADDR_SCOPE_LINKLOCAL)
continue;
@@ -484,7 +485,6 @@ batadv_mcast_mla_softif_get_ipv6(struct net_device *dev,
hlist_add_head(&new->list, mcast_list);
ret++;
}
read_unlock_bh(&in6_dev->lock);
rcu_read_unlock();

return ret;
@@ -5107,17 +5107,20 @@ static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb,
break;
}
case MULTICAST_ADDR:
read_unlock_bh(&idev->lock);
fillargs->event = RTM_GETMULTICAST;

/* multicast address */
for (ifmca = idev->mc_list; ifmca;
ifmca = ifmca->next, ip_idx++) {
for (ifmca = rcu_dereference(idev->mc_list);
ifmca;
ifmca = rcu_dereference(ifmca->next), ip_idx++) {
if (ip_idx < s_ip_idx)
continue;
err = inet6_fill_ifmcaddr(skb, ifmca, fillargs);
if (err < 0)
break;
}
read_lock_bh(&idev->lock);
break;
case ANYCAST_ADDR:
fillargs->event = RTM_GETANYCAST;
@@ -6093,10 +6096,8 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)

static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
{
rcu_read_lock_bh();
if (likely(ifp->idev->dead == 0))
__ipv6_ifa_notify(event, ifp);
rcu_read_unlock_bh();
}

#ifdef CONFIG_SYSCTL
@@ -250,7 +250,7 @@ void in6_dev_finish_destroy(struct inet6_dev *idev)
struct net_device *dev = idev->dev;

WARN_ON(!list_empty(&idev->addr_list));
WARN_ON(idev->mc_list);
WARN_ON(rcu_access_pointer(idev->mc_list));
WARN_ON(timer_pending(&idev->rs_timer));

#ifdef NET_REFCNT_DEBUG
@@ -222,7 +222,7 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol,
inet->mc_loop = 1;
inet->mc_ttl = 1;
inet->mc_index = 0;
inet->mc_list = NULL;
RCU_INIT_POINTER(inet->mc_list, NULL);
inet->rcv_tos = 0;

if (net->ipv4.sysctl_ip_no_pmtu_disc)

0 comments on commit 05d3b96

Please sign in to comment.