summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/netinet6/in6_mcast.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/netinet6/in6_mcast.c')
-rw-r--r--freebsd/sys/netinet6/in6_mcast.c46
1 files changed, 37 insertions, 9 deletions
diff --git a/freebsd/sys/netinet6/in6_mcast.c b/freebsd/sys/netinet6/in6_mcast.c
index 32660c89..3824645d 100644
--- a/freebsd/sys/netinet6/in6_mcast.c
+++ b/freebsd/sys/netinet6/in6_mcast.c
@@ -540,6 +540,8 @@ in6m_release(struct in6_multi *inm)
CTR2(KTR_MLD, "%s: purging ifma %p", __func__, ifma);
KASSERT(ifma->ifma_protospec == NULL,
("%s: ifma_protospec != NULL", __func__));
+ if (ifp == NULL)
+ ifp = ifma->ifma_ifp;
if (ifp != NULL) {
CURVNET_SET(ifp->if_vnet);
@@ -564,8 +566,13 @@ static void in6m_init(void)
taskqgroup_config_gtask_init(NULL, &free_gtask, in6m_release_task, "in6m release task");
}
+#ifdef EARLY_AP_STARTUP
SYSINIT(in6m_init, SI_SUB_SMP + 1, SI_ORDER_FIRST,
in6m_init, NULL);
+#else
+SYSINIT(in6m_init, SI_SUB_ROOT_CONF - 1, SI_ORDER_SECOND,
+ in6m_init, NULL);
+#endif
void
@@ -589,11 +596,20 @@ in6m_disconnect(struct in6_multi *inm)
struct ifmultiaddr *ifma, *ll_ifma;
ifp = inm->in6m_ifp;
+
+ if (ifp == NULL)
+ return;
+ inm->in6m_ifp = NULL;
IF_ADDR_WLOCK_ASSERT(ifp);
ifma = inm->in6m_ifma;
+ if (ifma == NULL)
+ return;
if_ref(ifp);
- CK_STAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifmultiaddr, ifma_link);
+ if (ifma->ifma_flags & IFMA_F_ENQUEUED) {
+ CK_STAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifmultiaddr, ifma_link);
+ ifma->ifma_flags &= ~IFMA_F_ENQUEUED;
+ }
MCDPRINTF("removed ifma: %p from %s\n", ifma, ifp->if_xname);
if ((ll_ifma = ifma->ifma_llifma) != NULL) {
MPASS(ifma != ll_ifma);
@@ -602,7 +618,10 @@ in6m_disconnect(struct in6_multi *inm)
MPASS(ll_ifma->ifma_ifp == ifp);
if (--ll_ifma->ifma_refcount == 0) {
ifma6_restart = true;
- CK_STAILQ_REMOVE(&ifp->if_multiaddrs, ll_ifma, ifmultiaddr, ifma_link);
+ 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;
+ }
MCDPRINTF("removed ll_ifma: %p from %s\n", ll_ifma, ifp->if_xname);
if_freemulti(ll_ifma);
}
@@ -629,7 +648,7 @@ in6m_release_deferred(struct in6_multi *inm)
IN6_MULTI_LIST_LOCK_ASSERT();
KASSERT(inm->in6m_refcount > 0, ("refcount == %d inm: %p", inm->in6m_refcount, inm));
if (--inm->in6m_refcount == 0) {
- in6m_disconnect(inm);
+ MPASS(inm->in6m_ifp == NULL);
SLIST_INIT(&tmp);
inm->in6m_ifma->ifma_protospec = NULL;
MPASS(inm->in6m_ifma->ifma_llifma == NULL);
@@ -1307,6 +1326,7 @@ out_in6m_release:
break;
}
}
+ in6m_disconnect(inm);
in6m_release_deferred(inm);
IF_ADDR_RUNLOCK(ifp);
} else {
@@ -1386,13 +1406,17 @@ in6_leavegroup_locked(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf)
KASSERT(error == 0, ("%s: failed to merge inm state", __func__));
CTR1(KTR_MLD, "%s: doing mld downcall", __func__);
- error = mld_change_state(inm, 0);
+ error = 0;
+ if (ifp)
+ error = mld_change_state(inm, 0);
if (error)
CTR1(KTR_MLD, "%s: failed mld downcall", __func__);
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);
if (ifp)
IF_ADDR_WUNLOCK(ifp);
@@ -1626,16 +1650,13 @@ in6p_findmoptions(struct inpcb *inp)
*/
static void
-inp_gcmoptions(epoch_context_t ctx)
+inp_gcmoptions(struct ip6_moptions *imo)
{
- struct ip6_moptions *imo;
struct in6_mfilter *imf;
struct in6_multi *inm;
struct ifnet *ifp;
size_t idx, nmships;
- imo = __containerof(ctx, struct ip6_moptions, imo6_epoch_ctx);
-
nmships = imo->im6o_num_memberships;
for (idx = 0; idx < nmships; ++idx) {
imf = imo->im6o_mfilters ? &imo->im6o_mfilters[idx] : NULL;
@@ -1665,7 +1686,7 @@ ip6_freemoptions(struct ip6_moptions *imo)
{
if (imo == NULL)
return;
- epoch_call(net_epoch_preempt, &imo->imo6_epoch_ctx, inp_gcmoptions);
+ inp_gcmoptions(imo);
}
/*
@@ -2159,6 +2180,7 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
IN6_MULTI_UNLOCK();
goto out_im6o_free;
}
+ in6m_acquire(inm);
imo->im6o_membership[idx] = inm;
} else {
CTR1(KTR_MLD, "%s: merge inm state", __func__);
@@ -2193,6 +2215,12 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
out_im6o_free:
if (error && is_new) {
+ inm = imo->im6o_membership[idx];
+ if (inm != NULL) {
+ IN6_MULTI_LIST_LOCK();
+ in6m_release_deferred(inm);
+ IN6_MULTI_LIST_UNLOCK();
+ }
imo->im6o_membership[idx] = NULL;
--imo->im6o_num_memberships;
}