DF-4497 / fix.diff
--- a/sys/dev/netif/sbsh/if_sbsh.c +++ b/sys/dev/netif/sbsh/if_sbsh.c @@ -608,6 +608,10 @@ sbsh_watchdog(struct ifnet *ifp) static void resume_tx(struct sbsh_softc *sc) { u_int32_t cur_tbd = sc->regs->CTDR; + if (sc->state != ACTIVE) + return; + while (cur_tbd != sc->regs->LTDR && (sc->tbd[cur_tbd++].length & LAST_FRAG) == 0) ; @@ -726,6 +730,10 @@ static void free_sent_buffers(struct sbsh_softc *sc) { u_int32_t cur_tbd; + if (sc->state != ACTIVE) + return; + cur_tbd = sc->regs->CTDR; while (sc->head_tdesc != cur_tbd) { @@ -790,6 +798,10 @@ static void indicate_frames(struct sbsh_softc *sc) { struct ifnet *ifp = &sc->arpcom.ac_if; unsigned cur_rbd = sc->regs->CRDR & 0x7f; + if (sc->state != ACTIVE) + return; + while (sc->head_rdesc != cur_rbd) { struct mbuf *m = sc->rq[sc->head_rq++]; sc->head_rq &= (RQLEN - 1); @@ -811,12 +823,16 @@ static void drop_queues(struct sbsh_softc *sc) { while (sc->head_rq != sc->tail_rq) { - m_freem(sc->rq[sc->head_rq++]); + m_freem(sc->rq[sc->head_rq]); + sc->rq[sc->head_rq] = NULL; + sc->head_rq = (sc->head_rq + 1) & (RQLEN - 1); sc->head_rq &= (RQLEN - 1); } while (sc->head_xq != sc->tail_xq) { - m_freem(sc->xq[sc->head_xq++]); + m_freem(sc->xq[sc->head_xq]); + sc->xq[sc->head_xq] = NULL; + sc->head_xq = (sc->head_xq + 1) & (XQLEN - 1); sc->head_xq &= (XQLEN - 1); } } @@ -849,10 +865,14 @@ static void deactivate(struct sbsh_softc *sc) { + /* Drain any TXS/RXS/UFL already latched in SR: once the queues are + * dropped below, free_sent_buffers()/indicate_frames()/resume_tx() + * must not run against stale xq/rq pointers (they are also guarded + * by sc->state, this covers a deferred ISR racing this function). */ + sc->regs->SR = TXS | RXS | UFL | CRC | OFL; sc->regs->CR &= ~(RXEN | TXEN); sc->regs->CRB |= RXDE; sc->regs->IMR = EXT; sc->regs->CTDR = sc->regs->LTDR; sc->regs->CRDR = sc->regs->LRDR; + sc->head_tdesc = sc->regs->CTDR & 0x7f; + sc->head_rdesc = sc->regs->CRDR & 0x7f; sc->state = ACTIVATION; drop_queues(sc); |