Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+87 −96
Original line number Original line Diff line number Diff line
@@ -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)
	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)
	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)
			*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,7 +234,8 @@ 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() */
static struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net,
					     struct in6_addr *group,
					     struct in6_addr *group,
					     int ifindex)
					     int ifindex)
{
{
@@ -242,36 +243,28 @@ static struct inet6_dev *ip6_mc_find_dev(struct net *net,
	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;

	return idev;

unlock_release:
		read_unlock_bh(&idev->lock);
		read_unlock_bh(&idev->lock);
	in6_dev_put(idev);
release:
	dev_put(dev);
nodev:
		return NULL;
		return NULL;
	}
	}
	return idev;
}


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)
		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,
	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,
		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,
	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)
	    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)
		(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)
		(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,
	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,
	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,
	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)
	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)
	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)


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;


	idev = __in6_dev_get(dev);
	if (!idev)
		err = -ENODEV;
	else
		err = __ipv6_dev_mc_dec(idev, addr);
		err = __ipv6_dev_mc_dec(idev, addr);


	in6_dev_put(idev);
	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,
	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,
				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,
	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)
	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)
	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)
	} 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)
		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)
		}
		}
	}
	}
	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)
	    !(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)
		}
		}
	}
	}
	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)
	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 *net = dev_net(skb->dev);
	int err;
	int err;
	struct flowi fl;
	struct flowi fl;
	struct dst_entry *dst;
	struct dst_entry *dst;


	rcu_read_lock();
	idev = __in6_dev_get(skb->dev);
	IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
	IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);


	payload_len = (skb->tail - skb->network_header) - sizeof(*pip6);
	payload_len = (skb->tail - skb->network_header) - sizeof(*pip6);
@@ -1441,8 +1433,7 @@ static void mld_sendpack(struct sk_buff *skb)
	} else
	} else
		IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_OUTDISCARDS);
		IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_OUTDISCARDS);


	if (likely(idev != NULL))
	rcu_read_unlock();
		in6_dev_put(idev);
	return;
	return;


err_out:
err_out:
@@ -1779,7 +1770,8 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
					 IPPROTO_ICMPV6,
					 IPPROTO_ICMPV6,
					 csum_partial(hdr, len, 0));
					 csum_partial(hdr, len, 0));


	idev = in6_dev_get(skb->dev);
	rcu_read_lock();
	idev = __in6_dev_get(skb->dev);


	dst = icmp6_dst_alloc(skb->dev, NULL, &ipv6_hdr(skb)->daddr);
	dst = icmp6_dst_alloc(skb->dev, NULL, &ipv6_hdr(skb)->daddr);
	if (!dst) {
	if (!dst) {
@@ -1806,8 +1798,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
	} else
	} else
		IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS);
		IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS);


	if (likely(idev != NULL))
	rcu_read_unlock();
		in6_dev_put(idev);
	return;
	return;


err_out:
err_out: