⬢ DragonFlyBSD Kernel Audit
DF-4305 / fix.diff
← back to finding ↓ download raw
--- 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);
 }
 
 /*