DF-4341 / fix.diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | --- 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"); |