From owner-svn-src-head@freebsd.org Sun May 6 20:34:15 2018 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 5EA96FC57B7; Sun, 6 May 2018 20:34:15 +0000 (UTC) (envelope-from mmacy@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 10E6B7573E; Sun, 6 May 2018 20:34:15 +0000 (UTC) (envelope-from mmacy@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 08B8C119F4; Sun, 6 May 2018 20:34:15 +0000 (UTC) (envelope-from mmacy@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id w46KYEKn088158; Sun, 6 May 2018 20:34:14 GMT (envelope-from mmacy@FreeBSD.org) Received: (from mmacy@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id w46KYDUY088148; Sun, 6 May 2018 20:34:13 GMT (envelope-from mmacy@FreeBSD.org) Message-Id: <201805062034.w46KYDUY088148@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: mmacy set sender to mmacy@FreeBSD.org using -f From: Matt Macy Date: Sun, 6 May 2018 20:34:13 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r333309 - in head/sys: net netinet netinet6 X-SVN-Group: head X-SVN-Commit-Author: mmacy X-SVN-Commit-Paths: in head/sys: net netinet netinet6 X-SVN-Commit-Revision: 333309 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.25 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 06 May 2018 20:34:15 -0000 Author: mmacy Date: Sun May 6 20:34:13 2018 New Revision: 333309 URL: https://svnweb.freebsd.org/changeset/base/333309 Log: r333175 introduced deferred deletion of multicast addresses in order to permit the driver ioctl to sleep on commands to the NIC when updating multicast filters. More generally this permitted driver's to use an sx as a softc lock. Unfortunately this change introduced a race whereby a a multicast update would still be queued for deletion when ifconfig deleted the interface thus calling down in to _purgemaddrs and synchronously deleting _all_ of the multicast addresses on the interface. Synchronously remove all external references to a multicast address before enqueueing for delete. Reported by: lwhsu Approved by: sbruno Modified: head/sys/net/if.c head/sys/net/if_var.h head/sys/netinet/igmp.c head/sys/netinet/in.c head/sys/netinet/in_mcast.c head/sys/netinet/in_var.h head/sys/netinet6/in6_ifattach.c head/sys/netinet6/in6_mcast.c head/sys/netinet6/in6_var.h head/sys/netinet6/mld6.c Modified: head/sys/net/if.c ============================================================================== --- head/sys/net/if.c Sun May 6 20:32:47 2018 (r333308) +++ head/sys/net/if.c Sun May 6 20:34:13 2018 (r333309) @@ -254,7 +254,6 @@ struct mbuf *(*tbr_dequeue_ptr)(struct ifaltq *, int) static void if_attachdomain(void *); static void if_attachdomain1(struct ifnet *); static int ifconf(u_long, caddr_t); -static void if_freemulti(struct ifmultiaddr *); static void if_grow(void); static void if_input_default(struct ifnet *, struct mbuf *); static int if_requestencap_default(struct ifnet *, struct if_encap_req *); @@ -3395,7 +3394,10 @@ if_allocmulti(struct ifnet *ifp, struct sockaddr *sa, * counting, notifying the driver, handling routing messages, and releasing * any dependent link layer state. */ -static void +#ifdef MCAST_VERBOSE +extern void kdb_backtrace(void); +#endif +void if_freemulti(struct ifmultiaddr *ifma) { @@ -3404,6 +3406,10 @@ if_freemulti(struct ifmultiaddr *ifma) if (ifma->ifma_lladdr != NULL) free(ifma->ifma_lladdr, M_IFMADDR); +#ifdef MCAST_VERBOSE + kdb_backtrace(); + printf("%s freeing ifma: %p\n", __func__, ifma); +#endif free(ifma->ifma_addr, M_IFMADDR); free(ifma, M_IFMADDR); } @@ -3610,6 +3616,12 @@ if_delallmulti(struct ifnet *ifp) IF_ADDR_WUNLOCK(ifp); } +void +if_delmulti_ifma(struct ifmultiaddr *ifma) +{ + if_delmulti_ifma_flags(ifma, 0); +} + /* * Delete a multicast group membership by group membership pointer. * Network-layer protocol domains must use this routine. @@ -3617,11 +3629,11 @@ if_delallmulti(struct ifnet *ifp) * It is safe to call this routine if the ifp disappeared. */ void -if_delmulti_ifma(struct ifmultiaddr *ifma) +if_delmulti_ifma_flags(struct ifmultiaddr *ifma, int flags) { struct ifnet *ifp; int lastref; - + MCDPRINTF("%s freeing ifma: %p\n", __func__, ifma); #ifdef INET IN_MULTI_LIST_UNLOCK_ASSERT(); #endif @@ -3649,7 +3661,7 @@ if_delmulti_ifma(struct ifmultiaddr *ifma) if (ifp != NULL) IF_ADDR_WLOCK(ifp); - lastref = if_delmulti_locked(ifp, ifma, 0); + lastref = if_delmulti_locked(ifp, ifma, flags); if (ifp != NULL) { /* @@ -3683,6 +3695,7 @@ if_delmulti_locked(struct ifnet *ifp, struct ifmultiad } ifp = ifma->ifma_ifp; + MCDPRINTF("%s freeing %p from %s \n", __func__, ifma, ifp ? ifp->if_xname : ""); /* * If the ifnet is detaching, null out references to ifnet, @@ -3708,6 +3721,9 @@ if_delmulti_locked(struct ifnet *ifp, struct ifmultiad if (--ifma->ifma_refcount > 0) return 0; + if (ifp != NULL && detaching == 0) + TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link); + /* * If this ifma is a network-layer ifma, a link-layer ifma may * have been associated with it. Release it first if so. @@ -3726,11 +3742,15 @@ if_delmulti_locked(struct ifnet *ifp, struct ifmultiad if_freemulti(ll_ifma); } } - if (ifp != NULL && detaching == 0) - TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link); +#ifdef INVARIANTS + if (ifp) { + struct ifmultiaddr *ifmatmp; + TAILQ_FOREACH(ifmatmp, &ifp->if_multiaddrs, ifma_link) + MPASS(ifma != ifmatmp); + } +#endif if_freemulti(ifma); - /* * The last reference to this instance of struct ifmultiaddr * was released; the hardware should be notified of this change. Modified: head/sys/net/if_var.h ============================================================================== --- head/sys/net/if_var.h Sun May 6 20:32:47 2018 (r333308) +++ head/sys/net/if_var.h Sun May 6 20:34:13 2018 (r333309) @@ -596,6 +596,12 @@ VNET_DECLARE(struct ifnet *, loif); /* first loopback #define V_if_index VNET(if_index) #define V_loif VNET(loif) +#ifdef MCAST_VERBOSE +#define MCDPRINTF printf +#else +#define MCDPRINTF(...) +#endif + int if_addgroup(struct ifnet *, const char *); int if_delgroup(struct ifnet *, const char *); int if_addmulti(struct ifnet *, struct sockaddr *, struct ifmultiaddr **); @@ -605,12 +611,14 @@ void if_attach(struct ifnet *); void if_dead(struct ifnet *); int if_delmulti(struct ifnet *, struct sockaddr *); void if_delmulti_ifma(struct ifmultiaddr *); +void if_delmulti_ifma_flags(struct ifmultiaddr *, int flags); void if_detach(struct ifnet *); void if_purgeaddrs(struct ifnet *); void if_delallmulti(struct ifnet *); void if_down(struct ifnet *); struct ifmultiaddr * if_findmulti(struct ifnet *, const struct sockaddr *); +void if_freemulti(struct ifmultiaddr *ifma); void if_free(struct ifnet *); void if_initname(struct ifnet *, const char *, int); void if_link_state_change(struct ifnet *, int); Modified: head/sys/netinet/igmp.c ============================================================================== --- head/sys/netinet/igmp.c Sun May 6 20:32:47 2018 (r333308) +++ head/sys/netinet/igmp.c Sun May 6 20:34:13 2018 (r333309) @@ -610,7 +610,7 @@ void igmp_ifdetach(struct ifnet *ifp) { struct igmp_ifsoftc *igi; - struct ifmultiaddr *ifma; + struct ifmultiaddr *ifma, *next; struct in_multi *inm; struct in_multi_head inm_free_tmp; CTR3(KTR_IGMPV3, "%s: called for ifp %p(%s)", __func__, ifp, @@ -621,21 +621,22 @@ igmp_ifdetach(struct ifnet *ifp) igi = ((struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp; if (igi->igi_version == IGMP_VERSION_3) { - IF_ADDR_RLOCK(ifp); - TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + IF_ADDR_WLOCK(ifp); + restart: + TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) { if (ifma->ifma_addr->sa_family != AF_INET || ifma->ifma_protospec == NULL) continue; -#if 0 - KASSERT(ifma->ifma_protospec != NULL, - ("%s: ifma_protospec is NULL", __func__)); -#endif inm = (struct in_multi *)ifma->ifma_protospec; if (inm->inm_state == IGMP_LEAVING_MEMBER) inm_rele_locked(&inm_free_tmp, inm); inm_clear_recorded(inm); + if (__predict_false(ifma_restart)) { + ifma_restart = false; + goto restart; + } } - IF_ADDR_RUNLOCK(ifp); + IF_ADDR_WUNLOCK(ifp); inm_release_list_deferred(&inm_free_tmp); } IGMP_UNLOCK(); @@ -1631,7 +1632,7 @@ igmp_fasttimo_vnet(void) struct mbufq qrq; /* Query response packets */ struct ifnet *ifp; struct igmp_ifsoftc *igi; - struct ifmultiaddr *ifma; + struct ifmultiaddr *ifma, *next; struct in_multi *inm; struct in_multi_head inm_free_tmp; int loop, uri_fasthz; @@ -1695,8 +1696,9 @@ igmp_fasttimo_vnet(void) mbufq_init(&scq, IGMP_MAX_STATE_CHANGE_PACKETS); } - IF_ADDR_RLOCK(ifp); - TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + IF_ADDR_WLOCK(ifp); + restart: + TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) { if (ifma->ifma_addr->sa_family != AF_INET || ifma->ifma_protospec == NULL) continue; @@ -1712,8 +1714,12 @@ igmp_fasttimo_vnet(void) &scq, inm, uri_fasthz); break; } + if (__predict_false(ifma_restart)) { + ifma_restart = false; + goto restart; + } } - IF_ADDR_RUNLOCK(ifp); + IF_ADDR_WUNLOCK(ifp); if (igi->igi_version == IGMP_VERSION_3) { igmp_dispatch_queue(&qrq, 0, loop); Modified: head/sys/netinet/in.c ============================================================================== --- head/sys/netinet/in.c Sun May 6 20:32:47 2018 (r333308) +++ head/sys/netinet/in.c Sun May 6 20:34:13 2018 (r333309) @@ -1012,7 +1012,7 @@ in_purgemaddrs(struct ifnet *ifp) { struct in_multi_head purgeinms; struct in_multi *inm; - struct ifmultiaddr *ifma; + struct ifmultiaddr *ifma, *next; SLIST_INIT(&purgeinms); IN_MULTI_LIST_LOCK(); @@ -1023,19 +1023,20 @@ in_purgemaddrs(struct ifnet *ifp) * We need to do this as IF_ADDR_LOCK() may be re-acquired * by code further down. */ - IF_ADDR_RLOCK(ifp); - TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + IF_ADDR_WLOCK(ifp); + restart: + TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) { if (ifma->ifma_addr->sa_family != AF_INET || ifma->ifma_protospec == NULL) continue; -#if 0 - KASSERT(ifma->ifma_protospec != NULL, - ("%s: ifma_protospec is NULL", __func__)); -#endif inm = (struct in_multi *)ifma->ifma_protospec; inm_rele_locked(&purgeinms, inm); + if (__predict_false(ifma_restart)) { + ifma_restart = true; + goto restart; + } } - IF_ADDR_RUNLOCK(ifp); + IF_ADDR_WUNLOCK(ifp); inm_release_list_deferred(&purgeinms); igmp_ifdetach(ifp); Modified: head/sys/netinet/in_mcast.c ============================================================================== --- head/sys/netinet/in_mcast.c Sun May 6 20:32:47 2018 (r333308) +++ head/sys/netinet/in_mcast.c Sun May 6 20:34:13 2018 (r333309) @@ -112,6 +112,8 @@ MTX_SYSINIT(in_multi_free_mtx, &in_multi_free_mtx, "in struct sx in_multi_sx; SX_SYSINIT(in_multi_sx, &in_multi_sx, "in_multi_sx"); +int ifma_restart; + /* * Functions with non-static linkage defined in this file should be * declared in in_var.h: @@ -252,6 +254,33 @@ inm_release_list_deferred(struct in_multi_head *inmh) } void +inm_disconnect(struct in_multi *inm) +{ + struct ifnet *ifp; + struct ifmultiaddr *ifma, *ll_ifma; + + ifp = inm->inm_ifp; + IF_ADDR_WLOCK_ASSERT(ifp); + ifma = inm->inm_ifma; + + if_ref(ifp); + TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link); + MCDPRINTF("removed ifma: %p from %s\n", ifma, ifp->if_xname); + if ((ll_ifma = ifma->ifma_llifma) != NULL) { + MPASS(ifma != ll_ifma); + ifma->ifma_llifma = NULL; + MPASS(ll_ifma->ifma_llifma == NULL); + MPASS(ll_ifma->ifma_ifp == ifp); + if (--ll_ifma->ifma_refcount == 0) { + TAILQ_REMOVE(&ifp->if_multiaddrs, ll_ifma, ifma_link); + MCDPRINTF("removed ll_ifma: %p from %s\n", ll_ifma, ifp->if_xname); + if_freemulti(ll_ifma); + ifma_restart = true; + } + } +} + +void inm_release_deferred(struct in_multi *inm) { struct in_multi_head tmp; @@ -260,6 +289,7 @@ inm_release_deferred(struct in_multi *inm) MPASS(inm->inm_refcount > 0); if (--inm->inm_refcount == 0) { SLIST_INIT(&tmp); + inm_disconnect(inm); inm->inm_ifma->ifma_protospec = NULL; SLIST_INSERT_HEAD(&tmp, inm, inm_nrele); inm_release_list_deferred(&tmp); @@ -643,9 +673,11 @@ inm_release(struct in_multi *inm) inm_purge(inm); free(inm, M_IPMADDR); - if_delmulti_ifma(ifma); - if (ifp) + if_delmulti_ifma_flags(ifma, 1); + if (ifp) { CURVNET_RESTORE(); + if_rele(ifp); + } } /* @@ -1270,7 +1302,6 @@ in_joingroup_locked(struct ifnet *ifp, const struct in } out_inm_release: - IN_MULTI_LIST_UNLOCK(); if (error) { CTR2(KTR_IGMPV3, "%s: dropping ref on %p", __func__, inm); @@ -1278,6 +1309,7 @@ in_joingroup_locked(struct ifnet *ifp, const struct in } else { *pinm = inm; } + IN_MULTI_LIST_UNLOCK(); return (error); } @@ -1350,7 +1382,9 @@ in_leavegroup_locked(struct in_multi *inm, /*const*/ s CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); CURVNET_SET(inm->inm_ifp->if_vnet); error = igmp_change_state(inm); + IF_ADDR_WLOCK(inm->inm_ifp); inm_release_deferred(inm); + IF_ADDR_WUNLOCK(inm->inm_ifp); IN_MULTI_LIST_UNLOCK(); CURVNET_RESTORE(); if (error) Modified: head/sys/netinet/in_var.h ============================================================================== --- head/sys/netinet/in_var.h Sun May 6 20:32:47 2018 (r333308) +++ head/sys/netinet/in_var.h Sun May 6 20:34:13 2018 (r333309) @@ -343,6 +343,9 @@ extern struct sx in_multi_sx; #define IN_MULTI_LOCK_ASSERT() sx_assert(&in_multi_sx, SA_XLOCKED) #define IN_MULTI_UNLOCK_ASSERT() sx_assert(&in_multi_sx, SA_XUNLOCKED) +void inm_disconnect(struct in_multi *inm); +extern int ifma_restart; + /* Acquire an in_multi record. */ static __inline void inm_acquire_locked(struct in_multi *inm) @@ -368,6 +371,7 @@ inm_rele_locked(struct in_multi_head *inmh, struct in_ if (--inm->inm_refcount == 0) { MPASS(inmh != NULL); + inm_disconnect(inm); inm->inm_ifma->ifma_protospec = NULL; SLIST_INSERT_HEAD(inmh, inm, inm_nrele); } Modified: head/sys/netinet6/in6_ifattach.c ============================================================================== --- head/sys/netinet6/in6_ifattach.c Sun May 6 20:32:47 2018 (r333308) +++ head/sys/netinet6/in6_ifattach.c Sun May 6 20:34:13 2018 (r333309) @@ -848,26 +848,29 @@ in6_purgemaddrs(struct ifnet *ifp) { struct in6_multi_head purgeinms; struct in6_multi *inm; - struct ifmultiaddr *ifma; + struct ifmultiaddr *ifma, *next; SLIST_INIT(&purgeinms); IN6_MULTI_LOCK(); IN6_MULTI_LIST_LOCK(); + IF_ADDR_WLOCK(ifp); /* * Extract list of in6_multi associated with the detaching ifp * which the PF_INET6 layer is about to release. - * We need to do this as IF_ADDR_LOCK() may be re-acquired - * by code further down. */ - IF_ADDR_RLOCK(ifp); - TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + restart: + TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) { if (ifma->ifma_addr->sa_family != AF_INET6 || ifma->ifma_protospec == NULL) continue; inm = (struct in6_multi *)ifma->ifma_protospec; in6m_rele_locked(&purgeinms, inm); + if (__predict_false(ifma6_restart)) { + ifma6_restart = false; + goto restart; + } } - IF_ADDR_RUNLOCK(ifp); + IF_ADDR_WUNLOCK(ifp); mld_ifdetach(ifp); IN6_MULTI_LIST_UNLOCK(); IN6_MULTI_UNLOCK(); Modified: head/sys/netinet6/in6_mcast.c ============================================================================== --- head/sys/netinet6/in6_mcast.c Sun May 6 20:32:47 2018 (r333308) +++ head/sys/netinet6/in6_mcast.c Sun May 6 20:34:13 2018 (r333309) @@ -190,6 +190,7 @@ static SYSCTL_NODE(_net_inet6_ip6_mcast, OID_AUTO, fil 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. @@ -531,6 +532,7 @@ in6m_release(struct in6_multi *inm) ifma = inm->in6m_ifma; ifp = inm->in6m_ifp; + MPASS(ifma->ifma_llifma == NULL); /* XXX this access is not covered by IF_ADDR_LOCK */ CTR2(KTR_MLD, "%s: purging ifma %p", __func__, ifma); @@ -542,9 +544,11 @@ in6m_release(struct in6_multi *inm) in6m_purge(inm); free(inm, M_IP6MADDR); - if_delmulti_ifma(ifma); - if (ifp) + if_delmulti_ifma_flags(ifma, 1); + if (ifp) { CURVNET_RESTORE(); + if_rele(ifp); + } } static struct grouptask free_gtask; @@ -572,30 +576,58 @@ in6m_release_list_deferred(struct in6_multi_head *inmh } void -in6m_release_deferred(struct in6_multi *inm) +in6m_disconnect(struct in6_multi *inm) { - struct in6_multi_head tmp; struct ifnet *ifp; struct ifaddr *ifa; struct in6_ifaddr *ifa6; struct in6_multi_mship *imm; + struct ifmultiaddr *ifma, *ll_ifma; + ifp = inm->in6m_ifp; + IF_ADDR_WLOCK_ASSERT(ifp); + ifma = inm->in6m_ifma; + + if_ref(ifp); + TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link); + MCDPRINTF("removed ifma: %p from %s\n", ifma, ifp->if_xname); + if ((ll_ifma = ifma->ifma_llifma) != NULL) { + MPASS(ifma != ll_ifma); + ifma->ifma_llifma = NULL; + MPASS(ll_ifma->ifma_llifma == NULL); + MPASS(ll_ifma->ifma_ifp == ifp); + if (--ll_ifma->ifma_refcount == 0) { + ifma6_restart = true; + TAILQ_REMOVE(&ifp->if_multiaddrs, ll_ifma, ifma_link); + MCDPRINTF("removed ll_ifma: %p from %s\n", ll_ifma, ifp->if_xname); + if_freemulti(ll_ifma); + } + } + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + ifa6 = (void *)ifa; + LIST_FOREACH(imm, &ifa6->ia6_memberships, i6mm_chain) { + if (inm == imm->i6mm_maddr) { + LIST_REMOVE(imm, i6mm_chain); + free(imm, M_IP6MADDR); + } + } + } +} + +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) { - ifp = inm->in6m_ifp; - IF_ADDR_LOCK_ASSERT(ifp); - TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { - if (ifa->ifa_addr->sa_family != AF_INET6) - continue; - ifa6 = (void *)ifa; - LIST_FOREACH(imm, &ifa6->ia6_memberships, i6mm_chain) { - if (inm == imm->i6mm_maddr) - imm->i6mm_maddr = NULL; - } - } + in6m_disconnect(inm); 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); } @@ -1355,10 +1387,10 @@ in6_leavegroup_locked(struct in6_multi *inm, /*const*/ CTR2(KTR_MLD, "%s: dropping ref on %p", __func__, inm); if (ifp) - IF_ADDR_RLOCK(ifp); + IF_ADDR_WLOCK(ifp); in6m_release_deferred(inm); if (ifp) - IF_ADDR_RUNLOCK(ifp); + IF_ADDR_WUNLOCK(ifp); IN6_MULTI_LIST_UNLOCK(); return (error); Modified: head/sys/netinet6/in6_var.h ============================================================================== --- head/sys/netinet6/in6_var.h Sun May 6 20:32:47 2018 (r333308) +++ head/sys/netinet6/in6_var.h Sun May 6 20:34:13 2018 (r333309) @@ -670,6 +670,8 @@ struct in6_multi { } in6m_st[2]; /* state at t0, t1 */ }; +void in6m_disconnect(struct in6_multi *inm); +extern int ifma6_restart; /* * Helper function to derive the filter mode on a source entry * from its internal counters. Predicates are: @@ -711,6 +713,7 @@ extern struct sx in6_multi_sx; #define IN6_MULTI_LOCK_ASSERT() sx_assert(&in6_multi_sx, SA_XLOCKED) #define IN6_MULTI_UNLOCK_ASSERT() sx_assert(&in6_multi_sx, SA_XUNLOCKED) + /* * Look up an in6_multi record for an IPv6 multicast address * on the interface ifp. @@ -779,28 +782,13 @@ in6m_acquire(struct in6_multi *inm) static __inline void in6m_rele_locked(struct in6_multi_head *inmh, struct in6_multi *inm) { - struct ifnet *ifp; - struct ifaddr *ifa; - struct in6_ifaddr *ifa6; - struct in6_multi_mship *imm; - KASSERT(inm->in6m_refcount > 0, ("refcount == %d inm: %p", inm->in6m_refcount, inm)); IN6_MULTI_LIST_LOCK_ASSERT(); if (--inm->in6m_refcount == 0) { - ifp = inm->in6m_ifp; - IF_ADDR_LOCK_ASSERT(ifp); - TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { - if (ifa->ifa_addr->sa_family != AF_INET6) - continue; - - ifa6 = (void *)ifa; - LIST_FOREACH(imm, &ifa6->ia6_memberships, i6mm_chain) { - if (inm == imm->i6mm_maddr) - imm->i6mm_maddr = NULL; - } - } + in6m_disconnect(inm); inm->in6m_ifma->ifma_protospec = NULL; + MPASS(inm->in6m_ifma->ifma_llifma == NULL); SLIST_INSERT_HEAD(inmh, inm, in6m_nrele); } } Modified: head/sys/netinet6/mld6.c ============================================================================== --- head/sys/netinet6/mld6.c Sun May 6 20:32:47 2018 (r333308) +++ head/sys/netinet6/mld6.c Sun May 6 20:34:13 2018 (r333309) @@ -536,7 +536,7 @@ void mld_ifdetach(struct ifnet *ifp) { struct mld_ifsoftc *mli; - struct ifmultiaddr *ifma; + struct ifmultiaddr *ifma, *next; struct in6_multi *inm; struct in6_multi_head inmh; @@ -549,8 +549,9 @@ mld_ifdetach(struct ifnet *ifp) mli = MLD_IFINFO(ifp); if (mli->mli_version == MLD_VERSION_2) { - IF_ADDR_RLOCK(ifp); - TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + IF_ADDR_WLOCK(ifp); + restart: + TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) { if (ifma->ifma_addr->sa_family != AF_INET6 || ifma->ifma_protospec == NULL) continue; @@ -560,8 +561,12 @@ mld_ifdetach(struct ifnet *ifp) ifma->ifma_protospec = NULL; } in6m_clear_recorded(inm); + if (__predict_false(ifma6_restart)) { + ifma6_restart = false; + goto restart; + } } - IF_ADDR_RUNLOCK(ifp); + IF_ADDR_WUNLOCK(ifp); } MLD_UNLOCK(); @@ -696,7 +701,7 @@ mld_v1_input_query(struct ifnet *ifp, const struct ip6 * interface, kick the report timer. */ CTR2(KTR_MLD, "process v1 general query on ifp %p(%s)", - ifp, if_name(ifp)); + ifp, if_name(ifp)); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_INET6 || ifma->ifma_protospec == NULL) @@ -1326,7 +1331,7 @@ mld_fasttimo_vnet(void) struct mbufq qrq; /* Query response packets */ struct ifnet *ifp; struct mld_ifsoftc *mli; - struct ifmultiaddr *ifma; + struct ifmultiaddr *ifma, *next; struct in6_multi *inm, *tinm; struct in6_multi_head inmh; int uri_fasthz; @@ -1388,8 +1393,9 @@ mld_fasttimo_vnet(void) mbufq_init(&scq, MLD_MAX_STATE_CHANGE_PACKETS); } - IF_ADDR_RLOCK(ifp); - TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + IF_ADDR_WLOCK(ifp); + restart: + TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) { if (ifma->ifma_addr->sa_family != AF_INET6 || ifma->ifma_protospec == NULL) continue; @@ -1403,8 +1409,12 @@ mld_fasttimo_vnet(void) &scq, inm, uri_fasthz); break; } + if (__predict_false(ifma6_restart)) { + ifma6_restart = false; + goto restart; + } } - IF_ADDR_RUNLOCK(ifp); + IF_ADDR_WUNLOCK(ifp); switch (mli->mli_version) { case MLD_VERSION_1: @@ -1641,7 +1651,7 @@ mld_set_version(struct mld_ifsoftc *mli, const int ver static void mld_v2_cancel_link_timers(struct mld_ifsoftc *mli) { - struct ifmultiaddr *ifma; + struct ifmultiaddr *ifma, *next; struct ifnet *ifp; struct in6_multi *inm; struct in6_multi_head inmh; @@ -1666,8 +1676,9 @@ mld_v2_cancel_link_timers(struct mld_ifsoftc *mli) ifp = mli->mli_ifp; - IF_ADDR_RLOCK(ifp); - TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + IF_ADDR_WLOCK(ifp); + restart: + TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) { if (ifma->ifma_addr->sa_family != AF_INET6) continue; inm = (struct in6_multi *)ifma->ifma_protospec; @@ -1702,8 +1713,12 @@ mld_v2_cancel_link_timers(struct mld_ifsoftc *mli) mbufq_drain(&inm->in6m_scq); break; } + if (__predict_false(ifma6_restart)) { + ifma6_restart = false; + goto restart; + } } - IF_ADDR_RUNLOCK(ifp); + IF_ADDR_WUNLOCK(ifp); in6m_release_list_deferred(&inmh); }