DF-4305 / 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 | --- 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); } /* |