From 43f80b5f8e6c84ef9838d1bed98cf883a5999018 Mon Sep 17 00:00:00 2001
From: jhb <jhb@FreeBSD.org>
Date: Thu, 18 Aug 2005 19:10:07 +0000
Subject: [PATCH] Fixup locking and mark MPSAFE: - Add locked versions of start
 and init.  The SRM_MEDIA code in dc_init()   stayed in dc_init() instead of
 moving to dc_init_locked() to make the   locking saner. - Use
 callout_init_mtx(). - Fixup locking in detach and ioctl. - Lock the driver in
 the ifmedia callouts. - Don't recurse on the driver lock. - De-spl.

MFC after:	3 days
---
 sys/dev/dc/if_dc.c | 147 ++++++++++++++++++++++++---------------------
 sys/pci/if_dc.c    | 147 ++++++++++++++++++++++++---------------------
 2 files changed, 154 insertions(+), 140 deletions(-)

diff --git a/sys/dev/dc/if_dc.c b/sys/dev/dc/if_dc.c
index 4daa230bd602..fce20b4e4f5b 100644
--- a/sys/dev/dc/if_dc.c
+++ b/sys/dev/dc/if_dc.c
@@ -235,8 +235,10 @@ static void dc_tick(void *);
 static void dc_tx_underrun(struct dc_softc *);
 static void dc_intr(void *);
 static void dc_start(struct ifnet *);
+static void dc_start_locked(struct ifnet *);
 static int dc_ioctl(struct ifnet *, u_long, caddr_t);
 static void dc_init(void *);
+static void dc_init_locked(struct dc_softc *);
 static void dc_stop(struct dc_softc *);
 static void dc_watchdog(struct ifnet *);
 static void dc_shutdown(device_t);
@@ -343,8 +345,6 @@ DRIVER_MODULE(miibus, dc, miibus_driver, miibus_devclass, 0, 0);
 #define SIO_SET(x)	DC_SETBIT(sc, DC_SIO, (x))
 #define SIO_CLR(x)	DC_CLRBIT(sc, DC_SIO, (x))
 
-#define IS_MPSAFE 	0
-
 static void
 dc_delay(struct dc_softc *sc)
 {
@@ -670,8 +670,6 @@ dc_mii_readreg(struct dc_softc *sc, struct dc_mii_frame *frame)
 {
 	int i, ack;
 
-	DC_LOCK(sc);
-
 	/*
 	 * Set up frame for RX.
 	 */
@@ -724,8 +722,6 @@ fail:
 	dc_mii_writebit(sc, 0);
 	dc_mii_writebit(sc, 0);
 
-	DC_UNLOCK(sc);
-
 	if (ack)
 		return (1);
 	return (0);
@@ -738,7 +734,6 @@ static int
 dc_mii_writereg(struct dc_softc *sc, struct dc_mii_frame *frame)
 {
 
-	DC_LOCK(sc);
 	/*
 	 * Set up frame for TX.
 	 */
@@ -763,8 +758,6 @@ dc_mii_writereg(struct dc_softc *sc, struct dc_mii_frame *frame)
 	dc_mii_writebit(sc, 0);
 	dc_mii_writebit(sc, 0);
 
-	DC_UNLOCK(sc);
-
 	return (0);
 }
 
@@ -1844,7 +1837,7 @@ dc_attach(device_t dev)
 	sc = device_get_softc(dev);
 
 	mtx_init(&sc->dc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
-	    MTX_DEF | MTX_RECURSE);
+	    MTX_DEF);
 
 	/*
 	 * Map control/status registers.
@@ -2194,8 +2187,6 @@ dc_attach(device_t dev)
 	/* XXX: bleah, MTU gets overwritten in ether_ifattach() */
 	ifp->if_mtu = ETHERMTU;
 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
-	if (!IS_MPSAFE)
-		ifp->if_flags |= IFF_NEEDSGIANT;
 	ifp->if_ioctl = dc_ioctl;
 	ifp->if_start = dc_start;
 	ifp->if_watchdog = dc_watchdog;
@@ -2276,7 +2267,7 @@ dc_attach(device_t dev)
 #endif
 	ifp->if_capenable = ifp->if_capabilities;
 
-	callout_init(&sc->dc_stat_ch, IS_MPSAFE ? CALLOUT_MPSAFE : 0);
+	callout_init_mtx(&sc->dc_stat_ch, &sc->dc_mtx, 0);
 
 #ifdef SRM_MEDIA
 	sc->dc_srm_media = 0;
@@ -2310,8 +2301,7 @@ dc_attach(device_t dev)
 	ether_ifattach(ifp, eaddr);
 
 	/* Hook interrupt last to avoid having to lock softc */
-	error = bus_setup_intr(dev, sc->dc_irq, INTR_TYPE_NET |
-	    (IS_MPSAFE ? INTR_MPSAFE : 0),
+	error = bus_setup_intr(dev, sc->dc_irq, INTR_TYPE_NET | INTR_MPSAFE,
 	    dc_intr, sc, &sc->dc_intrhand);
 
 	if (error) {
@@ -2344,13 +2334,15 @@ dc_detach(device_t dev)
 
 	sc = device_get_softc(dev);
 	KASSERT(mtx_initialized(&sc->dc_mtx), ("dc mutex not initialized"));
-	DC_LOCK(sc);
 
 	ifp = sc->dc_ifp;
 
 	/* These should only be active if attach succeeded */
 	if (device_is_attached(dev)) {
+		DC_LOCK(sc);
 		dc_stop(sc);
+		DC_UNLOCK(sc);
+		callout_drain(&sc->dc_stat_ch);
 		ether_ifdetach(ifp);
 		if_free(ifp);
 	}
@@ -2390,7 +2382,6 @@ dc_detach(device_t dev)
 	}
 	free(sc->dc_srom, M_DEVBUF);
 
-	DC_UNLOCK(sc);
 	mtx_destroy(&sc->dc_mtx);
 
 	return (0);
@@ -2751,7 +2742,7 @@ dc_rxeof(struct dc_softc *sc)
 					DC_INC(i, DC_RX_LIST_CNT);
 					continue;
 				} else {
-					dc_init(sc);
+					dc_init_locked(sc);
 					return;
 				}
 			}
@@ -2879,7 +2870,7 @@ dc_txeof(struct dc_softc *sc)
 			if (txstat & DC_TXSTAT_LATECOLL)
 				ifp->if_collisions++;
 			if (!(txstat & DC_TXSTAT_UNDERRUN)) {
-				dc_init(sc);
+				dc_init_locked(sc);
 				return;
 			}
 		}
@@ -2918,7 +2909,7 @@ dc_tick(void *xsc)
 	u_int32_t r;
 
 	sc = xsc;
-	DC_LOCK(sc);
+	DC_LOCK_ASSERT(sc);
 	ifp = sc->dc_ifp;
 	mii = device_get_softc(sc->dc_miibus);
 
@@ -2972,15 +2963,13 @@ dc_tick(void *xsc)
 	    IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
 		sc->dc_link++;
 		if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
-			dc_start(ifp);
+			dc_start_locked(ifp);
 	}
 
 	if (sc->dc_flags & DC_21143_NWAY && !sc->dc_link)
 		callout_reset(&sc->dc_stat_ch, hz/10, dc_tick, sc);
 	else
 		callout_reset(&sc->dc_stat_ch, hz, dc_tick, sc);
-
-	DC_UNLOCK(sc);
 }
 
 /*
@@ -2994,7 +2983,7 @@ dc_tx_underrun(struct dc_softc *sc)
 	int i;
 
 	if (DC_IS_DAVICOM(sc))
-		dc_init(sc);
+		dc_init_locked(sc);
 
 	if (DC_IS_INTEL(sc)) {
 		/*
@@ -3013,7 +3002,7 @@ dc_tx_underrun(struct dc_softc *sc)
 		if (i == DC_TIMEOUT) {
 			if_printf(sc->dc_ifp,
 			    "failed to force tx to idle state\n");
-			dc_init(sc);
+			dc_init_locked(sc);
 		}
 	}
 
@@ -3055,7 +3044,7 @@ dc_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
 	dc_txeof(sc);
 	if (!IFQ_IS_EMPTY(&ifp->if_snd) &&
 	    !(ifp->if_drv_flags & IFF_DRV_OACTIVE))
-		dc_start(ifp);
+		dc_start_locked(ifp);
 
 	if (cmd == POLL_AND_CHECK_STATUS) { /* also check status register */
 		u_int32_t	status;
@@ -3088,7 +3077,7 @@ dc_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
 		if (status & DC_ISR_BUS_ERR) {
 			if_printf(ifp, "dc_poll: bus error\n");
 			dc_reset(sc);
-			dc_init(sc);
+			dc_init_locked(sc);
 		}
 	}
 	DC_UNLOCK(sc);
@@ -3175,7 +3164,7 @@ dc_intr(void *arg)
 
 		if (status & DC_ISR_BUS_ERR) {
 			dc_reset(sc);
-			dc_init(sc);
+			dc_init_locked(sc);
 		}
 	}
 
@@ -3183,7 +3172,7 @@ dc_intr(void *arg)
 	CSR_WRITE_4(sc, DC_IMR, DC_INTRS);
 
 	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
-		dc_start(ifp);
+		dc_start_locked(ifp);
 
 #ifdef DEVICE_POLLING
 done:
@@ -3310,6 +3299,17 @@ dc_encap(struct dc_softc *sc, struct mbuf **m_head)
 
 static void
 dc_start(struct ifnet *ifp)
+{
+	struct dc_softc *sc;
+
+	sc = ifp->if_softc;
+	DC_LOCK(sc);
+	dc_start_locked(ifp);
+	DC_UNLOCK(sc);
+}
+
+static void
+dc_start_locked(struct ifnet *ifp)
 {
 	struct dc_softc *sc;
 	struct mbuf *m_head = NULL, *m;
@@ -3318,17 +3318,13 @@ dc_start(struct ifnet *ifp)
 
 	sc = ifp->if_softc;
 
-	DC_LOCK(sc);
+	DC_LOCK_ASSERT(sc);
 
-	if (!sc->dc_link && ifp->if_snd.ifq_len < 10) {
-		DC_UNLOCK(sc);
+	if (!sc->dc_link && ifp->if_snd.ifq_len < 10)
 		return;
-	}
 
-	if (ifp->if_drv_flags & IFF_DRV_OACTIVE) {
-		DC_UNLOCK(sc);
+	if (ifp->if_drv_flags & IFF_DRV_OACTIVE)
 		return;
-	}
 
 	idx = sc->dc_cdata.dc_tx_first = sc->dc_cdata.dc_tx_prod;
 
@@ -3380,18 +3376,37 @@ dc_start(struct ifnet *ifp)
 		 */
 		ifp->if_timer = 5;
 	}
-
-	DC_UNLOCK(sc);
 }
 
 static void
 dc_init(void *xsc)
 {
 	struct dc_softc *sc = xsc;
+
+	DC_LOCK(sc);
+	dc_init_locked(sc);
+#ifdef SRM_MEDIA
+	if(sc->dc_srm_media) {
+		struct ifreq ifr;
+		struct mii_data *mii;
+
+		ifr.ifr_media = sc->dc_srm_media;
+		sc->dc_srm_media = 0;
+		DC_UNLOCK(sc);
+		mii = device_get_softc(sc->dc_miibus);
+		ifmedia_ioctl(sc->dc_ifp, &ifr, &mii->mii_media, SIOCSIFMEDIA);
+	} else
+#endif
+		DC_UNLOCK(sc);
+}
+
+static void
+dc_init_locked(struct dc_softc *sc)
+{
 	struct ifnet *ifp = sc->dc_ifp;
 	struct mii_data *mii;
 
-	DC_LOCK(sc);
+	DC_LOCK_ASSERT(sc);
 
 	mii = device_get_softc(sc->dc_miibus);
 
@@ -3487,7 +3502,6 @@ dc_init(void *xsc)
 		if_printf(ifp,
 		    "initialization failed: no memory for rx buffers\n");
 		dc_stop(sc);
-		DC_UNLOCK(sc);
 		return;
 	}
 
@@ -3559,17 +3573,6 @@ dc_init(void *xsc)
 		else
 			callout_reset(&sc->dc_stat_ch, hz, dc_tick, sc);
 	}
-
-#ifdef SRM_MEDIA
-	if(sc->dc_srm_media) {
-		struct ifreq ifr;
-
-		ifr.ifr_media = sc->dc_srm_media;
-		ifmedia_ioctl(ifp, &ifr, &mii->mii_media, SIOCSIFMEDIA);
-		sc->dc_srm_media = 0;
-	}
-#endif
-	DC_UNLOCK(sc);
 }
 
 /*
@@ -3584,6 +3587,7 @@ dc_ifmedia_upd(struct ifnet *ifp)
 
 	sc = ifp->if_softc;
 	mii = device_get_softc(sc->dc_miibus);
+	DC_LOCK(sc);
 	mii_mediachg(mii);
 	ifm = &mii->mii_media;
 
@@ -3592,6 +3596,7 @@ dc_ifmedia_upd(struct ifnet *ifp)
 		dc_setcfg(sc, ifm->ifm_media);
 	else
 		sc->dc_link = 0;
+	DC_UNLOCK(sc);
 
 	return (0);
 }
@@ -3608,6 +3613,7 @@ dc_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
 
 	sc = ifp->if_softc;
 	mii = device_get_softc(sc->dc_miibus);
+	DC_LOCK(sc);
 	mii_pollstat(mii);
 	ifm = &mii->mii_media;
 	if (DC_IS_DAVICOM(sc)) {
@@ -3619,6 +3625,7 @@ dc_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
 	}
 	ifmr->ifm_active = mii->mii_media_active;
 	ifmr->ifm_status = mii->mii_media_status;
+	DC_UNLOCK(sc);
 }
 
 static int
@@ -3629,10 +3636,9 @@ dc_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
 	struct mii_data *mii;
 	int error = 0;
 
-	DC_LOCK(sc);
-
 	switch (command) {
 	case SIOCSIFFLAGS:
+		DC_LOCK(sc);
 		if (ifp->if_flags & IFF_UP) {
 			int need_setfilt = (ifp->if_flags ^ sc->dc_if_flags) &
 				(IFF_PROMISC | IFF_ALLMULTI);
@@ -3642,18 +3648,21 @@ dc_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
 					dc_setfilt(sc);
 			} else {
 				sc->dc_txthresh = 0;
-				dc_init(sc);
+				dc_init_locked(sc);
 			}
 		} else {
 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
 				dc_stop(sc);
 		}
 		sc->dc_if_flags = ifp->if_flags;
+		DC_UNLOCK(sc);
 		error = 0;
 		break;
 	case SIOCADDMULTI:
 	case SIOCDELMULTI:
+		DC_LOCK(sc);
 		dc_setfilt(sc);
+		DC_UNLOCK(sc);
 		error = 0;
 		break;
 	case SIOCGIFMEDIA:
@@ -3661,21 +3670,23 @@ dc_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
 		mii = device_get_softc(sc->dc_miibus);
 		error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
 #ifdef SRM_MEDIA
+		DC_LOCK(sc);
 		if (sc->dc_srm_media)
 			sc->dc_srm_media = 0;
+		DC_UNLOCK(sc);
 #endif
 		break;
 	case SIOCSIFCAP:
+		DC_LOCK(sc);
 		ifp->if_capenable &= ~IFCAP_POLLING;
 		ifp->if_capenable |= ifr->ifr_reqcap & IFCAP_POLLING;
+		DC_UNLOCK(sc);
 		break;
 	default:
 		error = ether_ioctl(ifp, command, data);
 		break;
 	}
 
-	DC_UNLOCK(sc);
-
 	return (error);
 }
 
@@ -3693,10 +3704,10 @@ dc_watchdog(struct ifnet *ifp)
 
 	dc_stop(sc);
 	dc_reset(sc);
-	dc_init(sc);
+	dc_init_locked(sc);
 
 	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
-		dc_start(ifp);
+		dc_start_locked(ifp);
 
 	DC_UNLOCK(sc);
 }
@@ -3714,7 +3725,7 @@ dc_stop(struct dc_softc *sc)
 	int i;
 	u_int32_t ctl;
 
-	DC_LOCK(sc);
+	DC_LOCK_ASSERT(sc);
 
 	ifp = sc->dc_ifp;
 	ifp->if_timer = 0;
@@ -3762,8 +3773,6 @@ dc_stop(struct dc_softc *sc)
 		}
 	}
 	bzero(&ld->dc_tx_list, sizeof(ld->dc_tx_list));
-
-	DC_UNLOCK(sc);
 }
 
 /*
@@ -3775,15 +3784,13 @@ static int
 dc_suspend(device_t dev)
 {
 	struct dc_softc *sc;
-	int s;
-
-	s = splimp();
 
 	sc = device_get_softc(dev);
+	DC_LOCK(sc);
 	dc_stop(sc);
 	sc->suspended = 1;
+	DC_UNLOCK(sc);
 
-	splx(s);
 	return (0);
 }
 
@@ -3797,20 +3804,18 @@ dc_resume(device_t dev)
 {
 	struct dc_softc *sc;
 	struct ifnet *ifp;
-	int s;
-
-	s = splimp();
 
 	sc = device_get_softc(dev);
 	ifp = sc->dc_ifp;
 
 	/* reinitialize interface if necessary */
+	DC_LOCK(sc);
 	if (ifp->if_flags & IFF_UP)
-		dc_init(sc);
+		dc_init_locked(sc);
 
 	sc->suspended = 0;
+	DC_UNLOCK(sc);
 
-	splx(s);
 	return (0);
 }
 
@@ -3825,5 +3830,7 @@ dc_shutdown(device_t dev)
 
 	sc = device_get_softc(dev);
 
+	DC_LOCK(sc);
 	dc_stop(sc);
+	DC_UNLOCK(sc);
 }
diff --git a/sys/pci/if_dc.c b/sys/pci/if_dc.c
index 4daa230bd602..fce20b4e4f5b 100644
--- a/sys/pci/if_dc.c
+++ b/sys/pci/if_dc.c
@@ -235,8 +235,10 @@ static void dc_tick(void *);
 static void dc_tx_underrun(struct dc_softc *);
 static void dc_intr(void *);
 static void dc_start(struct ifnet *);
+static void dc_start_locked(struct ifnet *);
 static int dc_ioctl(struct ifnet *, u_long, caddr_t);
 static void dc_init(void *);
+static void dc_init_locked(struct dc_softc *);
 static void dc_stop(struct dc_softc *);
 static void dc_watchdog(struct ifnet *);
 static void dc_shutdown(device_t);
@@ -343,8 +345,6 @@ DRIVER_MODULE(miibus, dc, miibus_driver, miibus_devclass, 0, 0);
 #define SIO_SET(x)	DC_SETBIT(sc, DC_SIO, (x))
 #define SIO_CLR(x)	DC_CLRBIT(sc, DC_SIO, (x))
 
-#define IS_MPSAFE 	0
-
 static void
 dc_delay(struct dc_softc *sc)
 {
@@ -670,8 +670,6 @@ dc_mii_readreg(struct dc_softc *sc, struct dc_mii_frame *frame)
 {
 	int i, ack;
 
-	DC_LOCK(sc);
-
 	/*
 	 * Set up frame for RX.
 	 */
@@ -724,8 +722,6 @@ fail:
 	dc_mii_writebit(sc, 0);
 	dc_mii_writebit(sc, 0);
 
-	DC_UNLOCK(sc);
-
 	if (ack)
 		return (1);
 	return (0);
@@ -738,7 +734,6 @@ static int
 dc_mii_writereg(struct dc_softc *sc, struct dc_mii_frame *frame)
 {
 
-	DC_LOCK(sc);
 	/*
 	 * Set up frame for TX.
 	 */
@@ -763,8 +758,6 @@ dc_mii_writereg(struct dc_softc *sc, struct dc_mii_frame *frame)
 	dc_mii_writebit(sc, 0);
 	dc_mii_writebit(sc, 0);
 
-	DC_UNLOCK(sc);
-
 	return (0);
 }
 
@@ -1844,7 +1837,7 @@ dc_attach(device_t dev)
 	sc = device_get_softc(dev);
 
 	mtx_init(&sc->dc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
-	    MTX_DEF | MTX_RECURSE);
+	    MTX_DEF);
 
 	/*
 	 * Map control/status registers.
@@ -2194,8 +2187,6 @@ dc_attach(device_t dev)
 	/* XXX: bleah, MTU gets overwritten in ether_ifattach() */
 	ifp->if_mtu = ETHERMTU;
 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
-	if (!IS_MPSAFE)
-		ifp->if_flags |= IFF_NEEDSGIANT;
 	ifp->if_ioctl = dc_ioctl;
 	ifp->if_start = dc_start;
 	ifp->if_watchdog = dc_watchdog;
@@ -2276,7 +2267,7 @@ dc_attach(device_t dev)
 #endif
 	ifp->if_capenable = ifp->if_capabilities;
 
-	callout_init(&sc->dc_stat_ch, IS_MPSAFE ? CALLOUT_MPSAFE : 0);
+	callout_init_mtx(&sc->dc_stat_ch, &sc->dc_mtx, 0);
 
 #ifdef SRM_MEDIA
 	sc->dc_srm_media = 0;
@@ -2310,8 +2301,7 @@ dc_attach(device_t dev)
 	ether_ifattach(ifp, eaddr);
 
 	/* Hook interrupt last to avoid having to lock softc */
-	error = bus_setup_intr(dev, sc->dc_irq, INTR_TYPE_NET |
-	    (IS_MPSAFE ? INTR_MPSAFE : 0),
+	error = bus_setup_intr(dev, sc->dc_irq, INTR_TYPE_NET | INTR_MPSAFE,
 	    dc_intr, sc, &sc->dc_intrhand);
 
 	if (error) {
@@ -2344,13 +2334,15 @@ dc_detach(device_t dev)
 
 	sc = device_get_softc(dev);
 	KASSERT(mtx_initialized(&sc->dc_mtx), ("dc mutex not initialized"));
-	DC_LOCK(sc);
 
 	ifp = sc->dc_ifp;
 
 	/* These should only be active if attach succeeded */
 	if (device_is_attached(dev)) {
+		DC_LOCK(sc);
 		dc_stop(sc);
+		DC_UNLOCK(sc);
+		callout_drain(&sc->dc_stat_ch);
 		ether_ifdetach(ifp);
 		if_free(ifp);
 	}
@@ -2390,7 +2382,6 @@ dc_detach(device_t dev)
 	}
 	free(sc->dc_srom, M_DEVBUF);
 
-	DC_UNLOCK(sc);
 	mtx_destroy(&sc->dc_mtx);
 
 	return (0);
@@ -2751,7 +2742,7 @@ dc_rxeof(struct dc_softc *sc)
 					DC_INC(i, DC_RX_LIST_CNT);
 					continue;
 				} else {
-					dc_init(sc);
+					dc_init_locked(sc);
 					return;
 				}
 			}
@@ -2879,7 +2870,7 @@ dc_txeof(struct dc_softc *sc)
 			if (txstat & DC_TXSTAT_LATECOLL)
 				ifp->if_collisions++;
 			if (!(txstat & DC_TXSTAT_UNDERRUN)) {
-				dc_init(sc);
+				dc_init_locked(sc);
 				return;
 			}
 		}
@@ -2918,7 +2909,7 @@ dc_tick(void *xsc)
 	u_int32_t r;
 
 	sc = xsc;
-	DC_LOCK(sc);
+	DC_LOCK_ASSERT(sc);
 	ifp = sc->dc_ifp;
 	mii = device_get_softc(sc->dc_miibus);
 
@@ -2972,15 +2963,13 @@ dc_tick(void *xsc)
 	    IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
 		sc->dc_link++;
 		if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
-			dc_start(ifp);
+			dc_start_locked(ifp);
 	}
 
 	if (sc->dc_flags & DC_21143_NWAY && !sc->dc_link)
 		callout_reset(&sc->dc_stat_ch, hz/10, dc_tick, sc);
 	else
 		callout_reset(&sc->dc_stat_ch, hz, dc_tick, sc);
-
-	DC_UNLOCK(sc);
 }
 
 /*
@@ -2994,7 +2983,7 @@ dc_tx_underrun(struct dc_softc *sc)
 	int i;
 
 	if (DC_IS_DAVICOM(sc))
-		dc_init(sc);
+		dc_init_locked(sc);
 
 	if (DC_IS_INTEL(sc)) {
 		/*
@@ -3013,7 +3002,7 @@ dc_tx_underrun(struct dc_softc *sc)
 		if (i == DC_TIMEOUT) {
 			if_printf(sc->dc_ifp,
 			    "failed to force tx to idle state\n");
-			dc_init(sc);
+			dc_init_locked(sc);
 		}
 	}
 
@@ -3055,7 +3044,7 @@ dc_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
 	dc_txeof(sc);
 	if (!IFQ_IS_EMPTY(&ifp->if_snd) &&
 	    !(ifp->if_drv_flags & IFF_DRV_OACTIVE))
-		dc_start(ifp);
+		dc_start_locked(ifp);
 
 	if (cmd == POLL_AND_CHECK_STATUS) { /* also check status register */
 		u_int32_t	status;
@@ -3088,7 +3077,7 @@ dc_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
 		if (status & DC_ISR_BUS_ERR) {
 			if_printf(ifp, "dc_poll: bus error\n");
 			dc_reset(sc);
-			dc_init(sc);
+			dc_init_locked(sc);
 		}
 	}
 	DC_UNLOCK(sc);
@@ -3175,7 +3164,7 @@ dc_intr(void *arg)
 
 		if (status & DC_ISR_BUS_ERR) {
 			dc_reset(sc);
-			dc_init(sc);
+			dc_init_locked(sc);
 		}
 	}
 
@@ -3183,7 +3172,7 @@ dc_intr(void *arg)
 	CSR_WRITE_4(sc, DC_IMR, DC_INTRS);
 
 	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
-		dc_start(ifp);
+		dc_start_locked(ifp);
 
 #ifdef DEVICE_POLLING
 done:
@@ -3310,6 +3299,17 @@ dc_encap(struct dc_softc *sc, struct mbuf **m_head)
 
 static void
 dc_start(struct ifnet *ifp)
+{
+	struct dc_softc *sc;
+
+	sc = ifp->if_softc;
+	DC_LOCK(sc);
+	dc_start_locked(ifp);
+	DC_UNLOCK(sc);
+}
+
+static void
+dc_start_locked(struct ifnet *ifp)
 {
 	struct dc_softc *sc;
 	struct mbuf *m_head = NULL, *m;
@@ -3318,17 +3318,13 @@ dc_start(struct ifnet *ifp)
 
 	sc = ifp->if_softc;
 
-	DC_LOCK(sc);
+	DC_LOCK_ASSERT(sc);
 
-	if (!sc->dc_link && ifp->if_snd.ifq_len < 10) {
-		DC_UNLOCK(sc);
+	if (!sc->dc_link && ifp->if_snd.ifq_len < 10)
 		return;
-	}
 
-	if (ifp->if_drv_flags & IFF_DRV_OACTIVE) {
-		DC_UNLOCK(sc);
+	if (ifp->if_drv_flags & IFF_DRV_OACTIVE)
 		return;
-	}
 
 	idx = sc->dc_cdata.dc_tx_first = sc->dc_cdata.dc_tx_prod;
 
@@ -3380,18 +3376,37 @@ dc_start(struct ifnet *ifp)
 		 */
 		ifp->if_timer = 5;
 	}
-
-	DC_UNLOCK(sc);
 }
 
 static void
 dc_init(void *xsc)
 {
 	struct dc_softc *sc = xsc;
+
+	DC_LOCK(sc);
+	dc_init_locked(sc);
+#ifdef SRM_MEDIA
+	if(sc->dc_srm_media) {
+		struct ifreq ifr;
+		struct mii_data *mii;
+
+		ifr.ifr_media = sc->dc_srm_media;
+		sc->dc_srm_media = 0;
+		DC_UNLOCK(sc);
+		mii = device_get_softc(sc->dc_miibus);
+		ifmedia_ioctl(sc->dc_ifp, &ifr, &mii->mii_media, SIOCSIFMEDIA);
+	} else
+#endif
+		DC_UNLOCK(sc);
+}
+
+static void
+dc_init_locked(struct dc_softc *sc)
+{
 	struct ifnet *ifp = sc->dc_ifp;
 	struct mii_data *mii;
 
-	DC_LOCK(sc);
+	DC_LOCK_ASSERT(sc);
 
 	mii = device_get_softc(sc->dc_miibus);
 
@@ -3487,7 +3502,6 @@ dc_init(void *xsc)
 		if_printf(ifp,
 		    "initialization failed: no memory for rx buffers\n");
 		dc_stop(sc);
-		DC_UNLOCK(sc);
 		return;
 	}
 
@@ -3559,17 +3573,6 @@ dc_init(void *xsc)
 		else
 			callout_reset(&sc->dc_stat_ch, hz, dc_tick, sc);
 	}
-
-#ifdef SRM_MEDIA
-	if(sc->dc_srm_media) {
-		struct ifreq ifr;
-
-		ifr.ifr_media = sc->dc_srm_media;
-		ifmedia_ioctl(ifp, &ifr, &mii->mii_media, SIOCSIFMEDIA);
-		sc->dc_srm_media = 0;
-	}
-#endif
-	DC_UNLOCK(sc);
 }
 
 /*
@@ -3584,6 +3587,7 @@ dc_ifmedia_upd(struct ifnet *ifp)
 
 	sc = ifp->if_softc;
 	mii = device_get_softc(sc->dc_miibus);
+	DC_LOCK(sc);
 	mii_mediachg(mii);
 	ifm = &mii->mii_media;
 
@@ -3592,6 +3596,7 @@ dc_ifmedia_upd(struct ifnet *ifp)
 		dc_setcfg(sc, ifm->ifm_media);
 	else
 		sc->dc_link = 0;
+	DC_UNLOCK(sc);
 
 	return (0);
 }
@@ -3608,6 +3613,7 @@ dc_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
 
 	sc = ifp->if_softc;
 	mii = device_get_softc(sc->dc_miibus);
+	DC_LOCK(sc);
 	mii_pollstat(mii);
 	ifm = &mii->mii_media;
 	if (DC_IS_DAVICOM(sc)) {
@@ -3619,6 +3625,7 @@ dc_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
 	}
 	ifmr->ifm_active = mii->mii_media_active;
 	ifmr->ifm_status = mii->mii_media_status;
+	DC_UNLOCK(sc);
 }
 
 static int
@@ -3629,10 +3636,9 @@ dc_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
 	struct mii_data *mii;
 	int error = 0;
 
-	DC_LOCK(sc);
-
 	switch (command) {
 	case SIOCSIFFLAGS:
+		DC_LOCK(sc);
 		if (ifp->if_flags & IFF_UP) {
 			int need_setfilt = (ifp->if_flags ^ sc->dc_if_flags) &
 				(IFF_PROMISC | IFF_ALLMULTI);
@@ -3642,18 +3648,21 @@ dc_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
 					dc_setfilt(sc);
 			} else {
 				sc->dc_txthresh = 0;
-				dc_init(sc);
+				dc_init_locked(sc);
 			}
 		} else {
 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
 				dc_stop(sc);
 		}
 		sc->dc_if_flags = ifp->if_flags;
+		DC_UNLOCK(sc);
 		error = 0;
 		break;
 	case SIOCADDMULTI:
 	case SIOCDELMULTI:
+		DC_LOCK(sc);
 		dc_setfilt(sc);
+		DC_UNLOCK(sc);
 		error = 0;
 		break;
 	case SIOCGIFMEDIA:
@@ -3661,21 +3670,23 @@ dc_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
 		mii = device_get_softc(sc->dc_miibus);
 		error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
 #ifdef SRM_MEDIA
+		DC_LOCK(sc);
 		if (sc->dc_srm_media)
 			sc->dc_srm_media = 0;
+		DC_UNLOCK(sc);
 #endif
 		break;
 	case SIOCSIFCAP:
+		DC_LOCK(sc);
 		ifp->if_capenable &= ~IFCAP_POLLING;
 		ifp->if_capenable |= ifr->ifr_reqcap & IFCAP_POLLING;
+		DC_UNLOCK(sc);
 		break;
 	default:
 		error = ether_ioctl(ifp, command, data);
 		break;
 	}
 
-	DC_UNLOCK(sc);
-
 	return (error);
 }
 
@@ -3693,10 +3704,10 @@ dc_watchdog(struct ifnet *ifp)
 
 	dc_stop(sc);
 	dc_reset(sc);
-	dc_init(sc);
+	dc_init_locked(sc);
 
 	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
-		dc_start(ifp);
+		dc_start_locked(ifp);
 
 	DC_UNLOCK(sc);
 }
@@ -3714,7 +3725,7 @@ dc_stop(struct dc_softc *sc)
 	int i;
 	u_int32_t ctl;
 
-	DC_LOCK(sc);
+	DC_LOCK_ASSERT(sc);
 
 	ifp = sc->dc_ifp;
 	ifp->if_timer = 0;
@@ -3762,8 +3773,6 @@ dc_stop(struct dc_softc *sc)
 		}
 	}
 	bzero(&ld->dc_tx_list, sizeof(ld->dc_tx_list));
-
-	DC_UNLOCK(sc);
 }
 
 /*
@@ -3775,15 +3784,13 @@ static int
 dc_suspend(device_t dev)
 {
 	struct dc_softc *sc;
-	int s;
-
-	s = splimp();
 
 	sc = device_get_softc(dev);
+	DC_LOCK(sc);
 	dc_stop(sc);
 	sc->suspended = 1;
+	DC_UNLOCK(sc);
 
-	splx(s);
 	return (0);
 }
 
@@ -3797,20 +3804,18 @@ dc_resume(device_t dev)
 {
 	struct dc_softc *sc;
 	struct ifnet *ifp;
-	int s;
-
-	s = splimp();
 
 	sc = device_get_softc(dev);
 	ifp = sc->dc_ifp;
 
 	/* reinitialize interface if necessary */
+	DC_LOCK(sc);
 	if (ifp->if_flags & IFF_UP)
-		dc_init(sc);
+		dc_init_locked(sc);
 
 	sc->suspended = 0;
+	DC_UNLOCK(sc);
 
-	splx(s);
 	return (0);
 }
 
@@ -3825,5 +3830,7 @@ dc_shutdown(device_t dev)
 
 	sc = device_get_softc(dev);
 
+	DC_LOCK(sc);
 	dc_stop(sc);
+	DC_UNLOCK(sc);
 }