diff options
Diffstat (limited to 'freebsd/sys/netinet6/in6_mcast.c')
-rw-r--r-- | freebsd/sys/netinet6/in6_mcast.c | 73 |
1 files changed, 40 insertions, 33 deletions
diff --git a/freebsd/sys/netinet6/in6_mcast.c b/freebsd/sys/netinet6/in6_mcast.c index 3824645d..ceeca992 100644 --- a/freebsd/sys/netinet6/in6_mcast.c +++ b/freebsd/sys/netinet6/in6_mcast.c @@ -192,7 +192,6 @@ static SYSCTL_NODE(_net_inet6_ip6_mcast, OID_AUTO, filters, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_ip6_mcast_filters, "Per-interface stack-wide source filters"); -int ifma6_restart = 0; #ifdef KTR /* * Inline function which wraps assertions for a valid ifp. @@ -407,6 +406,7 @@ static int in6_getmulti(struct ifnet *ifp, const struct in6_addr *group, struct in6_multi **pinm) { + struct epoch_tracker et; struct sockaddr_in6 gsin6; struct ifmultiaddr *ifma; struct in6_multi *inm; @@ -422,7 +422,10 @@ in6_getmulti(struct ifnet *ifp, const struct in6_addr *group, IN6_MULTI_LOCK_ASSERT(); IN6_MULTI_LIST_LOCK(); IF_ADDR_WLOCK(ifp); + NET_EPOCH_ENTER_ET(et); inm = in6m_lookup_locked(ifp, group); + NET_EPOCH_EXIT_ET(et); + if (inm != NULL) { /* * If we already joined this group, just bump the @@ -587,7 +590,15 @@ in6m_release_list_deferred(struct in6_multi_head *inmh) } void -in6m_disconnect(struct in6_multi *inm) +in6m_release_wait(void) +{ + + /* Wait for all jobs to complete. */ + gtaskqueue_drain_all(free_gtask.gt_taskqueue); +} + +void +in6m_disconnect_locked(struct in6_multi_head *inmh, struct in6_multi *inm) { struct ifnet *ifp; struct ifaddr *ifa; @@ -595,10 +606,12 @@ in6m_disconnect(struct in6_multi *inm) struct in6_multi_mship *imm, *imm_tmp; struct ifmultiaddr *ifma, *ll_ifma; - ifp = inm->in6m_ifp; + IN6_MULTI_LIST_LOCK_ASSERT(); + ifp = inm->in6m_ifp; if (ifp == NULL) - return; + return; /* already called */ + inm->in6m_ifp = NULL; IF_ADDR_WLOCK_ASSERT(ifp); ifma = inm->in6m_ifma; @@ -617,7 +630,6 @@ in6m_disconnect(struct in6_multi *inm) MPASS(ll_ifma->ifma_llifma == NULL); MPASS(ll_ifma->ifma_ifp == ifp); if (--ll_ifma->ifma_refcount == 0) { - ifma6_restart = true; if (ll_ifma->ifma_flags & IFMA_F_ENQUEUED) { CK_STAILQ_REMOVE(&ifp->if_multiaddrs, ll_ifma, ifmultiaddr, ifma_link); ll_ifma->ifma_flags &= ~IFMA_F_ENQUEUED; @@ -635,28 +647,12 @@ in6m_disconnect(struct in6_multi *inm) if (inm == imm->i6mm_maddr) { LIST_REMOVE(imm, i6mm_chain); free(imm, M_IP6MADDR); + in6m_rele_locked(inmh, inm); } } } } -void -in6m_release_deferred(struct in6_multi *inm) -{ - struct in6_multi_head tmp; - - IN6_MULTI_LIST_LOCK_ASSERT(); - KASSERT(inm->in6m_refcount > 0, ("refcount == %d inm: %p", inm->in6m_refcount, inm)); - if (--inm->in6m_refcount == 0) { - MPASS(inm->in6m_ifp == NULL); - SLIST_INIT(&tmp); - inm->in6m_ifma->ifma_protospec = NULL; - MPASS(inm->in6m_ifma->ifma_llifma == NULL); - SLIST_INSERT_HEAD(&tmp, inm, in6m_nrele); - in6m_release_list_deferred(&tmp); - } -} - static void in6m_release_task(void *arg __unused) { @@ -1256,6 +1252,7 @@ in6_joingroup_locked(struct ifnet *ifp, const struct in6_addr *mcaddr, /*const*/ struct in6_mfilter *imf, struct in6_multi **pinm, const int delay) { + struct in6_multi_head inmh; struct in6_mfilter timf; struct in6_multi *inm; struct ifmultiaddr *ifma; @@ -1317,6 +1314,7 @@ in6_joingroup_locked(struct ifnet *ifp, const struct in6_addr *mcaddr, } out_in6m_release: + SLIST_INIT(&inmh); if (error) { CTR2(KTR_MLD, "%s: dropping ref on %p", __func__, inm); IF_ADDR_RLOCK(ifp); @@ -1326,13 +1324,14 @@ out_in6m_release: break; } } - in6m_disconnect(inm); - in6m_release_deferred(inm); + in6m_disconnect_locked(&inmh, inm); + in6m_rele_locked(&inmh, inm); IF_ADDR_RUNLOCK(ifp); } else { *pinm = inm; } IN6_MULTI_LIST_UNLOCK(); + in6m_release_list_deferred(&inmh); return (error); } @@ -1366,6 +1365,7 @@ in6_leavegroup(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf) int in6_leavegroup_locked(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf) { + struct in6_multi_head inmh; struct in6_mfilter timf; struct ifnet *ifp; int error; @@ -1415,13 +1415,15 @@ in6_leavegroup_locked(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf) CTR2(KTR_MLD, "%s: dropping ref on %p", __func__, inm); if (ifp) IF_ADDR_WLOCK(ifp); - if (inm->in6m_refcount == 1 && inm->in6m_ifp != NULL) - in6m_disconnect(inm); - in6m_release_deferred(inm); + + SLIST_INIT(&inmh); + if (inm->in6m_refcount == 1) + in6m_disconnect_locked(&inmh, inm); + in6m_rele_locked(&inmh, inm); if (ifp) IF_ADDR_WUNLOCK(ifp); IN6_MULTI_LIST_UNLOCK(); - + in6m_release_list_deferred(&inmh); return (error); } @@ -1935,6 +1937,7 @@ in6p_lookup_mcast_ifp(const struct inpcb *in6p, static int in6p_join_group(struct inpcb *inp, struct sockopt *sopt) { + struct in6_multi_head inmh; struct group_source_req gsr; sockunion_t *gsa, *ssa; struct ifnet *ifp; @@ -1945,6 +1948,7 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt) size_t idx; int error, is_new; + SLIST_INIT(&inmh); ifp = NULL; imf = NULL; lims = NULL; @@ -2180,7 +2184,10 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt) IN6_MULTI_UNLOCK(); goto out_im6o_free; } - in6m_acquire(inm); + /* + * NOTE: Refcount from in6_joingroup_locked() + * is protecting membership. + */ imo->im6o_membership[idx] = inm; } else { CTR1(KTR_MLD, "%s: merge inm state", __func__); @@ -2218,7 +2225,7 @@ out_im6o_free: inm = imo->im6o_membership[idx]; if (inm != NULL) { IN6_MULTI_LIST_LOCK(); - in6m_release_deferred(inm); + in6m_rele_locked(&inmh, inm); IN6_MULTI_LIST_UNLOCK(); } imo->im6o_membership[idx] = NULL; @@ -2227,6 +2234,7 @@ out_im6o_free: out_in6p_locked: INP_WUNLOCK(inp); + in6m_release_list_deferred(&inmh); return (error); } @@ -2870,10 +2878,9 @@ sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS) IN6_MULTI_LIST_LOCK(); IF_ADDR_RLOCK(ifp); CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { - if (ifma->ifma_addr->sa_family != AF_INET6 || - ifma->ifma_protospec == NULL) + inm = in6m_ifmultiaddr_get_inm(ifma); + if (inm == NULL) continue; - inm = (struct in6_multi *)ifma->ifma_protospec; if (!IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, &mcaddr)) continue; fmode = inm->in6m_st[1].iss_fmode; |