Commit 882ba1f7 authored by Taehee Yoo's avatar Taehee Yoo Committed by David S. Miller

mld: convert ipv6_mc_socklist->sflist to RCU

The sflist has been protected by rwlock so that the critical section
is atomic context.
In order to switch this context, changing locking is needed.
The sflist actually already protected by RTNL So if it's converted
to use RCU, its control path context can be switched to sleepable.
Suggested-by: default avatarCong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: default avatarTaehee Yoo <ap420073@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent cf2ce339
...@@ -78,6 +78,7 @@ struct inet6_ifaddr { ...@@ -78,6 +78,7 @@ struct inet6_ifaddr {
struct ip6_sf_socklist { struct ip6_sf_socklist {
unsigned int sl_max; unsigned int sl_max;
unsigned int sl_count; unsigned int sl_count;
struct rcu_head rcu;
struct in6_addr sl_addr[]; struct in6_addr sl_addr[];
}; };
...@@ -91,8 +92,7 @@ struct ipv6_mc_socklist { ...@@ -91,8 +92,7 @@ struct ipv6_mc_socklist {
int ifindex; int ifindex;
unsigned int sfmode; /* MCAST_{INCLUDE,EXCLUDE} */ unsigned int sfmode; /* MCAST_{INCLUDE,EXCLUDE} */
struct ipv6_mc_socklist __rcu *next; struct ipv6_mc_socklist __rcu *next;
rwlock_t sflock; struct ip6_sf_socklist __rcu *sflist;
struct ip6_sf_socklist *sflist;
struct rcu_head rcu; struct rcu_head rcu;
}; };
......
...@@ -178,8 +178,7 @@ static int __ipv6_sock_mc_join(struct sock *sk, int ifindex, ...@@ -178,8 +178,7 @@ static int __ipv6_sock_mc_join(struct sock *sk, int ifindex,
mc_lst->ifindex = dev->ifindex; mc_lst->ifindex = dev->ifindex;
mc_lst->sfmode = mode; mc_lst->sfmode = mode;
rwlock_init(&mc_lst->sflock); RCU_INIT_POINTER(mc_lst->sflist, NULL);
mc_lst->sflist = NULL;
/* /*
* now add/increase the group membership on the device * now add/increase the group membership on the device
...@@ -335,7 +334,6 @@ int ip6_mc_source(int add, int omode, struct sock *sk, ...@@ -335,7 +334,6 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
struct net *net = sock_net(sk); struct net *net = sock_net(sk);
int i, j, rv; int i, j, rv;
int leavegroup = 0; int leavegroup = 0;
int pmclocked = 0;
int err; int err;
source = &((struct sockaddr_in6 *)&pgsr->gsr_source)->sin6_addr; source = &((struct sockaddr_in6 *)&pgsr->gsr_source)->sin6_addr;
...@@ -364,7 +362,7 @@ int ip6_mc_source(int add, int omode, struct sock *sk, ...@@ -364,7 +362,7 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
goto done; goto done;
} }
/* if a source filter was set, must be the same mode as before */ /* if a source filter was set, must be the same mode as before */
if (pmc->sflist) { if (rcu_access_pointer(pmc->sflist)) {
if (pmc->sfmode != omode) { if (pmc->sfmode != omode) {
err = -EINVAL; err = -EINVAL;
goto done; goto done;
...@@ -376,10 +374,7 @@ int ip6_mc_source(int add, int omode, struct sock *sk, ...@@ -376,10 +374,7 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
pmc->sfmode = omode; pmc->sfmode = omode;
} }
write_lock(&pmc->sflock); psl = rtnl_dereference(pmc->sflist);
pmclocked = 1;
psl = pmc->sflist;
if (!add) { if (!add) {
if (!psl) if (!psl)
goto done; /* err = -EADDRNOTAVAIL */ goto done; /* err = -EADDRNOTAVAIL */
...@@ -429,9 +424,11 @@ int ip6_mc_source(int add, int omode, struct sock *sk, ...@@ -429,9 +424,11 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
if (psl) { if (psl) {
for (i = 0; i < psl->sl_count; i++) for (i = 0; i < psl->sl_count; i++)
newpsl->sl_addr[i] = psl->sl_addr[i]; newpsl->sl_addr[i] = psl->sl_addr[i];
sock_kfree_s(sk, psl, IP6_SFLSIZE(psl->sl_max)); atomic_sub(IP6_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc);
kfree_rcu(psl, rcu);
} }
pmc->sflist = psl = newpsl; psl = newpsl;
rcu_assign_pointer(pmc->sflist, psl);
} }
rv = 1; /* > 0 for insert logic below if sl_count is 0 */ rv = 1; /* > 0 for insert logic below if sl_count is 0 */
for (i = 0; i < psl->sl_count; i++) { for (i = 0; i < psl->sl_count; i++) {
...@@ -447,8 +444,6 @@ int ip6_mc_source(int add, int omode, struct sock *sk, ...@@ -447,8 +444,6 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
/* update the interface list */ /* update the interface list */
ip6_mc_add_src(idev, group, omode, 1, source, 1); ip6_mc_add_src(idev, group, omode, 1, source, 1);
done: done:
if (pmclocked)
write_unlock(&pmc->sflock);
read_unlock_bh(&idev->lock); read_unlock_bh(&idev->lock);
rcu_read_unlock(); rcu_read_unlock();
if (leavegroup) if (leavegroup)
...@@ -526,17 +521,16 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf, ...@@ -526,17 +521,16 @@ 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(&pmc->sflock); psl = rtnl_dereference(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,
psl->sl_count, psl->sl_addr, 0); psl->sl_count, psl->sl_addr, 0);
sock_kfree_s(sk, psl, IP6_SFLSIZE(psl->sl_max)); atomic_sub(IP6_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc);
kfree_rcu(psl, rcu);
} else } else
(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; rcu_assign_pointer(pmc->sflist, newpsl);
pmc->sfmode = gsf->gf_fmode; pmc->sfmode = gsf->gf_fmode;
write_unlock(&pmc->sflock);
err = 0; err = 0;
done: done:
read_unlock_bh(&idev->lock); read_unlock_bh(&idev->lock);
...@@ -585,16 +579,14 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf, ...@@ -585,16 +579,14 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
if (!pmc) /* must have a prior join */ if (!pmc) /* must have a prior join */
goto done; goto done;
gsf->gf_fmode = pmc->sfmode; gsf->gf_fmode = pmc->sfmode;
psl = pmc->sflist; psl = rtnl_dereference(pmc->sflist);
count = psl ? psl->sl_count : 0; count = psl ? psl->sl_count : 0;
read_unlock_bh(&idev->lock); read_unlock_bh(&idev->lock);
rcu_read_unlock(); rcu_read_unlock();
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;
/* changes to psl require the socket lock, and a write lock
* on pmc->sflock. We have the socket lock so reading here is safe.
*/
for (i = 0; i < copycount; i++, p++) { for (i = 0; i < copycount; i++, p++) {
struct sockaddr_in6 *psin6; struct sockaddr_in6 *psin6;
struct sockaddr_storage ss; struct sockaddr_storage ss;
...@@ -630,8 +622,7 @@ bool inet6_mc_check(struct sock *sk, const struct in6_addr *mc_addr, ...@@ -630,8 +622,7 @@ bool inet6_mc_check(struct sock *sk, const struct in6_addr *mc_addr,
rcu_read_unlock(); rcu_read_unlock();
return np->mc_all; return np->mc_all;
} }
read_lock(&mc->sflock); psl = rcu_dereference(mc->sflist);
psl = mc->sflist;
if (!psl) { if (!psl) {
rv = mc->sfmode == MCAST_EXCLUDE; rv = mc->sfmode == MCAST_EXCLUDE;
} else { } else {
...@@ -646,7 +637,6 @@ bool inet6_mc_check(struct sock *sk, const struct in6_addr *mc_addr, ...@@ -646,7 +637,6 @@ bool inet6_mc_check(struct sock *sk, const struct in6_addr *mc_addr,
if (mc->sfmode == MCAST_EXCLUDE && i < psl->sl_count) if (mc->sfmode == MCAST_EXCLUDE && i < psl->sl_count)
rv = false; rv = false;
} }
read_unlock(&mc->sflock);
rcu_read_unlock(); rcu_read_unlock();
return rv; return rv;
...@@ -2422,19 +2412,21 @@ static void igmp6_join_group(struct ifmcaddr6 *ma) ...@@ -2422,19 +2412,21 @@ static void igmp6_join_group(struct ifmcaddr6 *ma)
static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml, static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml,
struct inet6_dev *idev) struct inet6_dev *idev)
{ {
struct ip6_sf_socklist *psl;
int err; int err;
write_lock_bh(&iml->sflock); psl = rtnl_dereference(iml->sflist);
if (!iml->sflist) {
if (!psl) {
/* any-source empty exclude case */ /* any-source empty exclude case */
err = ip6_mc_del_src(idev, &iml->addr, iml->sfmode, 0, NULL, 0); err = ip6_mc_del_src(idev, &iml->addr, iml->sfmode, 0, NULL, 0);
} else { } else {
err = ip6_mc_del_src(idev, &iml->addr, iml->sfmode, err = ip6_mc_del_src(idev, &iml->addr, iml->sfmode,
iml->sflist->sl_count, iml->sflist->sl_addr, 0); psl->sl_count, psl->sl_addr, 0);
sock_kfree_s(sk, iml->sflist, IP6_SFLSIZE(iml->sflist->sl_max)); RCU_INIT_POINTER(iml->sflist, NULL);
iml->sflist = NULL; atomic_sub(IP6_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc);
kfree_rcu(psl, rcu);
} }
write_unlock_bh(&iml->sflock);
return err; return err;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment