igmp v6: add __ipv6_sock_mc_join and __ipv6_sock_mc_drop

Based on the igmp v4 changes from Eric Dumazet.
959d10f6bbf6("igmp: add __ip_mc_{join|leave}_group()")

These changes are needed to perform igmp v6 join/leave while
RTNL is held.

Make ipv6_sock_mc_join and ipv6_sock_mc_drop wrappers around
__ipv6_sock_mc_join and  __ipv6_sock_mc_drop to avoid
proliferation of work queues.

Signed-off-by: Madhu Challa <challa@noironetworks.com>
Acked-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Madhu Challa 2015-02-25 09:58:34 -08:00 committed by David S. Miller
parent 723b8e460d
commit 46a4dee074
2 changed files with 40 additions and 10 deletions

View File

@ -940,4 +940,12 @@ int ipv6_sysctl_register(void);
void ipv6_sysctl_unregister(void); void ipv6_sysctl_unregister(void);
#endif #endif
int ipv6_sock_mc_join(struct sock *sk, int ifindex,
const struct in6_addr *addr);
int __ipv6_sock_mc_join(struct sock *sk, int ifindex,
const struct in6_addr *addr);
int ipv6_sock_mc_drop(struct sock *sk, int ifindex,
const struct in6_addr *addr);
int __ipv6_sock_mc_drop(struct sock *sk, int ifindex,
const struct in6_addr *addr);
#endif /* _NET_IPV6_H */ #endif /* _NET_IPV6_H */

View File

@ -132,7 +132,7 @@ static int unsolicited_report_interval(struct inet6_dev *idev)
return iv > 0 ? iv : 1; return iv > 0 ? iv : 1;
} }
int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) int __ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
{ {
struct net_device *dev = NULL; struct net_device *dev = NULL;
struct ipv6_mc_socklist *mc_lst; struct ipv6_mc_socklist *mc_lst;
@ -140,6 +140,8 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
struct net *net = sock_net(sk); struct net *net = sock_net(sk);
int err; int err;
ASSERT_RTNL();
if (!ipv6_addr_is_multicast(addr)) if (!ipv6_addr_is_multicast(addr))
return -EINVAL; return -EINVAL;
@ -161,7 +163,6 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
mc_lst->next = NULL; mc_lst->next = NULL;
mc_lst->addr = *addr; mc_lst->addr = *addr;
rtnl_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);
@ -173,7 +174,6 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
dev = __dev_get_by_index(net, ifindex); dev = __dev_get_by_index(net, ifindex);
if (dev == NULL) { if (dev == NULL) {
rtnl_unlock();
sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
return -ENODEV; return -ENODEV;
} }
@ -190,7 +190,6 @@ 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) {
rtnl_unlock();
sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
return err; return err;
} }
@ -198,25 +197,37 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
mc_lst->next = np->ipv6_mc_list; mc_lst->next = np->ipv6_mc_list;
rcu_assign_pointer(np->ipv6_mc_list, mc_lst); rcu_assign_pointer(np->ipv6_mc_list, mc_lst);
rtnl_unlock();
return 0; return 0;
} }
EXPORT_SYMBOL(__ipv6_sock_mc_join);
int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
{
int ret;
rtnl_lock();
ret = __ipv6_sock_mc_join(sk, ifindex, addr);
rtnl_unlock();
return ret;
}
EXPORT_SYMBOL(ipv6_sock_mc_join);
/* /*
* socket leave on multicast group * socket leave on multicast group
*/ */
int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) int __ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
{ {
struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk);
struct ipv6_mc_socklist *mc_lst; struct ipv6_mc_socklist *mc_lst;
struct ipv6_mc_socklist __rcu **lnk; struct ipv6_mc_socklist __rcu **lnk;
struct net *net = sock_net(sk); struct net *net = sock_net(sk);
ASSERT_RTNL();
if (!ipv6_addr_is_multicast(addr)) if (!ipv6_addr_is_multicast(addr))
return -EINVAL; return -EINVAL;
rtnl_lock();
for (lnk = &np->ipv6_mc_list; for (lnk = &np->ipv6_mc_list;
(mc_lst = rtnl_dereference(*lnk)) != NULL; (mc_lst = rtnl_dereference(*lnk)) != NULL;
lnk = &mc_lst->next) { lnk = &mc_lst->next) {
@ -235,17 +246,28 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
__ipv6_dev_mc_dec(idev, &mc_lst->addr); __ipv6_dev_mc_dec(idev, &mc_lst->addr);
} else } else
(void) ip6_mc_leave_src(sk, mc_lst, NULL); (void) ip6_mc_leave_src(sk, mc_lst, NULL);
rtnl_unlock();
atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc); atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc);
kfree_rcu(mc_lst, rcu); kfree_rcu(mc_lst, rcu);
return 0; return 0;
} }
} }
rtnl_unlock();
return -EADDRNOTAVAIL; return -EADDRNOTAVAIL;
} }
EXPORT_SYMBOL(__ipv6_sock_mc_drop);
int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
{
int ret;
rtnl_lock();
ret = __ipv6_sock_mc_drop(sk, ifindex, addr);
rtnl_unlock();
return ret;
}
EXPORT_SYMBOL(ipv6_sock_mc_drop);
/* called with rcu_read_lock() */ /* called with rcu_read_lock() */
static struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net, static struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net,