Commit 96b52e61 authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

ipv6: mcast: RCU conversions

- ipv6_sock_mc_join() : doesnt touch dev refcount

- ipv6_sock_mc_drop() : doesnt touch dev/idev refcounts

- ip6_mc_find_dev() becomes ip6_mc_find_dev_rcu() (called from rcu),
                    and doesnt touch dev/idev refcounts

- ipv6_sock_mc_close() : doesnt touch dev/idev refcounts

- ip6_mc_source() uses ip6_mc_find_dev_rcu()

- ip6_mc_msfilter() uses ip6_mc_find_dev_rcu()

- ip6_mc_msfget() uses ip6_mc_find_dev_rcu()

- ipv6_dev_mc_dec(), ipv6_chk_mcast_addr(),
  igmp6_event_query(), igmp6_event_report(),
  mld_sendpack(), igmp6_send() dont touch idev refcount
Signed-off-by: default avatarEric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 97859160
...@@ -152,18 +152,19 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) ...@@ -152,18 +152,19 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
mc_lst->next = NULL; mc_lst->next = NULL;
ipv6_addr_copy(&mc_lst->addr, addr); ipv6_addr_copy(&mc_lst->addr, addr);
rcu_read_lock();
if (ifindex == 0) { if (ifindex == 0) {
struct rt6_info *rt; struct rt6_info *rt;
rt = rt6_lookup(net, addr, NULL, 0, 0); rt = rt6_lookup(net, addr, NULL, 0, 0);
if (rt) { if (rt) {
dev = rt->rt6i_dev; dev = rt->rt6i_dev;
dev_hold(dev);
dst_release(&rt->u.dst); dst_release(&rt->u.dst);
} }
} else } else
dev = dev_get_by_index(net, ifindex); dev = dev_get_by_index_rcu(net, ifindex);
if (dev == NULL) { if (dev == NULL) {
rcu_read_unlock();
sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
return -ENODEV; return -ENODEV;
} }
...@@ -180,8 +181,8 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) ...@@ -180,8 +181,8 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
err = ipv6_dev_mc_inc(dev, addr); err = ipv6_dev_mc_inc(dev, addr);
if (err) { if (err) {
rcu_read_unlock();
sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
dev_put(dev);
return err; return err;
} }
...@@ -190,7 +191,7 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) ...@@ -190,7 +191,7 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
np->ipv6_mc_list = mc_lst; np->ipv6_mc_list = mc_lst;
write_unlock_bh(&ipv6_sk_mc_lock); write_unlock_bh(&ipv6_sk_mc_lock);
dev_put(dev); rcu_read_unlock();
return 0; return 0;
} }
...@@ -213,18 +214,17 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) ...@@ -213,18 +214,17 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
*lnk = mc_lst->next; *lnk = mc_lst->next;
write_unlock_bh(&ipv6_sk_mc_lock); write_unlock_bh(&ipv6_sk_mc_lock);
dev = dev_get_by_index(net, mc_lst->ifindex); rcu_read_lock();
dev = dev_get_by_index_rcu(net, mc_lst->ifindex);
if (dev != NULL) { if (dev != NULL) {
struct inet6_dev *idev = in6_dev_get(dev); struct inet6_dev *idev = __in6_dev_get(dev);
(void) ip6_mc_leave_src(sk, mc_lst, idev); (void) ip6_mc_leave_src(sk, mc_lst, idev);
if (idev) { if (idev)
__ipv6_dev_mc_dec(idev, &mc_lst->addr); __ipv6_dev_mc_dec(idev, &mc_lst->addr);
in6_dev_put(idev);
}
dev_put(dev);
} else } else
(void) ip6_mc_leave_src(sk, mc_lst, NULL); (void) ip6_mc_leave_src(sk, mc_lst, NULL);
rcu_read_unlock();
sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
return 0; return 0;
} }
...@@ -234,43 +234,36 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) ...@@ -234,43 +234,36 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
return -EADDRNOTAVAIL; return -EADDRNOTAVAIL;
} }
static struct inet6_dev *ip6_mc_find_dev(struct net *net, /* called with rcu_read_lock() */
struct in6_addr *group, static struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net,
int ifindex) struct in6_addr *group,
int ifindex)
{ {
struct net_device *dev = NULL; struct net_device *dev = NULL;
struct inet6_dev *idev = NULL; struct inet6_dev *idev = NULL;
if (ifindex == 0) { if (ifindex == 0) {
struct rt6_info *rt; struct rt6_info *rt = rt6_lookup(net, group, NULL, 0, 0);
rt = rt6_lookup(net, group, NULL, 0, 0);
if (rt) { if (rt) {
dev = rt->rt6i_dev; dev = rt->rt6i_dev;
dev_hold(dev); dev_hold(dev);
dst_release(&rt->u.dst); dst_release(&rt->u.dst);
} }
} else } else
dev = dev_get_by_index(net, ifindex); dev = dev_get_by_index_rcu(net, ifindex);
if (!dev) if (!dev)
goto nodev; return NULL;
idev = in6_dev_get(dev); idev = __in6_dev_get(dev);
if (!idev) if (!idev)
goto release; return NULL;;
read_lock_bh(&idev->lock); read_lock_bh(&idev->lock);
if (idev->dead) if (idev->dead) {
goto unlock_release; read_unlock_bh(&idev->lock);
return NULL;
}
return idev; return idev;
unlock_release:
read_unlock_bh(&idev->lock);
in6_dev_put(idev);
release:
dev_put(dev);
nodev:
return NULL;
} }
void ipv6_sock_mc_close(struct sock *sk) void ipv6_sock_mc_close(struct sock *sk)
...@@ -286,19 +279,17 @@ void ipv6_sock_mc_close(struct sock *sk) ...@@ -286,19 +279,17 @@ void ipv6_sock_mc_close(struct sock *sk)
np->ipv6_mc_list = mc_lst->next; np->ipv6_mc_list = mc_lst->next;
write_unlock_bh(&ipv6_sk_mc_lock); write_unlock_bh(&ipv6_sk_mc_lock);
dev = dev_get_by_index(net, mc_lst->ifindex); rcu_read_lock();
dev = dev_get_by_index_rcu(net, mc_lst->ifindex);
if (dev) { if (dev) {
struct inet6_dev *idev = in6_dev_get(dev); struct inet6_dev *idev = __in6_dev_get(dev);
(void) ip6_mc_leave_src(sk, mc_lst, idev); (void) ip6_mc_leave_src(sk, mc_lst, idev);
if (idev) { if (idev)
__ipv6_dev_mc_dec(idev, &mc_lst->addr); __ipv6_dev_mc_dec(idev, &mc_lst->addr);
in6_dev_put(idev);
}
dev_put(dev);
} else } else
(void) ip6_mc_leave_src(sk, mc_lst, NULL); (void) ip6_mc_leave_src(sk, mc_lst, NULL);
rcu_read_unlock();
sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
write_lock_bh(&ipv6_sk_mc_lock); write_lock_bh(&ipv6_sk_mc_lock);
...@@ -327,14 +318,17 @@ int ip6_mc_source(int add, int omode, struct sock *sk, ...@@ -327,14 +318,17 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
if (!ipv6_addr_is_multicast(group)) if (!ipv6_addr_is_multicast(group))
return -EINVAL; return -EINVAL;
idev = ip6_mc_find_dev(net, group, pgsr->gsr_interface); rcu_read_lock();
if (!idev) idev = ip6_mc_find_dev_rcu(net, group, pgsr->gsr_interface);
if (!idev) {
rcu_read_unlock();
return -ENODEV; return -ENODEV;
}
dev = idev->dev; dev = idev->dev;
err = -EADDRNOTAVAIL; err = -EADDRNOTAVAIL;
read_lock_bh(&ipv6_sk_mc_lock); read_lock(&ipv6_sk_mc_lock);
for (pmc=inet6->ipv6_mc_list; pmc; pmc=pmc->next) { for (pmc=inet6->ipv6_mc_list; pmc; pmc=pmc->next) {
if (pgsr->gsr_interface && pmc->ifindex != pgsr->gsr_interface) if (pgsr->gsr_interface && pmc->ifindex != pgsr->gsr_interface)
continue; continue;
...@@ -358,7 +352,7 @@ int ip6_mc_source(int add, int omode, struct sock *sk, ...@@ -358,7 +352,7 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
pmc->sfmode = omode; pmc->sfmode = omode;
} }
write_lock_bh(&pmc->sflock); write_lock(&pmc->sflock);
pmclocked = 1; pmclocked = 1;
psl = pmc->sflist; psl = pmc->sflist;
...@@ -433,11 +427,10 @@ int ip6_mc_source(int add, int omode, struct sock *sk, ...@@ -433,11 +427,10 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
ip6_mc_add_src(idev, group, omode, 1, source, 1); ip6_mc_add_src(idev, group, omode, 1, source, 1);
done: done:
if (pmclocked) if (pmclocked)
write_unlock_bh(&pmc->sflock); write_unlock(&pmc->sflock);
read_unlock_bh(&ipv6_sk_mc_lock); read_unlock(&ipv6_sk_mc_lock);
read_unlock_bh(&idev->lock); read_unlock_bh(&idev->lock);
in6_dev_put(idev); rcu_read_unlock();
dev_put(dev);
if (leavegroup) if (leavegroup)
return ipv6_sock_mc_drop(sk, pgsr->gsr_interface, group); return ipv6_sock_mc_drop(sk, pgsr->gsr_interface, group);
return err; return err;
...@@ -463,14 +456,17 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf) ...@@ -463,14 +456,17 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf)
gsf->gf_fmode != MCAST_EXCLUDE) gsf->gf_fmode != MCAST_EXCLUDE)
return -EINVAL; return -EINVAL;
idev = ip6_mc_find_dev(net, group, gsf->gf_interface); rcu_read_lock();
idev = ip6_mc_find_dev_rcu(net, group, gsf->gf_interface);
if (!idev) if (!idev) {
rcu_read_unlock();
return -ENODEV; return -ENODEV;
}
dev = idev->dev; dev = idev->dev;
err = 0; err = 0;
read_lock_bh(&ipv6_sk_mc_lock); read_lock(&ipv6_sk_mc_lock);
if (gsf->gf_fmode == MCAST_INCLUDE && gsf->gf_numsrc == 0) { if (gsf->gf_fmode == MCAST_INCLUDE && gsf->gf_numsrc == 0) {
leavegroup = 1; leavegroup = 1;
...@@ -512,7 +508,7 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf) ...@@ -512,7 +508,7 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf)
(void) ip6_mc_add_src(idev, group, gsf->gf_fmode, 0, NULL, 0); (void) ip6_mc_add_src(idev, group, gsf->gf_fmode, 0, NULL, 0);
} }
write_lock_bh(&pmc->sflock); write_lock(&pmc->sflock);
psl = pmc->sflist; psl = pmc->sflist;
if (psl) { if (psl) {
(void) ip6_mc_del_src(idev, group, pmc->sfmode, (void) ip6_mc_del_src(idev, group, pmc->sfmode,
...@@ -522,13 +518,12 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf) ...@@ -522,13 +518,12 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf)
(void) ip6_mc_del_src(idev, group, pmc->sfmode, 0, NULL, 0); (void) ip6_mc_del_src(idev, group, pmc->sfmode, 0, NULL, 0);
pmc->sflist = newpsl; pmc->sflist = newpsl;
pmc->sfmode = gsf->gf_fmode; pmc->sfmode = gsf->gf_fmode;
write_unlock_bh(&pmc->sflock); write_unlock(&pmc->sflock);
err = 0; err = 0;
done: done:
read_unlock_bh(&ipv6_sk_mc_lock); read_unlock(&ipv6_sk_mc_lock);
read_unlock_bh(&idev->lock); read_unlock_bh(&idev->lock);
in6_dev_put(idev); rcu_read_unlock();
dev_put(dev);
if (leavegroup) if (leavegroup)
err = ipv6_sock_mc_drop(sk, gsf->gf_interface, group); err = ipv6_sock_mc_drop(sk, gsf->gf_interface, group);
return err; return err;
...@@ -551,11 +546,13 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf, ...@@ -551,11 +546,13 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
if (!ipv6_addr_is_multicast(group)) if (!ipv6_addr_is_multicast(group))
return -EINVAL; return -EINVAL;
idev = ip6_mc_find_dev(net, group, gsf->gf_interface); rcu_read_lock();
idev = ip6_mc_find_dev_rcu(net, group, gsf->gf_interface);
if (!idev) if (!idev) {
rcu_read_unlock();
return -ENODEV; return -ENODEV;
}
dev = idev->dev; dev = idev->dev;
err = -EADDRNOTAVAIL; err = -EADDRNOTAVAIL;
...@@ -577,8 +574,7 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf, ...@@ -577,8 +574,7 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
psl = pmc->sflist; psl = pmc->sflist;
count = psl ? psl->sl_count : 0; count = psl ? psl->sl_count : 0;
read_unlock_bh(&idev->lock); read_unlock_bh(&idev->lock);
in6_dev_put(idev); rcu_read_unlock();
dev_put(dev);
copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc; copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
gsf->gf_numsrc = count; gsf->gf_numsrc = count;
...@@ -604,8 +600,7 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf, ...@@ -604,8 +600,7 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
return 0; return 0;
done: done:
read_unlock_bh(&idev->lock); read_unlock_bh(&idev->lock);
in6_dev_put(idev); rcu_read_unlock();
dev_put(dev);
return err; return err;
} }
...@@ -822,6 +817,7 @@ int ipv6_dev_mc_inc(struct net_device *dev, const struct in6_addr *addr) ...@@ -822,6 +817,7 @@ int ipv6_dev_mc_inc(struct net_device *dev, const struct in6_addr *addr)
struct ifmcaddr6 *mc; struct ifmcaddr6 *mc;
struct inet6_dev *idev; struct inet6_dev *idev;
/* we need to take a reference on idev */
idev = in6_dev_get(dev); idev = in6_dev_get(dev);
if (idev == NULL) if (idev == NULL)
...@@ -860,7 +856,7 @@ int ipv6_dev_mc_inc(struct net_device *dev, const struct in6_addr *addr) ...@@ -860,7 +856,7 @@ int ipv6_dev_mc_inc(struct net_device *dev, const struct in6_addr *addr)
setup_timer(&mc->mca_timer, igmp6_timer_handler, (unsigned long)mc); setup_timer(&mc->mca_timer, igmp6_timer_handler, (unsigned long)mc);
ipv6_addr_copy(&mc->mca_addr, addr); ipv6_addr_copy(&mc->mca_addr, addr);
mc->idev = idev; mc->idev = idev; /* (reference taken) */
mc->mca_users = 1; mc->mca_users = 1;
/* mca_stamp should be updated upon changes */ /* mca_stamp should be updated upon changes */
mc->mca_cstamp = mc->mca_tstamp = jiffies; mc->mca_cstamp = mc->mca_tstamp = jiffies;
...@@ -915,16 +911,18 @@ int __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr) ...@@ -915,16 +911,18 @@ int __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr)
int ipv6_dev_mc_dec(struct net_device *dev, const struct in6_addr *addr) int ipv6_dev_mc_dec(struct net_device *dev, const struct in6_addr *addr)
{ {
struct inet6_dev *idev = in6_dev_get(dev); struct inet6_dev *idev;
int err; int err;
if (!idev) rcu_read_lock();
return -ENODEV;
err = __ipv6_dev_mc_dec(idev, addr);
in6_dev_put(idev); idev = __in6_dev_get(dev);
if (!idev)
err = -ENODEV;
else
err = __ipv6_dev_mc_dec(idev, addr);
rcu_read_unlock();
return err; return err;
} }
...@@ -965,7 +963,8 @@ int ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group, ...@@ -965,7 +963,8 @@ int ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group,
struct ifmcaddr6 *mc; struct ifmcaddr6 *mc;
int rv = 0; int rv = 0;
idev = in6_dev_get(dev); rcu_read_lock();
idev = __in6_dev_get(dev);
if (idev) { if (idev) {
read_lock_bh(&idev->lock); read_lock_bh(&idev->lock);
for (mc = idev->mc_list; mc; mc=mc->next) { for (mc = idev->mc_list; mc; mc=mc->next) {
...@@ -992,8 +991,8 @@ int ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group, ...@@ -992,8 +991,8 @@ int ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group,
rv = 1; /* don't filter unspecified source */ rv = 1; /* don't filter unspecified source */
} }
read_unlock_bh(&idev->lock); read_unlock_bh(&idev->lock);
in6_dev_put(idev);
} }
rcu_read_unlock();
return rv; return rv;
} }
...@@ -1104,6 +1103,7 @@ static int mld_marksources(struct ifmcaddr6 *pmc, int nsrcs, ...@@ -1104,6 +1103,7 @@ static int mld_marksources(struct ifmcaddr6 *pmc, int nsrcs,
return 1; return 1;
} }
/* called with rcu_read_lock() */
int igmp6_event_query(struct sk_buff *skb) int igmp6_event_query(struct sk_buff *skb)
{ {
struct mld2_query *mlh2 = NULL; struct mld2_query *mlh2 = NULL;
...@@ -1127,7 +1127,7 @@ int igmp6_event_query(struct sk_buff *skb) ...@@ -1127,7 +1127,7 @@ int igmp6_event_query(struct sk_buff *skb)
if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL))
return -EINVAL; return -EINVAL;
idev = in6_dev_get(skb->dev); idev = __in6_dev_get(skb->dev);
if (idev == NULL) if (idev == NULL)
return 0; return 0;
...@@ -1137,10 +1137,8 @@ int igmp6_event_query(struct sk_buff *skb) ...@@ -1137,10 +1137,8 @@ int igmp6_event_query(struct sk_buff *skb)
group_type = ipv6_addr_type(group); group_type = ipv6_addr_type(group);
if (group_type != IPV6_ADDR_ANY && if (group_type != IPV6_ADDR_ANY &&
!(group_type&IPV6_ADDR_MULTICAST)) { !(group_type&IPV6_ADDR_MULTICAST))
in6_dev_put(idev);
return -EINVAL; return -EINVAL;
}
if (len == 24) { if (len == 24) {
int switchback; int switchback;
...@@ -1161,10 +1159,9 @@ int igmp6_event_query(struct sk_buff *skb) ...@@ -1161,10 +1159,9 @@ int igmp6_event_query(struct sk_buff *skb)
} else if (len >= 28) { } else if (len >= 28) {
int srcs_offset = sizeof(struct mld2_query) - int srcs_offset = sizeof(struct mld2_query) -
sizeof(struct icmp6hdr); sizeof(struct icmp6hdr);
if (!pskb_may_pull(skb, srcs_offset)) { if (!pskb_may_pull(skb, srcs_offset))
in6_dev_put(idev);
return -EINVAL; return -EINVAL;
}
mlh2 = (struct mld2_query *)skb_transport_header(skb); mlh2 = (struct mld2_query *)skb_transport_header(skb);
max_delay = (MLDV2_MRC(ntohs(mlh2->mld2q_mrc))*HZ)/1000; max_delay = (MLDV2_MRC(ntohs(mlh2->mld2q_mrc))*HZ)/1000;
if (!max_delay) if (!max_delay)
...@@ -1173,28 +1170,23 @@ int igmp6_event_query(struct sk_buff *skb) ...@@ -1173,28 +1170,23 @@ int igmp6_event_query(struct sk_buff *skb)
if (mlh2->mld2q_qrv) if (mlh2->mld2q_qrv)
idev->mc_qrv = mlh2->mld2q_qrv; idev->mc_qrv = mlh2->mld2q_qrv;
if (group_type == IPV6_ADDR_ANY) { /* general query */ if (group_type == IPV6_ADDR_ANY) { /* general query */
if (mlh2->mld2q_nsrcs) { if (mlh2->mld2q_nsrcs)
in6_dev_put(idev);
return -EINVAL; /* no sources allowed */ return -EINVAL; /* no sources allowed */
}
mld_gq_start_timer(idev); mld_gq_start_timer(idev);
in6_dev_put(idev);
return 0; return 0;
} }
/* mark sources to include, if group & source-specific */ /* mark sources to include, if group & source-specific */
if (mlh2->mld2q_nsrcs != 0) { if (mlh2->mld2q_nsrcs != 0) {
if (!pskb_may_pull(skb, srcs_offset + if (!pskb_may_pull(skb, srcs_offset +
ntohs(mlh2->mld2q_nsrcs) * sizeof(struct in6_addr))) { ntohs(mlh2->mld2q_nsrcs) * sizeof(struct in6_addr)))
in6_dev_put(idev);
return -EINVAL; return -EINVAL;
}
mlh2 = (struct mld2_query *)skb_transport_header(skb); mlh2 = (struct mld2_query *)skb_transport_header(skb);
mark = 1; mark = 1;
} }
} else { } else
in6_dev_put(idev);
return -EINVAL; return -EINVAL;
}
read_lock_bh(&idev->lock); read_lock_bh(&idev->lock);
if (group_type == IPV6_ADDR_ANY) { if (group_type == IPV6_ADDR_ANY) {
...@@ -1227,12 +1219,11 @@ int igmp6_event_query(struct sk_buff *skb) ...@@ -1227,12 +1219,11 @@ int igmp6_event_query(struct sk_buff *skb)
} }
} }
read_unlock_bh(&idev->lock); read_unlock_bh(&idev->lock);
in6_dev_put(idev);
return 0; return 0;
} }
/* called with rcu_read_lock() */
int igmp6_event_report(struct sk_buff *skb) int igmp6_event_report(struct sk_buff *skb)
{ {
struct ifmcaddr6 *ma; struct ifmcaddr6 *ma;
...@@ -1260,7 +1251,7 @@ int igmp6_event_report(struct sk_buff *skb) ...@@ -1260,7 +1251,7 @@ int igmp6_event_report(struct sk_buff *skb)
!(addr_type&IPV6_ADDR_LINKLOCAL)) !(addr_type&IPV6_ADDR_LINKLOCAL))
return -EINVAL; return -EINVAL;
idev = in6_dev_get(skb->dev); idev = __in6_dev_get(skb->dev);
if (idev == NULL) if (idev == NULL)
return -ENODEV; return -ENODEV;
...@@ -1280,7 +1271,6 @@ int igmp6_event_report(struct sk_buff *skb) ...@@ -1280,7 +1271,6 @@ int igmp6_event_report(struct sk_buff *skb)
} }
} }
read_unlock_bh(&idev->lock); read_unlock_bh(&idev->lock);
in6_dev_put(idev);
return 0; return 0;
} }
...@@ -1396,12 +1386,14 @@ static void mld_sendpack(struct sk_buff *skb) ...@@ -1396,12 +1386,14 @@ static void mld_sendpack(struct sk_buff *skb)
struct mld2_report *pmr = struct mld2_report *pmr =
(struct mld2_report *)skb_transport_header(skb); (struct mld2_report *)skb_transport_header(skb);
int payload_len, mldlen; int payload_len, mldlen;
struct inet6_dev *idev = in6_dev_get(skb->dev); struct inet6_dev *idev;
struct net *net = dev_net(skb->dev); struct net *