Index: sys/netpfil/ipfw/dn_sched_qfq.c =================================================================== --- sys/netpfil/ipfw/dn_sched_qfq.c +++ sys/netpfil/ipfw/dn_sched_qfq.c @@ -568,7 +568,8 @@ m = dn_dequeue(&cl->_q); if (!m) { - D("BUG/* non-workconserving leaf */"); + /* This can happen if the packet has been purged, because it's + * destined for a removed interface. */ return NULL; } NO(q->queued--;) Index: sys/netpfil/ipfw/dn_sched_wf2q.c =================================================================== --- sys/netpfil/ipfw/dn_sched_wf2q.c +++ sys/netpfil/ipfw/dn_sched_wf2q.c @@ -242,8 +242,13 @@ /* ok we have at least one eligible pkt */ q = HEAP_TOP(sch)->object; alg_fq = (struct wf2qp_queue *)q; - m = dn_dequeue(q); + m= dn_dequeue(q); heap_extract(sch, NULL); /* Remove queue from heap. */ + if (m == NULL) { + /* Packet may have been purged. */ + heap_insert(&si->idle_heap, alg_fq->F, q); + return (NULL); + } si->V += (uint64_t)(m->m_pkthdr.len) * si->inv_wsum; alg_fq->S = alg_fq->F; /* Update start time. */ if (q->mq.head == 0) { /* not backlogged any more. */ Index: sys/netpfil/ipfw/ip_dummynet.c =================================================================== --- sys/netpfil/ipfw/ip_dummynet.c +++ sys/netpfil/ipfw/ip_dummynet.c @@ -45,6 +45,7 @@ #include #include +#include #include #include #include @@ -64,6 +65,8 @@ #include /* ip_output(), IP_FORWARDING */ #include #include +#include +#include #include #include @@ -101,6 +104,8 @@ CK_LIST_HEAD(, dn_aqm) aqmlist; /* list of AQMs */ #endif +static eventhandler_tag ip_dn_detach_cookie; + static void dummynet(void *arg) { @@ -2593,6 +2598,8 @@ ip_dn_vnet_destroy(void) { DN_BH_WLOCK(); + V_dn_cfg.init_done = 0; + dummynet_flush(); DN_BH_WUNLOCK(); @@ -2603,6 +2610,114 @@ DN_LOCK_DESTROY(); } +static int +ip_dn_mq_flush_ifnet(struct mq *mq, struct ifnet *ifp) +{ + struct mbuf *m, *tmp; + struct mbuf *mprev; + int removed = 0; + + m = mq->head; + mprev = NULL; + + while (m != NULL) { + struct dn_pkt_tag *p = dn_tag_get(m); + if (p->ifp != ifp && m->m_pkthdr.rcvif != ifp) { + mprev = m; + m = m->m_nextpkt; + continue; + } + + if (mprev == NULL) { + /* We're removing the first element in the mq. */ + mq->head = m->m_nextpkt; + } else { + MPASS(mprev->m_nextpkt == m); + mprev->m_nextpkt = m->m_nextpkt; + } + tmp = m; + m = m->m_nextpkt; + + tmp->m_nextpkt = NULL; + m_freem(tmp); + + removed++; + } + + mq->tail = mprev; + + return (removed); +} + +static int +ip_dn_q_flush_ifnet(void *_q, void *_arg) +{ + struct dn_queue *q = _q; + struct ifnet *ifp = _arg; + int removed; + + removed = ip_dn_mq_flush_ifnet(&q->mq, ifp); + q->count -= removed; + + return (0); +} + +static int +ip_dn_fs_flush_ifnet(void *_fs, void *_arg) +{ + struct dn_fsk *fs = _fs; + + if (!fs->qht) + return (0); + + if (fs->fs.flags & DN_QHT_HASH) + dn_ht_scan(fs->qht, ip_dn_q_flush_ifnet, _arg); + else + ip_dn_q_flush_ifnet(fs->qht, _arg); + + return (0); +} + +static int +ip_dn_schi_flush_ifnet(void *_si, void *_arg) +{ + struct dn_sch_inst *si = _si; + struct ifnet *ifp = _arg; + + ip_dn_mq_flush_ifnet(&si->dline.mq, ifp); + + return (0); +} + +static int +ip_dn_sched_flush_ifnet(void *_s, void *_arg) +{ + struct dn_schk *s = _s; + + if (s->siht == NULL) + return (0); + + if (s->sch.flags & DN_HAVE_MASK) + dn_ht_scan(s->siht, ip_dn_schi_flush_ifnet, _arg); + else if (s->siht) + ip_dn_schi_flush_ifnet(s->siht, _arg); + + return (0); +} + +static void +ip_dn_detach_ifnet_event(void *arg __unused, struct ifnet *ifp) +{ + if (! V_dn_cfg.init_done) + return; + + DN_BH_WLOCK(); + /* Purge all queued packets for this interface. */ + dn_ht_scan(V_dn_cfg.fshash, ip_dn_fs_flush_ifnet, ifp); + dn_ht_scan(V_dn_cfg.schedhash, ip_dn_sched_flush_ifnet, ifp); + DN_BH_WUNLOCK(); +} + static void ip_dn_init(void) { @@ -2617,6 +2732,9 @@ taskqueue_thread_enqueue, &dn_tq); taskqueue_start_threads(&dn_tq, 1, PI_NET, "dummynet"); + ip_dn_detach_cookie = EVENTHANDLER_REGISTER(ifnet_departure_event, + ip_dn_detach_ifnet_event, NULL, EVENTHANDLER_PRI_ANY); + CK_LIST_INIT(&schedlist); callout_init(&dn_timeout, 1); dn_reschedule(); @@ -2635,6 +2753,8 @@ ip_dn_io_ptr = NULL; } + EVENTHANDLER_DEREGISTER(ifnet_departure_event, ip_dn_detach_cookie); + callout_drain(&dn_timeout); taskqueue_drain(dn_tq, &dn_task); taskqueue_free(dn_tq);