2
0
mirror of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git synced 2025-09-04 20:19:47 +08:00

ipv6: mcast: Don't hold RTNL for IPV6_DROP_MEMBERSHIP and MCAST_LEAVE_GROUP.

In __ipv6_sock_mc_drop(), per-socket mld data is protected by lock_sock(),
and only __dev_get_by_index() and __in6_dev_get() require RTNL.

Let's use dev_get_by_index() and in6_dev_get() and drop RTNL for
IPV6_ADD_MEMBERSHIP and MCAST_JOIN_GROUP.

Note that __ipv6_sock_mc_drop() is factorised to reuse in the next patch.

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20250702230210.3115355-8-kuni1840@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Kuniyuki Iwashima 2025-07-02 16:01:24 -07:00 committed by Jakub Kicinski
parent 1767bb2d47
commit 2ceb71ce7d
2 changed files with 27 additions and 22 deletions

View File

@ -121,10 +121,8 @@ static bool setsockopt_needs_rtnl(int optname)
{ {
switch (optname) { switch (optname) {
case IPV6_ADDRFORM: case IPV6_ADDRFORM:
case IPV6_DROP_MEMBERSHIP:
case IPV6_JOIN_ANYCAST: case IPV6_JOIN_ANYCAST:
case IPV6_LEAVE_ANYCAST: case IPV6_LEAVE_ANYCAST:
case MCAST_LEAVE_GROUP:
case MCAST_JOIN_SOURCE_GROUP: case MCAST_JOIN_SOURCE_GROUP:
case MCAST_LEAVE_SOURCE_GROUP: case MCAST_LEAVE_SOURCE_GROUP:
case MCAST_BLOCK_SOURCE: case MCAST_BLOCK_SOURCE:

View File

@ -253,14 +253,36 @@ int ipv6_sock_mc_join_ssm(struct sock *sk, int ifindex,
/* /*
* socket leave on multicast group * socket leave on multicast group
*/ */
static void __ipv6_sock_mc_drop(struct sock *sk, struct ipv6_mc_socklist *mc_lst)
{
struct net *net = sock_net(sk);
struct net_device *dev;
dev = dev_get_by_index(net, mc_lst->ifindex);
if (dev) {
struct inet6_dev *idev = in6_dev_get(dev);
ip6_mc_leave_src(sk, mc_lst, idev);
if (idev) {
__ipv6_dev_mc_dec(idev, &mc_lst->addr);
in6_dev_put(idev);
}
dev_put(dev);
} else {
ip6_mc_leave_src(sk, mc_lst, NULL);
}
atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc);
kfree_rcu(mc_lst, rcu);
}
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 __rcu **lnk; struct ipv6_mc_socklist __rcu **lnk;
struct net *net = sock_net(sk); struct ipv6_mc_socklist *mc_lst;
ASSERT_RTNL();
if (!ipv6_addr_is_multicast(addr)) if (!ipv6_addr_is_multicast(addr))
return -EINVAL; return -EINVAL;
@ -270,23 +292,8 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
lnk = &mc_lst->next) { lnk = &mc_lst->next) {
if ((ifindex == 0 || mc_lst->ifindex == ifindex) && if ((ifindex == 0 || mc_lst->ifindex == ifindex) &&
ipv6_addr_equal(&mc_lst->addr, addr)) { ipv6_addr_equal(&mc_lst->addr, addr)) {
struct net_device *dev;
*lnk = mc_lst->next; *lnk = mc_lst->next;
__ipv6_sock_mc_drop(sk, mc_lst);
dev = __dev_get_by_index(net, mc_lst->ifindex);
if (dev) {
struct inet6_dev *idev = __in6_dev_get(dev);
ip6_mc_leave_src(sk, mc_lst, idev);
if (idev)
__ipv6_dev_mc_dec(idev, &mc_lst->addr);
} else {
ip6_mc_leave_src(sk, mc_lst, NULL);
}
atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc);
kfree_rcu(mc_lst, rcu);
return 0; return 0;
} }
} }