--- a/sys/dev/netif/mn/if_mn.c +++ b/sys/dev/netif/mn/if_mn.c @@ -238,10 +238,11 @@ enum framing {WHOKNOWS, E1, E1U, T1, T1U}; struct softc { - int unit; + int unit; device_t dev; struct resource *irq; void *intrhand; + struct lwkt_serializer slicer; /* serializes ISR vs netgraph methods */ enum framing framing; int nhooks; void *m0v, *m1v; @@ -467,15 +468,19 @@ chan = ffs(ts) - 1; else chan = 1; + lwkt_serialize_enter(&sc->slicer); if (!sc->ch[chan]) mn_create_channel(sc, chan); - else if (sc->ch[chan]->state == UP) + else if (sc->ch[chan]->state == UP) { + lwkt_serialize_exit(&sc->slicer); return (EBUSY); + } sc->ch[chan]->ts = ts; sc->ch[chan]->hook = hook; sc->ch[chan]->tx_limit = nbit * 8; hook->private = sc->ch[chan]; sc->nhooks++; + lwkt_serialize_exit(&sc->slicer); return(0); } @@ -579,16 +584,20 @@ struct softc *sc; int chan, pitch, len; + /* mn_intr may be walking/freeing the very ring we append to */ sch = hook->private; sc = sch->sc; + lwkt_serialize_enter(&sc->slicer); chan = sch->chan; if (sch->state != UP) { NG_FREE_DATA(m, meta); + lwkt_serialize_exit(&sc->slicer); return (0); } if (sch->tx_pending + m->m_pkthdr.len > sch->tx_limit * mn_maxlatency) { NG_FREE_DATA(m, meta); + lwkt_serialize_exit(&sc->slicer); return (0); } NG_FREE_META(meta); @@ -641,6 +650,7 @@ sch->tx_pending += m->m_pkthdr.len; sc->m32x->txpoll &= ~(1 << chan); } + lwkt_serialize_exit(&sc->slicer); return (0); } @@ -657,12 +667,15 @@ struct schan *sch; u_int32_t u; + /* serialize against mn_intr, which starts walking these rings as + * soon as the channel comes up below */ sch = hook->private; - chan = sch->chan; sc = sch->sc; + lwkt_serialize_enter(&sc->slicer); + chan = sch->chan; if (sch->state == UP) - return (0); + goto out; sch->state = UP; /* Count and configure the timeslots for this channel */ @@ -734,6 +747,8 @@ kprintf("%s: init chan %d stat %08x\n", sc->name, chan, u); sc->m32x->stat = 1; +out: + lwkt_serialize_exit(&sc->slicer); return (0); } @@ -749,12 +764,16 @@ struct trxd *dp, *dp2; u_int32_t u; + /* serialize against mn_intr / mn_rx_intr / mn_tx_intr */ sch = hook->private; - chan = sch->chan; sc = sch->sc; + lwkt_serialize_enter(&sc->slicer); + chan = sch->chan; - if (sch->state == DOWN) + if (sch->state == DOWN) { + lwkt_serialize_exit(&sc->slicer); return (0); + } sch->state = DOWN; /* Set receiver & transmitter off */ @@ -769,10 +788,24 @@ /* Initialize this channel */ sc->m32_mem.ccb = 0x00008000 + (chan << 8); sc->m32x->cmd = 0x1; - DELAY(30); - u = sc->m32x->stat; - if (!(u & 1)) - kprintf("%s: zap chan %d stat %08x\n", sc->name, chan, u); + /* Poll the channel-zap to completion; never free DMA targets under + * a still-armed MUNICH32X (device write-after-free otherwise). */ + for (i = 0; i < 100; i++) { + DELAY(30); + u = sc->m32x->stat; + if (u & 1) + break; + } + if (!(u & 1)) { + kprintf("%s: zap chan %d FAILED stat %08x, leaking rings\n", + sc->name, chan, u); + /* Keep the descriptors + mbufs allocated: the device may + * still own them. Channel is DOWN so nothing else touches + * them; this leaks a bounded number of descriptors only on + * real hardware failure. */ + lwkt_serialize_exit(&sc->slicer); + return (0); + } sc->m32x->stat = 1; /* Free all receive descriptors and mbufs */ @@ -792,6 +825,7 @@ sc->ch[chan]->x1 = dp2 = dp->vnext; mn_free_desc(dp); } + lwkt_serialize_exit(&sc->slicer); sc->nhooks--; return(0); } @@ -1297,6 +1331,7 @@ sc->unit = device_get_unit(self); sc->framing = E1; ksprintf(sc->name, "mn%d", sc->unit); + lwkt_serialize_init(&sc->slicer); rid = PCIR_MAPS; res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); @@ -1327,7 +1362,7 @@ } error = bus_setup_intr(self, sc->irq, INTR_MPSAFE, mn_intr, sc, - &sc->intrhand, NULL); + &sc->intrhand, &sc->slicer); if (error) { kprintf("couldn't set up irq\n");