--- a/sys/dev/netif/bfe/if_bfe.c +++ b/sys/dev/netif/bfe/if_bfe.c @@ -93,6 +93,7 @@ static void bfe_start(struct ifnet *, struct ifaltq_subque *); static int bfe_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *); static void bfe_init(void *); +static void bfe_init_waitok(struct bfe_softc *, int); static void bfe_stop(struct bfe_softc *); static void bfe_watchdog(struct ifnet *); static void bfe_shutdown(device_t); @@ -536,12 +537,12 @@ } static int -bfe_list_rx_init(struct bfe_softc *sc) +bfe_list_rx_init(struct bfe_softc *sc, int waitok) { int i, error; for (i = 0; i < BFE_RX_LIST_CNT; i++) { - error = bfe_newbuf(sc, i, 1); + error = bfe_newbuf(sc, i, waitok); if (error) return(error); } @@ -554,7 +555,7 @@ } static int -bfe_newbuf(struct bfe_softc *sc, int c, int init) +bfe_newbuf(struct bfe_softc *sc, int c, int waitok) { struct bfe_data *r; bus_dmamap_t map; @@ -562,7 +563,8 @@ struct mbuf *m; int error, nsegs; - m = m_getcl(init ? M_WAITOK : M_NOWAIT, MT_DATA, M_PKTHDR); + /* Never sleep when reached from interrupt context. */ + m = m_getcl(waitok ? M_WAITOK : M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) return ENOBUFS; m->m_len = m->m_pkthdr.len = MCLBYTES; @@ -572,7 +574,7 @@ &seg, 1, &nsegs, BUS_DMA_NOWAIT); if (error) { m_freem(m); - if (init) + if (waitok) if_printf(&sc->arpcom.ac_if, "can't load RX mbuf\n"); return error; } @@ -1183,7 +1185,7 @@ IFNET_STAT_INC(ifp, ierrors, 1); ifp->if_flags &= ~IFF_RUNNING; - bfe_init(sc); + bfe_init_waitok(sc, 0); /* interrupt context: no sleeping */ } /* A packet was received */ @@ -1354,6 +1356,12 @@ static void bfe_init(void *xsc) { + bfe_init_waitok(xsc, 1); +} + +static void +bfe_init_waitok(void *xsc, int waitok) +{ struct bfe_softc *sc = (struct bfe_softc*)xsc; struct ifnet *ifp = &sc->arpcom.ac_if; @@ -1365,7 +1373,7 @@ bfe_stop(sc); bfe_chip_reset(sc); - if (bfe_list_rx_init(sc) == ENOBUFS) { + if (bfe_list_rx_init(sc, waitok) == ENOBUFS) { if_printf(ifp, "bfe_init failed. " " Not enough memory for list buffers\n"); bfe_stop(sc); @@ -1477,7 +1485,7 @@ if_printf(ifp, "watchdog timeout -- resetting\n"); ifp->if_flags &= ~IFF_RUNNING; - bfe_init(sc); + bfe_init_waitok(sc, 0); /* netisr context: no sleeping */ IFNET_STAT_INC(ifp, oerrors, 1); } @@ -1491,21 +1499,30 @@ mii = device_get_softc(sc->bfe_miibus); - lwkt_serialize_enter(ifp->if_serializer); - - bfe_stats_update(sc); - callout_reset(&sc->bfe_stat_timer, hz, bfe_tick, sc); + /* + * Never block on the serializer here. bfe_stop() tears this + * callout down with callout_stop() while holding the same + * serializer, and callout_stop() synchronously waits for an + * in-progress callback (kern_timeout.c "costp" wait). A + * callback blocked in lwkt_serialize_enter() therefore + * deadlocks the softclock thread against the serializer + * holder (AB-BA). Use try-enter and retry on the next tick. + */ + if (lwkt_serialize_try(ifp->if_serializer)) { + bfe_stats_update(sc); - if (sc->bfe_link == 0) { - mii_tick(mii); - if (!sc->bfe_link && mii->mii_media_status & IFM_ACTIVE && - IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { - sc->bfe_link++; + if (sc->bfe_link == 0) { + mii_tick(mii); + if (!sc->bfe_link && mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + sc->bfe_link++; + } + if (!sc->bfe_link) + sc->bfe_link++; } - if (!sc->bfe_link) - sc->bfe_link++; + lwkt_serialize_exit(ifp->if_serializer); } - lwkt_serialize_exit(ifp->if_serializer); + callout_reset(&sc->bfe_stat_timer, hz, bfe_tick, sc); } /*