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.
Suggested-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: Taehee Yoo <ap420073@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
tmp.disp_flag = QETH_DISP_ADDR_ADD;
tmp.is_multicast = 1;
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);
tmp.u.a6.addr = im6->mca_addr;
ipm = qeth_l3_find_addr_by_ip(card, &tmp);
qeth_l3_ipaddr_hash(ipm));
}
qeth_l3_ipaddr_hash(ipm));
}
- read_unlock_bh(&in6_dev->lock);
struct ifmcaddr6 {
struct in6_addr mca_addr;
struct inet6_dev *idev;
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 __rcu *mca_tomb;
unsigned int mca_sfmode;
struct ip6_sf_list __rcu *mca_sources;
struct ip6_sf_list __rcu *mca_tomb;
unsigned int mca_sfmode;
spinlock_t mca_lock;
unsigned long mca_cstamp;
unsigned long mca_tstamp;
spinlock_t mca_lock;
unsigned long mca_cstamp;
unsigned long mca_tstamp;
struct list_head addr_list;
struct list_head addr_list;
- struct ifmcaddr6 *mc_list;
- struct ifmcaddr6 *mc_tomb;
+ struct ifmcaddr6 __rcu *mc_list;
+ struct ifmcaddr6 __rcu *mc_tomb;
unsigned char mc_qrv; /* Query Robustness Variable */
unsigned char mc_gq_running;
unsigned char mc_qrv; /* Query Robustness Variable */
unsigned char mc_gq_running;
- 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;
if (IPV6_ADDR_MC_SCOPE(&pmc6->mca_addr) <
IPV6_ADDR_SCOPE_LINKLOCAL)
continue;
hlist_add_head(&new->list, mcast_list);
ret++;
}
hlist_add_head(&new->list, mcast_list);
ret++;
}
- read_unlock_bh(&in6_dev->lock);
rcu_read_unlock();
return ret;
rcu_read_unlock();
return ret;
break;
}
case MULTICAST_ADDR:
break;
}
case MULTICAST_ADDR:
+ read_unlock_bh(&idev->lock);
fillargs->event = RTM_GETMULTICAST;
/* multicast address */
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;
}
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;
break;
case ANYCAST_ADDR:
fillargs->event = RTM_GETANYCAST;
static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
{
static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
{
if (likely(ifp->idev->dead == 0))
__ipv6_ifa_notify(event, ifp);
if (likely(ifp->idev->dead == 0))
__ipv6_ifa_notify(event, ifp);
struct net_device *dev = idev->dev;
WARN_ON(!list_empty(&idev->addr_list));
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
WARN_ON(timer_pending(&idev->rs_timer));
#ifdef NET_REFCNT_DEBUG
inet->mc_loop = 1;
inet->mc_ttl = 1;
inet->mc_index = 0;
inet->mc_loop = 1;
inet->mc_ttl = 1;
inet->mc_index = 0;
+ RCU_INIT_POINTER(inet->mc_list, NULL);
inet->rcv_tos = 0;
if (net->ipv4.sysctl_ip_no_pmtu_disc)
inet->rcv_tos = 0;
if (net->ipv4.sysctl_ip_no_pmtu_disc)
* socket join on multicast group
*/
* socket join on multicast group
*/
+#define for_each_pmc_rtnl(np, pmc) \
+ for (pmc = rtnl_dereference((np)->ipv6_mc_list); \
+ pmc; \
+ pmc = rtnl_dereference(pmc->next))
+
#define for_each_pmc_rcu(np, pmc) \
for (pmc = rcu_dereference((np)->ipv6_mc_list); \
pmc; \
#define for_each_pmc_rcu(np, pmc) \
for (pmc = rcu_dereference((np)->ipv6_mc_list); \
pmc; \
psf; \
psf = rtnl_dereference(psf->sf_next))
psf; \
psf = rtnl_dereference(psf->sf_next))
+#define for_each_mc_rtnl(idev, mc) \
+ for (mc = rtnl_dereference((idev)->mc_list); \
+ mc; \
+ mc = rtnl_dereference(mc->next))
+
+#define for_each_mc_rcu(idev, mc) \
+ for (mc = rcu_dereference((idev)->mc_list); \
+ mc; \
+ mc = rcu_dereference(mc->next))
+
+#define for_each_mc_tomb(idev, mc) \
+ for (mc = rtnl_dereference((idev)->mc_tomb); \
+ mc; \
+ mc = rtnl_dereference(mc->next))
+
static int unsolicited_report_interval(struct inet6_dev *idev)
{
int iv;
static int unsolicited_report_interval(struct inet6_dev *idev)
{
int iv;
if (!ipv6_addr_is_multicast(addr))
return -EINVAL;
if (!ipv6_addr_is_multicast(addr))
return -EINVAL;
- rcu_read_lock();
- for_each_pmc_rcu(np, mc_lst) {
+ for_each_pmc_rtnl(np, mc_lst) {
if ((ifindex == 0 || mc_lst->ifindex == ifindex) &&
if ((ifindex == 0 || mc_lst->ifindex == ifindex) &&
- ipv6_addr_equal(&mc_lst->addr, addr)) {
- rcu_read_unlock();
+ ipv6_addr_equal(&mc_lst->addr, addr))
mc_lst = sock_kmalloc(sk, sizeof(struct ipv6_mc_socklist), GFP_KERNEL);
mc_lst = sock_kmalloc(sk, sizeof(struct ipv6_mc_socklist), GFP_KERNEL);
}
EXPORT_SYMBOL(ipv6_sock_mc_drop);
}
EXPORT_SYMBOL(ipv6_sock_mc_drop);
-/* called with rcu_read_lock() */
-static struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net,
- const struct in6_addr *group,
- int ifindex)
+static struct inet6_dev *ip6_mc_find_dev_rtnl(struct net *net,
+ const struct in6_addr *group,
+ int ifindex)
{
struct net_device *dev = NULL;
struct inet6_dev *idev = NULL;
{
struct net_device *dev = NULL;
struct inet6_dev *idev = NULL;
dev = rt->dst.dev;
ip6_rt_put(rt);
}
dev = rt->dst.dev;
ip6_rt_put(rt);
}
- } else
- dev = dev_get_by_index_rcu(net, ifindex);
+ } else {
+ dev = __dev_get_by_index(net, ifindex);
+ }
if (!dev)
return NULL;
idev = __in6_dev_get(dev);
if (!idev)
return NULL;
if (!dev)
return NULL;
idev = __in6_dev_get(dev);
if (!idev)
return NULL;
- read_lock_bh(&idev->lock);
- if (idev->dead) {
- read_unlock_bh(&idev->lock);
if (!ipv6_addr_is_multicast(group))
return -EINVAL;
if (!ipv6_addr_is_multicast(group))
return -EINVAL;
- rcu_read_lock();
- idev = ip6_mc_find_dev_rcu(net, group, pgsr->gsr_interface);
- if (!idev) {
- rcu_read_unlock();
+ idev = ip6_mc_find_dev_rtnl(net, group, pgsr->gsr_interface);
+ if (!idev)
- for_each_pmc_rcu(inet6, pmc) {
+ for_each_pmc_rtnl(inet6, pmc) {
if (pgsr->gsr_interface && pmc->ifindex != pgsr->gsr_interface)
continue;
if (ipv6_addr_equal(&pmc->addr, group))
if (pgsr->gsr_interface && pmc->ifindex != pgsr->gsr_interface)
continue;
if (ipv6_addr_equal(&pmc->addr, group))
/* update the interface list */
ip6_mc_add_src(idev, group, omode, 1, source, 1);
done:
/* update the interface list */
ip6_mc_add_src(idev, group, omode, 1, source, 1);
done:
- read_unlock_bh(&idev->lock);
- rcu_read_unlock();
if (leavegroup)
err = ipv6_sock_mc_drop(sk, pgsr->gsr_interface, group);
return err;
if (leavegroup)
err = ipv6_sock_mc_drop(sk, pgsr->gsr_interface, group);
return err;
gsf->gf_fmode != MCAST_EXCLUDE)
return -EINVAL;
gsf->gf_fmode != MCAST_EXCLUDE)
return -EINVAL;
- rcu_read_lock();
- idev = ip6_mc_find_dev_rcu(net, group, gsf->gf_interface);
-
- if (!idev) {
- rcu_read_unlock();
+ idev = ip6_mc_find_dev_rtnl(net, group, gsf->gf_interface);
+ if (!idev)
- for_each_pmc_rcu(inet6, pmc) {
+ for_each_pmc_rtnl(inet6, pmc) {
if (pmc->ifindex != gsf->gf_interface)
continue;
if (ipv6_addr_equal(&pmc->addr, group))
if (pmc->ifindex != gsf->gf_interface)
continue;
if (ipv6_addr_equal(&pmc->addr, group))
pmc->sfmode = gsf->gf_fmode;
err = 0;
done:
pmc->sfmode = gsf->gf_fmode;
err = 0;
done:
- read_unlock_bh(&idev->lock);
- rcu_read_unlock();
if (leavegroup)
err = ipv6_sock_mc_drop(sk, gsf->gf_interface, group);
return err;
if (leavegroup)
err = ipv6_sock_mc_drop(sk, gsf->gf_interface, group);
return err;
if (!ipv6_addr_is_multicast(group))
return -EINVAL;
if (!ipv6_addr_is_multicast(group))
return -EINVAL;
- rcu_read_lock();
- idev = ip6_mc_find_dev_rcu(net, group, gsf->gf_interface);
-
- if (!idev) {
- rcu_read_unlock();
+ idev = ip6_mc_find_dev_rtnl(net, group, gsf->gf_interface);
+ if (!idev)
err = -EADDRNOTAVAIL;
/* changes to the ipv6_mc_list require the socket lock and
err = -EADDRNOTAVAIL;
/* changes to the ipv6_mc_list require the socket lock and
* so reading the list is safe.
*/
* so reading the list is safe.
*/
- for_each_pmc_rcu(inet6, pmc) {
+ for_each_pmc_rtnl(inet6, pmc) {
if (pmc->ifindex != gsf->gf_interface)
continue;
if (ipv6_addr_equal(group, &pmc->addr))
break;
}
if (!pmc) /* must have a prior join */
if (pmc->ifindex != gsf->gf_interface)
continue;
if (ipv6_addr_equal(group, &pmc->addr))
break;
}
if (!pmc) /* must have a prior join */
gsf->gf_fmode = pmc->sfmode;
psl = rtnl_dereference(pmc->sflist);
count = psl ? psl->sl_count : 0;
gsf->gf_fmode = pmc->sfmode;
psl = rtnl_dereference(pmc->sflist);
count = psl ? psl->sl_count : 0;
- read_unlock_bh(&idev->lock);
- rcu_read_unlock();
copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
gsf->gf_numsrc = count;
copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
gsf->gf_numsrc = count;
return -EFAULT;
}
return 0;
return -EFAULT;
}
return 0;
-done:
- read_unlock_bh(&idev->lock);
- rcu_read_unlock();
- return err;
}
bool inet6_mc_check(struct sock *sk, const struct in6_addr *mc_addr,
}
bool inet6_mc_check(struct sock *sk, const struct in6_addr *mc_addr,
}
spin_unlock_bh(&im->mca_lock);
}
spin_unlock_bh(&im->mca_lock);
- pmc->next = idev->mc_tomb;
- idev->mc_tomb = pmc;
+ rcu_assign_pointer(pmc->next, idev->mc_tomb);
+ rcu_assign_pointer(idev->mc_tomb, pmc);
}
static void mld_del_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im)
}
static void mld_del_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im)
struct ifmcaddr6 *pmc, *pmc_prev;
pmc_prev = NULL;
struct ifmcaddr6 *pmc, *pmc_prev;
pmc_prev = NULL;
- for (pmc = idev->mc_tomb; pmc; pmc = pmc->next) {
+ for_each_mc_tomb(idev, pmc) {
if (ipv6_addr_equal(&pmc->mca_addr, pmca))
break;
pmc_prev = pmc;
}
if (pmc) {
if (pmc_prev)
if (ipv6_addr_equal(&pmc->mca_addr, pmca))
break;
pmc_prev = pmc;
}
if (pmc) {
if (pmc_prev)
- pmc_prev->next = pmc->next;
+ rcu_assign_pointer(pmc_prev->next, pmc->next);
- idev->mc_tomb = pmc->next;
+ rcu_assign_pointer(idev->mc_tomb, pmc->next);
}
spin_lock_bh(&im->mca_lock);
}
spin_lock_bh(&im->mca_lock);
}
in6_dev_put(pmc->idev);
ip6_mc_clear_src(pmc);
}
in6_dev_put(pmc->idev);
ip6_mc_clear_src(pmc);
}
spin_unlock_bh(&im->mca_lock);
}
}
spin_unlock_bh(&im->mca_lock);
}
{
struct ifmcaddr6 *pmc, *nextpmc;
{
struct ifmcaddr6 *pmc, *nextpmc;
- pmc = idev->mc_tomb;
- idev->mc_tomb = NULL;
+ pmc = rtnl_dereference(idev->mc_tomb);
+ RCU_INIT_POINTER(idev->mc_tomb, NULL);
for (; pmc; pmc = nextpmc) {
for (; pmc; pmc = nextpmc) {
+ nextpmc = rtnl_dereference(pmc->next);
ip6_mc_clear_src(pmc);
in6_dev_put(pmc->idev);
ip6_mc_clear_src(pmc);
in6_dev_put(pmc->idev);
}
/* clear dead sources, too */
}
/* clear dead sources, too */
- read_lock_bh(&idev->lock);
- for (pmc = idev->mc_list; pmc; pmc = pmc->next) {
+ for_each_mc_rtnl(idev, pmc) {
struct ip6_sf_list *psf, *psf_next;
spin_lock_bh(&pmc->mca_lock);
struct ip6_sf_list *psf, *psf_next;
spin_lock_bh(&pmc->mca_lock);
- read_unlock_bh(&idev->lock);
}
static void mca_get(struct ifmcaddr6 *mc)
}
static void mca_get(struct ifmcaddr6 *mc)
{
if (refcount_dec_and_test(&mc->mca_refcnt)) {
in6_dev_put(mc->idev);
{
if (refcount_dec_and_test(&mc->mca_refcnt)) {
in6_dev_put(mc->idev);
if (!idev)
return -EINVAL;
if (!idev)
return -EINVAL;
- write_lock_bh(&idev->lock);
- write_unlock_bh(&idev->lock);
in6_dev_put(idev);
return -ENODEV;
}
in6_dev_put(idev);
return -ENODEV;
}
- for (mc = idev->mc_list; mc; mc = mc->next) {
+ for_each_mc_rtnl(idev, mc) {
if (ipv6_addr_equal(&mc->mca_addr, addr)) {
mc->mca_users++;
if (ipv6_addr_equal(&mc->mca_addr, addr)) {
mc->mca_users++;
- write_unlock_bh(&idev->lock);
ip6_mc_add_src(idev, &mc->mca_addr, mode, 0, NULL, 0);
in6_dev_put(idev);
return 0;
ip6_mc_add_src(idev, &mc->mca_addr, mode, 0, NULL, 0);
in6_dev_put(idev);
return 0;
mc = mca_alloc(idev, addr, mode);
if (!mc) {
mc = mca_alloc(idev, addr, mode);
if (!mc) {
- write_unlock_bh(&idev->lock);
in6_dev_put(idev);
return -ENOMEM;
}
in6_dev_put(idev);
return -ENOMEM;
}
- mc->next = idev->mc_list;
- idev->mc_list = mc;
+ rcu_assign_pointer(mc->next, idev->mc_list);
+ rcu_assign_pointer(idev->mc_list, mc);
- /* Hold this for the code below before we unlock,
- * it is already exposed via idev->mc_list.
- */
- write_unlock_bh(&idev->lock);
mld_del_delrec(idev, mc);
igmp6_group_added(mc);
mld_del_delrec(idev, mc);
igmp6_group_added(mc);
*/
int __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr)
{
*/
int __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr)
{
- struct ifmcaddr6 *ma, **map;
+ struct ifmcaddr6 *ma, __rcu **map;
- write_lock_bh(&idev->lock);
- for (map = &idev->mc_list; (ma = *map) != NULL; map = &ma->next) {
+ for (map = &idev->mc_list;
+ (ma = rtnl_dereference(*map));
+ map = &ma->next) {
if (ipv6_addr_equal(&ma->mca_addr, addr)) {
if (--ma->mca_users == 0) {
*map = ma->next;
if (ipv6_addr_equal(&ma->mca_addr, addr)) {
if (--ma->mca_users == 0) {
*map = ma->next;
- write_unlock_bh(&idev->lock);
igmp6_group_dropped(ma);
ip6_mc_clear_src(ma);
igmp6_group_dropped(ma);
ip6_mc_clear_src(ma);
- write_unlock_bh(&idev->lock);
- write_unlock_bh(&idev->lock);
rcu_read_lock();
idev = __in6_dev_get(dev);
if (idev) {
rcu_read_lock();
idev = __in6_dev_get(dev);
if (idev) {
- read_lock_bh(&idev->lock);
- for (mc = idev->mc_list; mc; mc = mc->next) {
+ for_each_mc_rcu(idev, mc) {
if (ipv6_addr_equal(&mc->mca_addr, group))
break;
}
if (ipv6_addr_equal(&mc->mca_addr, group))
break;
}
} else
rv = true; /* don't filter unspecified source */
}
} else
rv = true; /* don't filter unspecified source */
}
- read_unlock_bh(&idev->lock);
}
rcu_read_unlock();
return rv;
}
rcu_read_unlock();
return rv;
- * IGMP handling (alias multicast ICMPv6 messages)
+ * IGMP handling (alias multicast ICMPv6 messages)
static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime)
{
unsigned long delay = resptime;
static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime)
{
unsigned long delay = resptime;
- read_lock_bh(&idev->lock);
if (group_type == IPV6_ADDR_ANY) {
if (group_type == IPV6_ADDR_ANY) {
- for (ma = idev->mc_list; ma; ma = ma->next) {
+ for_each_mc_rcu(idev, ma) {
spin_lock_bh(&ma->mca_lock);
igmp6_group_queried(ma, max_delay);
spin_unlock_bh(&ma->mca_lock);
}
} else {
spin_lock_bh(&ma->mca_lock);
igmp6_group_queried(ma, max_delay);
spin_unlock_bh(&ma->mca_lock);
}
} else {
- for (ma = idev->mc_list; ma; ma = ma->next) {
+ for_each_mc_rcu(idev, ma) {
if (!ipv6_addr_equal(group, &ma->mca_addr))
continue;
spin_lock_bh(&ma->mca_lock);
if (!ipv6_addr_equal(group, &ma->mca_addr))
continue;
spin_lock_bh(&ma->mca_lock);
- read_unlock_bh(&idev->lock);
* Cancel the work for this group
*/
* Cancel the work for this group
*/
- read_lock_bh(&idev->lock);
- for (ma = idev->mc_list; ma; ma = ma->next) {
+ for_each_mc_rcu(idev, ma) {
if (ipv6_addr_equal(&ma->mca_addr, &mld->mld_mca)) {
spin_lock(&ma->mca_lock);
if (cancel_delayed_work(&ma->mca_work))
refcount_dec(&ma->mca_refcnt);
if (ipv6_addr_equal(&ma->mca_addr, &mld->mld_mca)) {
spin_lock(&ma->mca_lock);
if (cancel_delayed_work(&ma->mca_work))
refcount_dec(&ma->mca_refcnt);
- ma->mca_flags &= ~(MAF_LAST_REPORTER|MAF_TIMER_RUNNING);
+ ma->mca_flags &= ~(MAF_LAST_REPORTER |
+ MAF_TIMER_RUNNING);
spin_unlock(&ma->mca_lock);
break;
}
}
spin_unlock(&ma->mca_lock);
break;
}
}
- read_unlock_bh(&idev->lock);
struct sk_buff *skb = NULL;
int type;
struct sk_buff *skb = NULL;
int type;
- read_lock_bh(&idev->lock);
- for (pmc = idev->mc_list; pmc; pmc = pmc->next) {
+ for_each_mc_rtnl(idev, pmc) {
if (pmc->mca_flags & MAF_NOREPORT)
continue;
spin_lock_bh(&pmc->mca_lock);
if (pmc->mca_flags & MAF_NOREPORT)
continue;
spin_lock_bh(&pmc->mca_lock);
skb = add_grec(skb, pmc, type, 0, 0, 0);
spin_unlock_bh(&pmc->mca_lock);
}
skb = add_grec(skb, pmc, type, 0, 0, 0);
spin_unlock_bh(&pmc->mca_lock);
}
- read_unlock_bh(&idev->lock);
if (skb)
mld_sendpack(skb);
}
if (skb)
mld_sendpack(skb);
}
struct sk_buff *skb = NULL;
int type, dtype;
struct sk_buff *skb = NULL;
int type, dtype;
- read_lock_bh(&idev->lock);
-
/* deleted MCA's */
pmc_prev = NULL;
/* deleted MCA's */
pmc_prev = NULL;
- for (pmc = idev->mc_tomb; pmc; pmc = pmc_next) {
- pmc_next = pmc->next;
+ for (pmc = rtnl_dereference(idev->mc_tomb);
+ pmc;
+ pmc = pmc_next) {
+ pmc_next = rtnl_dereference(pmc->next);
if (pmc->mca_sfmode == MCAST_INCLUDE) {
type = MLD2_BLOCK_OLD_SOURCES;
dtype = MLD2_BLOCK_OLD_SOURCES;
if (pmc->mca_sfmode == MCAST_INCLUDE) {
type = MLD2_BLOCK_OLD_SOURCES;
dtype = MLD2_BLOCK_OLD_SOURCES;
!rcu_access_pointer(pmc->mca_tomb) &&
!rcu_access_pointer(pmc->mca_sources)) {
if (pmc_prev)
!rcu_access_pointer(pmc->mca_tomb) &&
!rcu_access_pointer(pmc->mca_sources)) {
if (pmc_prev)
- pmc_prev->next = pmc_next;
+ rcu_assign_pointer(pmc_prev->next, pmc_next);
- idev->mc_tomb = pmc_next;
+ rcu_assign_pointer(idev->mc_tomb, pmc_next);
} else
pmc_prev = pmc;
}
/* change recs */
} else
pmc_prev = pmc;
}
/* change recs */
- for (pmc = idev->mc_list; pmc; pmc = pmc->next) {
+ for_each_mc_rtnl(idev, pmc) {
spin_lock_bh(&pmc->mca_lock);
if (pmc->mca_sfcount[MCAST_EXCLUDE]) {
type = MLD2_BLOCK_OLD_SOURCES;
spin_lock_bh(&pmc->mca_lock);
if (pmc->mca_sfcount[MCAST_EXCLUDE]) {
type = MLD2_BLOCK_OLD_SOURCES;
}
spin_unlock_bh(&pmc->mca_lock);
}
}
spin_unlock_bh(&pmc->mca_lock);
}
- read_unlock_bh(&idev->lock);
if (!skb)
return;
(void) mld_sendpack(skb);
if (!skb)
return;
(void) mld_sendpack(skb);
- read_lock_bh(&idev->lock);
- for (pmc = idev->mc_list; pmc; pmc = pmc->next) {
+ for_each_mc_rtnl(idev, pmc) {
spin_lock_bh(&pmc->mca_lock);
if (pmc->mca_sfcount[MCAST_EXCLUDE])
type = MLD2_CHANGE_TO_EXCLUDE;
spin_lock_bh(&pmc->mca_lock);
if (pmc->mca_sfcount[MCAST_EXCLUDE])
type = MLD2_CHANGE_TO_EXCLUDE;
skb = add_grec(skb, pmc, type, 0, 0, 1);
spin_unlock_bh(&pmc->mca_lock);
}
skb = add_grec(skb, pmc, type, 0, 0, 1);
spin_unlock_bh(&pmc->mca_lock);
}
- read_unlock_bh(&idev->lock);
if (skb)
mld_sendpack(skb);
}
if (skb)
mld_sendpack(skb);
}
struct inet6_dev,
mc_dad_work);
struct inet6_dev,
mc_dad_work);
mld_send_initial_cr(idev);
mld_send_initial_cr(idev);
if (idev->mc_dad_count) {
idev->mc_dad_count--;
if (idev->mc_dad_count)
if (idev->mc_dad_count) {
idev->mc_dad_count--;
if (idev->mc_dad_count)
if (!idev)
return -ENODEV;
if (!idev)
return -ENODEV;
- read_lock_bh(&idev->lock);
- for (pmc = idev->mc_list; pmc; pmc = pmc->next) {
+
+ for_each_mc_rtnl(idev, pmc) {
if (ipv6_addr_equal(pmca, &pmc->mca_addr))
break;
}
if (ipv6_addr_equal(pmca, &pmc->mca_addr))
break;
}
- if (!pmc) {
- /* MCA not found?? bug */
- read_unlock_bh(&idev->lock);
spin_lock_bh(&pmc->mca_lock);
spin_lock_bh(&pmc->mca_lock);
sf_markstate(pmc);
if (!delta) {
if (!pmc->mca_sfcount[sfmode]) {
spin_unlock_bh(&pmc->mca_lock);
sf_markstate(pmc);
if (!delta) {
if (!pmc->mca_sfcount[sfmode]) {
spin_unlock_bh(&pmc->mca_lock);
- read_unlock_bh(&idev->lock);
pmc->mca_sfcount[sfmode]--;
}
err = 0;
pmc->mca_sfcount[sfmode]--;
}
err = 0;
} else if (sf_setstate(pmc) || changerec)
mld_ifc_event(pmc->idev);
spin_unlock_bh(&pmc->mca_lock);
} else if (sf_setstate(pmc) || changerec)
mld_ifc_event(pmc->idev);
spin_unlock_bh(&pmc->mca_lock);
- read_unlock_bh(&idev->lock);
if (!idev)
return -ENODEV;
if (!idev)
return -ENODEV;
- read_lock_bh(&idev->lock);
- for (pmc = idev->mc_list; pmc; pmc = pmc->next) {
+
+ for_each_mc_rtnl(idev, pmc) {
if (ipv6_addr_equal(pmca, &pmc->mca_addr))
break;
}
if (ipv6_addr_equal(pmca, &pmc->mca_addr))
break;
}
- if (!pmc) {
- /* MCA not found?? bug */
- read_unlock_bh(&idev->lock);
spin_lock_bh(&pmc->mca_lock);
sf_markstate(pmc);
spin_lock_bh(&pmc->mca_lock);
sf_markstate(pmc);
for_each_psf_rtnl(pmc, psf)
psf->sf_crcount = 0;
mld_ifc_event(idev);
for_each_psf_rtnl(pmc, psf)
psf->sf_crcount = 0;
mld_ifc_event(idev);
- } else if (sf_setstate(pmc))
+ } else if (sf_setstate(pmc)) {
spin_unlock_bh(&pmc->mca_lock);
spin_unlock_bh(&pmc->mca_lock);
- read_unlock_bh(&idev->lock);
static void igmp6_leave_group(struct ifmcaddr6 *ma)
{
if (mld_in_v1_mode(ma->idev)) {
static void igmp6_leave_group(struct ifmcaddr6 *ma)
{
if (mld_in_v1_mode(ma->idev)) {
- if (ma->mca_flags & MAF_LAST_REPORTER)
+ if (ma->mca_flags & MAF_LAST_REPORTER) {
igmp6_send(&ma->mca_addr, ma->idev->dev,
ICMPV6_MGM_REDUCTION);
igmp6_send(&ma->mca_addr, ma->idev->dev,
ICMPV6_MGM_REDUCTION);
} else {
mld_add_delrec(ma->idev, ma);
mld_ifc_event(ma->idev);
} else {
mld_add_delrec(ma->idev, ma);
mld_ifc_event(ma->idev);
struct inet6_dev,
mc_gq_work);
struct inet6_dev,
mc_gq_work);
- idev->mc_gq_running = 0;
mld_send_report(idev, NULL);
mld_send_report(idev, NULL);
+ rtnl_unlock();
+
+ idev->mc_gq_running = 0;
+
struct inet6_dev,
mc_ifc_work);
struct inet6_dev,
mc_ifc_work);
if (idev->mc_ifc_count) {
idev->mc_ifc_count--;
if (idev->mc_ifc_count)
if (idev->mc_ifc_count) {
idev->mc_ifc_count--;
if (idev->mc_ifc_count)
{
if (mld_in_v1_mode(idev))
return;
{
if (mld_in_v1_mode(idev))
return;
idev->mc_ifc_count = idev->mc_qrv;
mld_ifc_start_work(idev, 1);
}
idev->mc_ifc_count = idev->mc_qrv;
mld_ifc_start_work(idev, 1);
}
struct ifmcaddr6 *ma = container_of(to_delayed_work(work),
struct ifmcaddr6, mca_work);
struct ifmcaddr6 *ma = container_of(to_delayed_work(work),
struct ifmcaddr6, mca_work);
if (mld_in_v1_mode(ma->idev))
igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT);
else
mld_send_report(ma->idev, ma);
if (mld_in_v1_mode(ma->idev))
igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT);
else
mld_send_report(ma->idev, ma);
spin_lock_bh(&ma->mca_lock);
ma->mca_flags |= MAF_LAST_REPORTER;
spin_lock_bh(&ma->mca_lock);
ma->mca_flags |= MAF_LAST_REPORTER;
/* Install multicast list, except for all-nodes (already installed) */
/* Install multicast list, except for all-nodes (already installed) */
- read_lock_bh(&idev->lock);
- for (i = idev->mc_list; i; i = i->next)
+ for_each_mc_rtnl(idev, i)
- read_unlock_bh(&idev->lock);
}
void ipv6_mc_remap(struct inet6_dev *idev)
}
void ipv6_mc_remap(struct inet6_dev *idev)
struct ifmcaddr6 *i;
/* Withdraw multicast list */
struct ifmcaddr6 *i;
/* Withdraw multicast list */
-
- read_lock_bh(&idev->lock);
-
- for (i = idev->mc_list; i; i = i->next)
+ for_each_mc_rtnl(idev, i)
igmp6_group_dropped(i);
/* Should stop work after group drop. or we will
igmp6_group_dropped(i);
/* Should stop work after group drop. or we will
mld_ifc_stop_work(idev);
mld_gq_stop_work(idev);
mld_dad_stop_work(idev);
mld_ifc_stop_work(idev);
mld_gq_stop_work(idev);
mld_dad_stop_work(idev);
- read_unlock_bh(&idev->lock);
}
static void ipv6_mc_reset(struct inet6_dev *idev)
}
static void ipv6_mc_reset(struct inet6_dev *idev)
/* Install multicast list, except for all-nodes (already installed) */
/* Install multicast list, except for all-nodes (already installed) */
- read_lock_bh(&idev->lock);
- for (i = idev->mc_list; i; i = i->next) {
+ for_each_mc_rtnl(idev, i) {
mld_del_delrec(idev, i);
igmp6_group_added(i);
}
mld_del_delrec(idev, i);
igmp6_group_added(i);
}
- read_unlock_bh(&idev->lock);
}
/* IPv6 device initialization. */
void ipv6_mc_init_dev(struct inet6_dev *idev)
{
}
/* IPv6 device initialization. */
void ipv6_mc_init_dev(struct inet6_dev *idev)
{
- write_lock_bh(&idev->lock);
idev->mc_gq_running = 0;
INIT_DELAYED_WORK(&idev->mc_gq_work, mld_gq_work);
idev->mc_gq_running = 0;
INIT_DELAYED_WORK(&idev->mc_gq_work, mld_gq_work);
+ RCU_INIT_POINTER(idev->mc_tomb, NULL);
idev->mc_ifc_count = 0;
INIT_DELAYED_WORK(&idev->mc_ifc_work, mld_ifc_work);
INIT_DELAYED_WORK(&idev->mc_dad_work, mld_dad_work);
ipv6_mc_reset(idev);
idev->mc_ifc_count = 0;
INIT_DELAYED_WORK(&idev->mc_ifc_work, mld_ifc_work);
INIT_DELAYED_WORK(&idev->mc_dad_work, mld_dad_work);
ipv6_mc_reset(idev);
- write_unlock_bh(&idev->lock);
if (idev->cnf.forwarding)
__ipv6_dev_mc_dec(idev, &in6addr_linklocal_allrouters);
if (idev->cnf.forwarding)
__ipv6_dev_mc_dec(idev, &in6addr_linklocal_allrouters);
- write_lock_bh(&idev->lock);
- while ((i = idev->mc_list) != NULL) {
- idev->mc_list = i->next;
+ while ((i = rtnl_dereference(idev->mc_list))) {
+ rcu_assign_pointer(idev->mc_list, rtnl_dereference(i->next));
- write_unlock_bh(&idev->lock);
ip6_mc_clear_src(i);
ma_put(i);
ip6_mc_clear_src(i);
ma_put(i);
- write_lock_bh(&idev->lock);
- write_unlock_bh(&idev->lock);
}
static void ipv6_mc_rejoin_groups(struct inet6_dev *idev)
}
static void ipv6_mc_rejoin_groups(struct inet6_dev *idev)
ASSERT_RTNL();
if (mld_in_v1_mode(idev)) {
ASSERT_RTNL();
if (mld_in_v1_mode(idev)) {
- read_lock_bh(&idev->lock);
- for (pmc = idev->mc_list; pmc; pmc = pmc->next)
+ for_each_mc_rtnl(idev, pmc)
- read_unlock_bh(&idev->lock);
- } else
mld_send_report(idev, NULL);
mld_send_report(idev, NULL);
}
static int ipv6_mc_netdev_event(struct notifier_block *this,
}
static int ipv6_mc_netdev_event(struct notifier_block *this,
idev = __in6_dev_get(state->dev);
if (!idev)
continue;
idev = __in6_dev_get(state->dev);
if (!idev)
continue;
- read_lock_bh(&idev->lock);
- im = idev->mc_list;
+
+ im = rcu_dereference(idev->mc_list);
if (im) {
state->idev = idev;
break;
}
if (im) {
state->idev = idev;
break;
}
- read_unlock_bh(&idev->lock);
{
struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
{
struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
+ im = rcu_dereference(im->next);
- if (likely(state->idev))
- read_unlock_bh(&state->idev->lock);
-
state->dev = next_net_device_rcu(state->dev);
if (!state->dev) {
state->idev = NULL;
state->dev = next_net_device_rcu(state->dev);
if (!state->dev) {
state->idev = NULL;
state->idev = __in6_dev_get(state->dev);
if (!state->idev)
continue;
state->idev = __in6_dev_get(state->dev);
if (!state->idev)
continue;
- read_lock_bh(&state->idev->lock);
- im = state->idev->mc_list;
+ im = rcu_dereference(state->idev->mc_list);
{
struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
{
struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
- if (likely(state->idev)) {
- read_unlock_bh(&state->idev->lock);
+ if (likely(state->idev))
state->dev = NULL;
rcu_read_unlock();
}
state->dev = NULL;
rcu_read_unlock();
}
state->dev->ifindex, state->dev->name,
&im->mca_addr,
im->mca_users, im->mca_flags,
state->dev->ifindex, state->dev->name,
&im->mca_addr,
im->mca_users, im->mca_flags,
- (im->mca_flags&MAF_TIMER_RUNNING) ?
+ (im->mca_flags & MAF_TIMER_RUNNING) ?
jiffies_to_clock_t(im->mca_work.timer.expires - jiffies) : 0);
return 0;
}
jiffies_to_clock_t(im->mca_work.timer.expires - jiffies) : 0);
return 0;
}
idev = __in6_dev_get(state->dev);
if (unlikely(idev == NULL))
continue;
idev = __in6_dev_get(state->dev);
if (unlikely(idev == NULL))
continue;
- read_lock_bh(&idev->lock);
- im = idev->mc_list;
+
+ im = rcu_dereference(idev->mc_list);
if (likely(im)) {
spin_lock_bh(&im->mca_lock);
psf = rcu_dereference(im->mca_sources);
if (likely(im)) {
spin_lock_bh(&im->mca_lock);
psf = rcu_dereference(im->mca_sources);
}
spin_unlock_bh(&im->mca_lock);
}
}
spin_unlock_bh(&im->mca_lock);
}
- read_unlock_bh(&idev->lock);
psf = rcu_dereference(psf->sf_next);
while (!psf) {
spin_unlock_bh(&state->im->mca_lock);
psf = rcu_dereference(psf->sf_next);
while (!psf) {
spin_unlock_bh(&state->im->mca_lock);
- state->im = state->im->next;
+ state->im = rcu_dereference(state->im->next);
- if (likely(state->idev))
- read_unlock_bh(&state->idev->lock);
-
state->dev = next_net_device_rcu(state->dev);
if (!state->dev) {
state->idev = NULL;
state->dev = next_net_device_rcu(state->dev);
if (!state->dev) {
state->idev = NULL;
state->idev = __in6_dev_get(state->dev);
if (!state->idev)
continue;
state->idev = __in6_dev_get(state->dev);
if (!state->idev)
continue;
- read_lock_bh(&state->idev->lock);
- state->im = state->idev->mc_list;
+ state->im = rcu_dereference(state->idev->mc_list);
__releases(RCU)
{
struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq);
__releases(RCU)
{
struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq);
if (likely(state->im)) {
spin_unlock_bh(&state->im->mca_lock);
state->im = NULL;
}
if (likely(state->im)) {
spin_unlock_bh(&state->im->mca_lock);
state->im = NULL;
}
- if (likely(state->idev)) {
- read_unlock_bh(&state->idev->lock);
+ if (likely(state->idev))
state->dev = NULL;
rcu_read_unlock();
}
state->dev = NULL;
rcu_read_unlock();
}