--- sys/netinet6/in6_mcast.c (revision 342455) +++ sys/netinet6/in6_mcast.c (working copy) @@ -426,6 +426,7 @@ * If we already joined this group, just bump the * refcount and return it. */ + hptrace(inm, "check"); KASSERT(inm->in6m_refcount >= 1, ("%s: bad refcount %d", __func__, inm->in6m_refcount)); in6m_acquire_locked(inm); @@ -497,6 +498,7 @@ inm->in6m_mli = MLD_IFINFO(ifp); inm->in6m_ifma = ifma; inm->in6m_refcount = 1; + hptrace(inm, "set"); inm->in6m_state = MLD_NOT_MEMBER; mbufq_init(&inm->in6m_scq, MLD_MAX_STATE_CHANGES); @@ -527,6 +529,8 @@ CTR2(KTR_MLD, "%s: refcount is %d", __func__, inm->in6m_refcount); + hptrace(inm, "check"); + MPASS(inm->in6m_refcount == 0); CTR2(KTR_MLD, "%s: freeing inm %p", __func__, inm); @@ -632,6 +636,9 @@ i6mm_chain, imm_tmp) { if (inm == imm->i6mm_maddr) { LIST_REMOVE(imm, i6mm_chain); + inm->in6m_refcount--; + hptrace(inm, "list remove - post dec"); + MPASS(inm->in6m_refcount > 0); free(imm, M_IP6MADDR); } } @@ -646,6 +653,7 @@ IN6_MULTI_LIST_LOCK_ASSERT(); KASSERT(inm->in6m_refcount > 0, ("refcount == %d inm: %p", inm->in6m_refcount, inm)); if (--inm->in6m_refcount == 0) { + hptrace(inm, "list release - post dec"); MPASS(inm->in6m_ifp == NULL); SLIST_INIT(&tmp); inm->in6m_ifma->ifma_protospec = NULL; @@ -652,6 +660,8 @@ MPASS(inm->in6m_ifma->ifma_llifma == NULL); SLIST_INSERT_HEAD(&tmp, inm, in6m_nrele); in6m_release_list_deferred(&tmp); + } else { + hptrace(inm, "list release - post dec"); } } @@ -1413,6 +1423,8 @@ CTR2(KTR_MLD, "%s: dropping ref on %p", __func__, inm); if (ifp) IF_ADDR_WLOCK(ifp); + + hptrace(inm, "leavegroup check"); if (inm->in6m_refcount == 1 && inm->in6m_ifp != NULL) in6m_disconnect(inm); in6m_release_deferred(inm); --- sys/netinet6/in6_var.h (revision 342455) +++ sys/netinet6/in6_var.h (working copy) @@ -760,6 +760,15 @@ return (inm); } +#include + +static inline void +hptrace(struct in6_multi *inm, const char *msg) +{ + printf("%s - inm=%p - in6m_refcount = %d\n", msg, inm, inm->in6m_refcount); + kdb_backtrace(); +} + /* Acquire an in6_multi record. */ static __inline void in6m_acquire_locked(struct in6_multi *inm) @@ -767,6 +776,7 @@ IN6_MULTI_LIST_LOCK_ASSERT(); ++inm->in6m_refcount; + hptrace(inm, "acquire_locked - post inc"); } static __inline void @@ -784,10 +794,13 @@ IN6_MULTI_LIST_LOCK_ASSERT(); if (--inm->in6m_refcount == 0) { + hptrace(inm, "rele_locked - post dec"); MPASS(inm->in6m_ifp == NULL); inm->in6m_ifma->ifma_protospec = NULL; MPASS(inm->in6m_ifma->ifma_llifma == NULL); SLIST_INSERT_HEAD(inmh, inm, in6m_nrele); + } else { + hptrace(inm, "rele_locked - post dec"); } } --- sys/netinet6/mld6.c (revision 342455) +++ sys/netinet6/mld6.c (working copy) @@ -1895,6 +1895,14 @@ error = 0; /* + * Check if the in6_multi has already been disconnected. + */ + if (inm->in6m_ifp == NULL) { + CTR3(KTR_MLD, "%s: inm is disconnected", __func__); + return (0); + } + + /* * Try to detect if the upper layer just asked us to change state * for an interface which has now gone away. */ @@ -2004,6 +2012,8 @@ if (mli->mli_version == MLD_VERSION_2 && inm->in6m_state == MLD_LEAVING_MEMBER) { inm->in6m_refcount--; + hptrace(inm, "leaving member - post dec"); + MPASS(inm->in6m_refcount > 0); } inm->in6m_state = MLD_REPORTING_MEMBER;