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
This commit is contained in:
Matt Macy 2018-05-06 20:34:13 +00:00
parent 7edd877a1e
commit b6f6f88018
10 changed files with 195 additions and 84 deletions

View File

@ -254,7 +254,6 @@ struct mbuf *(*tbr_dequeue_ptr)(struct ifaltq *, int) = NULL;
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, struct sockaddr *llsa,
* 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 ifmultiaddr *ifma, int detaching)
}
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 ifmultiaddr *ifma, int detaching)
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 ifmultiaddr *ifma, int detaching)
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.

View File

@ -596,6 +596,12 @@ VNET_DECLARE(struct ifnet *, loif); /* first loopback interface */
#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);

View File

@ -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);

View File

@ -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);

View File

@ -112,6 +112,8 @@ MTX_SYSINIT(in_multi_free_mtx, &in_multi_free_mtx, "in_multi_free_mtx", MTX_DEF)
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:
@ -251,6 +253,33 @@ inm_release_list_deferred(struct in_multi_head *inmh)
GROUPTASK_ENQUEUE(&free_gtask);
}
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)
{
@ -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_addr *gina,
}
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_addr *gina,
} else {
*pinm = inm;
}
IN_MULTI_LIST_UNLOCK();
return (error);
}
@ -1350,7 +1382,9 @@ in_leavegroup_locked(struct in_multi *inm, /*const*/ struct in_mfilter *imf)
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)

View File

@ -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_multi *inm)
if (--inm->inm_refcount == 0) {
MPASS(inmh != NULL);
inm_disconnect(inm);
inm->inm_ifma->ifma_protospec = NULL;
SLIST_INSERT_HEAD(inmh, inm, inm_nrele);
}

View File

@ -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();

View File

@ -190,6 +190,7 @@ 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.
@ -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*/ struct in6_mfilter *imf)
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);

View File

@ -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);
}
}

View File

@ -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_hdr *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 version)
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);
}