tech-net archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

NET_MPSAFE support for ifmedia / mii



This patch makes the ifmedia and mii layers MP-safe.

The basic approach is that the ifmedia / mii layers share the "core mutex" with the hardware driver.  The main reason for this is that the ifmedia and mii layers can be entered from "both directions" (the user-facing side, e.g. ioctl, and the driver-facing side, e.g. interrupt handler or (*if_init)() routine).  By making them share this lock, the locking protocol becomes very simple.

The main upshot is that essentially all of the ifmedia / mii functions that are called from core hardware driver routines are called with the lock held.  I would say the majority of the patch is actually just adding assertions that this is the case in a bunch of places.  The changes in the rest of the patch fall into 3 basic categories:

1- Changes to take and release the lock in the appropriate locations, which required a small amount of refactoring to ensure that the lock is not held while allocating memory or otherwise sleeping.

2- Some code to serialize probing the MII interface -- this is needed because there are several places during this process where the lock is released.

3- Some code to handle drivers that haven't converted to the new locking scheme.  Basically, if a driver is not MP-safe, it doesn't have a "core mutex".  To reduce the number of places in the code that have to handle this situation, I allocate a mutex object myself in this case, and a select few of the entry points from both directions check for this "legacy support" mutex and will acquire it on behalf of the driver so that the locking assertions everywhere else remain true.  Alas, some of these entry points can be called in a nested fashion (by design), which isn't a problem when the hardware driver provides a "core mutex" and properly uses it, but in the legacy case, it means that we need to support acquiring it recursively.  Once all drivers are converted to the new way (which will eventually happen or the driver will be deleted), this legacy support stuff can (and will) be garbage-collected.

Patch attached contains the changes to the shared code plus an example of an MP-safe driver converted to use the new ifmedia locking scheme (if_wm.c).  As you can see, conversion of an MP-safe driver to use the new mechanism is completely trivial -- one just needs to make sure the driver's mutex is initialized before calling the new ifmedia_init_with_lock() function (instead of ifmedia_init()).

Please provide feedback on the changes.  I still plan to update some more comments, and do some additional testing, before checking this in, but I would like some eyeballs on this now.

Thanks!

Index: dev/mii/acphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/acphy.c,v
retrieving revision 1.29
diff -u -p -r1.29 acphy.c
--- dev/mii/acphy.c	27 Nov 2019 10:19:20 -0000	1.29
+++ dev/mii/acphy.c	6 Feb 2020 00:10:01 -0000
@@ -111,17 +111,23 @@ acphyattach(device_t parent, device_t se
 	sc->mii_pdata = mii;
 	sc->mii_flags = ma->mii_flags;
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	/* XXX Check MCR_FX_SEL to set MIIF_HAVE_FIBER? */
 
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
 	sc->mii_capabilities &= ma->mii_capmask;
-	aprint_normal_dev(sc->mii_dev, "");
+
+	mii_unlock(mii);
 
 #define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
 	if (sc->mii_flags & MIIF_HAVEFIBER) {
+		aprint_normal_dev(sc->mii_dev, "");
+		mii_lock(mii);
 		sc->mii_anegticks = MII_ANEGTICKS;
+		mii_unlock(mii);
 		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_FX, 0, sc->mii_inst),
 		    MII_MEDIA_100_TX);
 		aprint_normal("100baseFX, ");
@@ -140,6 +146,8 @@ acphy_service(struct mii_softc *sc, stru
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -194,6 +202,8 @@ acphy_status(struct mii_softc *sc)
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmsr, bmcr, dr;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
Index: dev/mii/amhphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/amhphy.c,v
retrieving revision 1.25
diff -u -p -r1.25 amhphy.c
--- dev/mii/amhphy.c	27 Nov 2019 10:19:20 -0000	1.25
+++ dev/mii/amhphy.c	6 Feb 2020 00:10:01 -0000
@@ -106,11 +106,15 @@ amhphyattach(device_t parent, device_t s
 	sc->mii_pdata = mii;
 	sc->mii_flags = ma->mii_flags;
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
 	sc->mii_capabilities &= ma->mii_capmask;
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 }
 
@@ -120,6 +124,8 @@ amhphy_service(struct mii_softc *sc, str
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -174,6 +180,8 @@ amhphy_status(struct mii_softc *sc)
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmsr, bmcr, ssr;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
Index: dev/mii/atphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/atphy.c,v
retrieving revision 1.27
diff -u -p -r1.27 atphy.c
--- dev/mii/atphy.c	13 Dec 2019 08:30:26 -0000	1.27
+++ dev/mii/atphy.c	6 Feb 2020 00:10:01 -0000
@@ -201,6 +201,8 @@ atphy_attach(device_t parent, device_t s
 	if (asc->mii_clk_25m != 0)
 		atphy_clk_25m(asc);
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	PHY_READ(sc, MII_BMSR, &bmsr);
@@ -209,6 +211,8 @@ atphy_attach(device_t parent, device_t s
 	if (atphy_is_gige(mpd) && (sc->mii_capabilities & BMSR_EXTSTAT))
 		PHY_READ(sc, MII_EXTSR, &sc->mii_extcapabilities);
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 }
 
@@ -218,6 +222,8 @@ atphy_service(struct mii_softc *sc, stru
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t anar, bmcr, bmsr;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -339,6 +345,8 @@ atphy_status(struct mii_softc *sc)
 	struct mii_data *mii = sc->mii_pdata;
 	uint16_t bmsr, bmcr, gsr, ssr;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
@@ -405,6 +413,8 @@ atphy_reset(struct mii_softc *sc)
 	uint16_t reg;
 	int i;
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	/* Take PHY out of power down mode. */
 	PHY_WRITE(sc, 29, 0x29);
 	PHY_WRITE(sc, 30, 0);
@@ -449,6 +459,8 @@ atphy_mii_phy_auto(struct mii_softc *sc)
 {
 	uint16_t anar;
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	sc->mii_ticks = 0;
 	anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA;
 	if (sc->mii_flags & MIIF_DOPAUSE)
Index: dev/mii/bmtphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/bmtphy.c,v
retrieving revision 1.36
diff -u -p -r1.36 bmtphy.c
--- dev/mii/bmtphy.c	27 Nov 2019 10:19:20 -0000	1.36
+++ dev/mii/bmtphy.c	6 Feb 2020 00:10:01 -0000
@@ -135,6 +135,8 @@ bmtphyattach(device_t parent, device_t s
 	sc->mii_pdata = mii;
 	sc->mii_flags = ma->mii_flags;
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	/* XXX Check AUX_STS_FX_MODE to set MIIF_HAVE_FIBER? */
@@ -142,6 +144,8 @@ bmtphyattach(device_t parent, device_t s
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
 	sc->mii_capabilities &= ma->mii_capmask;
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 }
 
@@ -151,6 +155,8 @@ bmtphy_service(struct mii_softc *sc, str
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -205,6 +211,8 @@ bmtphy_status(struct mii_softc *sc)
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmsr, bmcr, aux_csr;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
@@ -256,6 +264,8 @@ bmtphy_reset(struct mii_softc *sc)
 {
 	uint16_t data;
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	mii_phy_reset(sc);
 
 	if (sc->mii_mpd_model == MII_MODEL_xxBROADCOM_BCM5221) {
Index: dev/mii/brgphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/brgphy.c,v
retrieving revision 1.86
diff -u -p -r1.86 brgphy.c
--- dev/mii/brgphy.c	27 Nov 2019 10:19:20 -0000	1.86
+++ dev/mii/brgphy.c	6 Feb 2020 00:10:01 -0000
@@ -263,6 +263,8 @@ brgphyattach(device_t parent, device_t s
 	} else
 		sc->mii_funcs = &brgphy_copper_funcs;
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
@@ -270,7 +272,10 @@ brgphyattach(device_t parent, device_t s
 	if (sc->mii_capabilities & BMSR_EXTSTAT)
 		PHY_READ(sc, MII_EXTSR, &sc->mii_extcapabilities);
 
+	mii_unlock(mii);
+
 	if (sc->mii_flags & MIIF_HAVEFIBER) {
+		mii_lock(mii);
 		sc->mii_flags |= MIIF_NOISOLATE | MIIF_NOLOOP;
 
 		/*
@@ -280,6 +285,7 @@ brgphyattach(device_t parent, device_t s
 		sc->mii_capabilities |= BMSR_ANEG;
 		sc->mii_capabilities &= ~BMSR_100T4;
 		sc->mii_extcapabilities |= EXTSR_1000XFDX;
+		mii_unlock(mii);
 
 		if (bsc->sc_isbnx) {
 			/*
@@ -305,6 +311,8 @@ brgphy_service(struct mii_softc *sc, str
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg, speed, gig;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -467,6 +475,8 @@ brgphy_copper_status(struct mii_softc *s
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmcr, bmsr, auxsts, gtsr;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
@@ -552,6 +562,8 @@ brgphy_fiber_status(struct mii_softc *sc
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmcr, bmsr, anar, anlpar, result;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
@@ -595,6 +607,8 @@ brgphy_5708s_status(struct mii_softc *sc
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmcr, bmsr;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
@@ -664,6 +678,8 @@ brgphy_5709s_status(struct mii_softc *sc
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmcr, bmsr, auxsts;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
@@ -730,11 +746,13 @@ brgphy_5709s_status(struct mii_softc *sc
 		mii->mii_media_active = ife->ifm_media;
 }
 
-int
+static int
 brgphy_mii_phy_auto(struct mii_softc *sc)
 {
 	uint16_t anar, ktcr = 0;
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	sc->mii_ticks = 0;
 	brgphy_loop(sc);
 	PHY_RESET(sc);
@@ -762,12 +780,14 @@ brgphy_mii_phy_auto(struct mii_softc *sc
 	return EJUSTRETURN;
 }
 
-void
+static void
 brgphy_loop(struct mii_softc *sc)
 {
 	uint16_t bmsr;
 	int i;
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	PHY_WRITE(sc, MII_BMCR, BMCR_LOOP);
 	for (i = 0; i < 15000; i++) {
 		PHY_READ(sc, MII_BMSR, &bmsr);
@@ -783,6 +803,8 @@ brgphy_reset(struct mii_softc *sc)
 	struct brgphy_softc *bsc = device_private(sc->mii_dev);
 	uint16_t reg;
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	mii_phy_reset(sc);
 	switch (sc->mii_mpd_oui) {
 	case MII_OUI_BROADCOM:
@@ -1060,7 +1082,7 @@ brgphy_bcm5411_dspcode(struct mii_softc 
 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
 }
 
-void
+static void
 brgphy_bcm5421_dspcode(struct mii_softc *sc)
 {
 	uint16_t data;
@@ -1079,7 +1101,7 @@ brgphy_bcm5421_dspcode(struct mii_softc 
 	PHY_WRITE(sc, BRGPHY_MII_DSP_RW_PORT, data | 0x0200);
 }
 
-void
+static void
 brgphy_bcm54k2_dspcode(struct mii_softc *sc)
 {
 	static const struct {
@@ -1158,7 +1180,7 @@ brgphy_ber_bug(struct mii_softc *sc)
 }
 
 /* BCM5701 A0/B0 CRC bug workaround */
-void
+static void
 brgphy_crc_bug(struct mii_softc *sc)
 {
 	static const struct {
Index: dev/mii/ciphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/ciphy.c,v
retrieving revision 1.38
diff -u -p -r1.38 ciphy.c
--- dev/mii/ciphy.c	27 Nov 2019 10:19:20 -0000	1.38
+++ dev/mii/ciphy.c	6 Feb 2020 00:10:01 -0000
@@ -119,6 +119,8 @@ ciphyattach(device_t parent, device_t se
 	sc->mii_flags = ma->mii_flags;
 	sc->mii_flags |= MIIF_NOISOLATE;
 
+	mii_lock(mii);
+
 	ciphy_reset(sc);
 
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
@@ -126,6 +128,8 @@ ciphyattach(device_t parent, device_t se
 	if (sc->mii_capabilities & BMSR_EXTSTAT)
 		PHY_READ(sc, MII_EXTSR, &sc->mii_extcapabilities);
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 }
 
@@ -135,6 +139,8 @@ ciphy_service(struct mii_softc *sc, stru
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg, speed, gig;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -293,6 +299,8 @@ ciphy_status(struct mii_softc *sc)
 	struct mii_data *mii = sc->mii_pdata;
 	uint16_t bmsr, bmcr, gtsr;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
@@ -348,6 +356,8 @@ static void
 ciphy_reset(struct mii_softc *sc)
 {
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	mii_phy_reset(sc);
 	DELAY(1000);
 }
Index: dev/mii/dmphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/dmphy.c,v
retrieving revision 1.44
diff -u -p -r1.44 dmphy.c
--- dev/mii/dmphy.c	27 Nov 2019 10:19:20 -0000	1.44
+++ dev/mii/dmphy.c	6 Feb 2020 00:10:01 -0000
@@ -132,11 +132,15 @@ dmphyattach(device_t parent, device_t se
 	sc->mii_pdata = mii;
 	sc->mii_flags = ma->mii_flags;
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
 	sc->mii_capabilities &= ma->mii_capmask;
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 }
 
@@ -146,6 +150,8 @@ dmphy_service(struct mii_softc *sc, stru
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -200,6 +206,8 @@ dmphy_status(struct mii_softc *sc)
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmsr, bmcr, dscsr;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
Index: dev/mii/etphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/etphy.c,v
retrieving revision 1.7
diff -u -p -r1.7 etphy.c
--- dev/mii/etphy.c	27 Nov 2019 10:19:20 -0000	1.7
+++ dev/mii/etphy.c	6 Feb 2020 00:10:01 -0000
@@ -166,6 +166,8 @@ etphy_attach(device_t parent, device_t s
 
 	sc->mii_flags |= MIIF_NOISOLATE | MIIF_NOLOOP;
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
@@ -176,6 +178,8 @@ etphy_attach(device_t parent, device_t s
 		sc->mii_extcapabilities &= ~EXTSR_1000THDX;
 	}
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 }
 
@@ -185,6 +189,8 @@ etphy_service(struct mii_softc *sc, stru
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmcr;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -252,6 +258,8 @@ etphy_reset(struct mii_softc *sc)
 	uint16_t reg;
 	int i;
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	if (sc->mii_mpd_model == MII_MODEL_AGERE_ET1011) {
 		mii_phy_reset(sc);
 		return;
@@ -304,6 +312,8 @@ etphy_status(struct mii_softc *sc)
 	struct mii_data *mii = sc->mii_pdata;
 	uint16_t bmsr, bmcr, sr;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
Index: dev/mii/exphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/exphy.c,v
retrieving revision 1.57
diff -u -p -r1.57 exphy.c
--- dev/mii/exphy.c	27 Nov 2019 10:19:20 -0000	1.57
+++ dev/mii/exphy.c	6 Feb 2020 00:10:01 -0000
@@ -136,11 +136,15 @@ exphyattach(device_t parent, device_t se
 	}
 	sc->mii_flags |= MIIF_NOISOLATE;
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
 	sc->mii_capabilities &= ma->mii_capmask;
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 }
 
@@ -153,6 +157,8 @@ exphy_service(struct mii_softc *sc, stru
 	if (IFM_INST(ife->ifm_media) != sc->mii_inst)
 		panic("exphy_service: can't isolate 3Com PHY");
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		break;
@@ -191,6 +197,8 @@ static void
 exphy_reset(struct mii_softc *sc)
 {
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	mii_phy_reset(sc);
 
 	/*
Index: dev/mii/gentbi.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/gentbi.c,v
retrieving revision 1.30
diff -u -p -r1.30 gentbi.c
--- dev/mii/gentbi.c	27 Nov 2019 10:19:20 -0000	1.30
+++ dev/mii/gentbi.c	6 Feb 2020 00:10:01 -0000
@@ -105,14 +105,20 @@ gentbimatch(device_t parent, cfdata_t ma
 	 *	- There is no media in the BMSR.
 	 *	- EXTSR has only 1000X.
 	 */
+	mii_lock(mii);
 	rv = (*mii->mii_readreg)(parent, ma->mii_phyno, MII_BMSR, &bmsr);
 	if ((rv != 0)
-	    || (bmsr & BMSR_EXTSTAT) == 0 || (bmsr & BMSR_MEDIAMASK) != 0)
+	    || (bmsr & BMSR_EXTSTAT) == 0 || (bmsr & BMSR_MEDIAMASK) != 0) {
+		mii_unlock(mii);
 		return 0;
+	}
 
 	rv = (*mii->mii_readreg)(parent, ma->mii_phyno, MII_EXTSR, &extsr);
-	if ((rv != 0) || ((extsr & (EXTSR_1000TFDX | EXTSR_1000THDX)) != 0))
+	if ((rv != 0) || ((extsr & (EXTSR_1000TFDX | EXTSR_1000THDX)) != 0)) {
+		mii_unlock(mii);
 		return 0;
+	}
+	mii_unlock(mii);
 
 	if (extsr & (EXTSR_1000XFDX | EXTSR_1000XHDX)) {
 		/*
@@ -156,6 +162,8 @@ gentbiattach(device_t parent, device_t s
 	sc->mii_pdata = mii;
 	sc->mii_flags = ma->mii_flags;
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	/*
@@ -168,6 +176,8 @@ gentbiattach(device_t parent, device_t s
 	if (sc->mii_capabilities & BMSR_EXTSTAT)
 		PHY_READ(sc, MII_EXTSR, &sc->mii_extcapabilities);
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 }
 
@@ -177,6 +187,8 @@ gentbi_service(struct mii_softc *sc, str
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -231,6 +243,8 @@ gentbi_status(struct mii_softc *sc)
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmsr, bmcr, anlpar;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
Index: dev/mii/glxtphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/glxtphy.c,v
retrieving revision 1.31
diff -u -p -r1.31 glxtphy.c
--- dev/mii/glxtphy.c	27 Nov 2019 10:19:20 -0000	1.31
+++ dev/mii/glxtphy.c	6 Feb 2020 00:10:01 -0000
@@ -126,6 +126,8 @@ glxtphyattach(device_t parent, device_t 
 	sc->mii_pdata = mii;
 	sc->mii_flags = ma->mii_flags;
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
@@ -133,6 +135,8 @@ glxtphyattach(device_t parent, device_t 
 	if (sc->mii_capabilities & BMSR_EXTSTAT)
 		PHY_READ(sc, MII_EXTSR, &sc->mii_extcapabilities);
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 }
 
@@ -142,6 +146,8 @@ glxtphy_service(struct mii_softc *sc, st
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -196,6 +202,8 @@ glxtphy_status(struct mii_softc *sc)
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmcr, qsr, gtsr;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
Index: dev/mii/gphyter.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/gphyter.c,v
retrieving revision 1.36
diff -u -p -r1.36 gphyter.c
--- dev/mii/gphyter.c	27 Nov 2019 10:19:20 -0000	1.36
+++ dev/mii/gphyter.c	6 Feb 2020 00:10:01 -0000
@@ -136,6 +136,8 @@ gphyterattach(device_t parent, device_t 
 	sc->mii_pdata = mii;
 	sc->mii_flags = ma->mii_flags;
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
@@ -155,9 +157,13 @@ gphyterattach(device_t parent, device_t 
 	if (anar & ANAR_10_FD)
 		sc->mii_capabilities |= (BMSR_10TFDX & ma->mii_capmask);
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 
+	mii_lock(mii);
 	PHY_READ(sc, MII_GPHYTER_STRAP, &strap);
+	mii_unlock(mii);
 	aprint_normal_dev(self, "strapped to %s mode",
 	    (strap & STRAP_MS_VAL) ? "master" : "slave");
 	if (strap & STRAP_NC_MODE)
@@ -171,6 +177,8 @@ gphyter_service(struct mii_softc *sc, st
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -225,6 +233,8 @@ gphyter_status(struct mii_softc *sc)
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmsr, bmcr, physup, gtsr;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
@@ -292,6 +302,8 @@ gphyter_reset(struct mii_softc *sc)
 	int i;
 	uint16_t reg;
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	if (sc->mii_flags & MIIF_NOISOLATE)
 		reg = BMCR_RESET;
 	else
Index: dev/mii/icsphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/icsphy.c,v
retrieving revision 1.55
diff -u -p -r1.55 icsphy.c
--- dev/mii/icsphy.c	27 Nov 2019 10:19:20 -0000	1.55
+++ dev/mii/icsphy.c	6 Feb 2020 00:10:01 -0000
@@ -131,11 +131,15 @@ icsphyattach(device_t parent, device_t s
 	sc->mii_pdata = mii;
 	sc->mii_flags = ma->mii_flags;
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
 	sc->mii_capabilities &= ma->mii_capmask;
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 }
 
@@ -145,6 +149,8 @@ icsphy_service(struct mii_softc *sc, str
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -199,6 +205,8 @@ icsphy_status(struct mii_softc *sc)
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmcr, qpr;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
@@ -246,6 +254,8 @@ static void
 icsphy_reset(struct mii_softc *sc)
 {
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	mii_phy_reset(sc);
 	/* Set powerdown feature */
 	switch (sc->mii_mpd_model) {
Index: dev/mii/igphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/igphy.c,v
retrieving revision 1.32
diff -u -p -r1.32 igphy.c
--- dev/mii/igphy.c	27 Nov 2019 10:19:20 -0000	1.32
+++ dev/mii/igphy.c	6 Feb 2020 00:10:01 -0000
@@ -158,6 +158,8 @@ igphyattach(device_t parent, device_t se
 	sc->mii_pdata = mii;
 	sc->mii_flags = ma->mii_flags;
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
@@ -165,6 +167,8 @@ igphyattach(device_t parent, device_t se
 	if (sc->mii_capabilities & BMSR_EXTSTAT)
 		PHY_READ(sc, MII_EXTSR, &sc->mii_extcapabilities);
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 }
 
@@ -297,6 +301,8 @@ igphy_reset(struct mii_softc *sc)
 	struct igphy_softc *igsc = (struct igphy_softc *)sc;
 	uint16_t fused, fine, coarse;
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	mii_phy_reset(sc);
 	delay(150);
 
@@ -349,6 +355,8 @@ igphy_service(struct mii_softc *sc, stru
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -416,6 +424,8 @@ igphy_status(struct mii_softc *sc)
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmcr, pssr, gtsr, bmsr;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
Index: dev/mii/ihphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/ihphy.c,v
retrieving revision 1.16
diff -u -p -r1.16 ihphy.c
--- dev/mii/ihphy.c	27 Nov 2019 10:19:20 -0000	1.16
+++ dev/mii/ihphy.c	6 Feb 2020 00:10:01 -0000
@@ -136,6 +136,8 @@ ihphyattach(device_t parent, device_t se
 	sc->mii_pdata = mii;
 	sc->mii_flags = ma->mii_flags;
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
@@ -143,13 +145,17 @@ ihphyattach(device_t parent, device_t se
 	if (sc->mii_capabilities & BMSR_EXTSTAT)
 		PHY_READ(sc, MII_EXTSR, &sc->mii_extcapabilities);
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 
+	mii_lock(mii);
 	/* Link setup (as done by Intel's Linux driver for the 82577). */
 	PHY_READ(sc, IHPHY_MII_CFG, &reg);
 	reg |= IHPHY_CFG_TX_CRS;
 	reg |= IHPHY_CFG_DOWN_SHIFT;
 	PHY_WRITE(sc, IHPHY_MII_CFG, reg);
+	mii_unlock(mii);
 }
 
 static int
@@ -158,6 +164,8 @@ ihphy_service(struct mii_softc *sc, stru
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -222,6 +230,8 @@ ihphy_status(struct mii_softc *sc)
 	struct mii_data *mii = sc->mii_pdata;
 	uint16_t esr, bmcr, gtsr;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
@@ -283,6 +293,8 @@ ihphy_reset(struct mii_softc *sc)
 	int i;
 	uint16_t reg;
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_ISO);
 
 	/* Wait another 100ms for it to complete. */
Index: dev/mii/ikphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/ikphy.c,v
retrieving revision 1.18
diff -u -p -r1.18 ikphy.c
--- dev/mii/ikphy.c	27 Nov 2019 10:19:20 -0000	1.18
+++ dev/mii/ikphy.c	6 Feb 2020 00:10:01 -0000
@@ -129,6 +129,8 @@ ikphyattach(device_t parent, device_t se
 	sc->mii_pdata = mii;
 	sc->mii_flags = ma->mii_flags;
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
@@ -136,6 +138,8 @@ ikphyattach(device_t parent, device_t se
 	if (sc->mii_capabilities & BMSR_EXTSTAT)
 		PHY_READ(sc, MII_EXTSR, &sc->mii_extcapabilities);
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 }
 
@@ -145,6 +149,8 @@ ikphy_service(struct mii_softc *sc, stru
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -199,6 +205,8 @@ ikphy_setmedia(struct mii_softc *sc)
 	struct mii_data *mii = sc->mii_pdata;
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 
+	KASSERT(mii_locked(mii));
+
 	/* Enable CRS on TX for half-duplex operation. */
 	PHY_READ(sc, GG82563_PHY_MAC_SPEC_CTRL, &phy_data);
 	phy_data |= GG82563_MSCR_ASSERT_CRS_ON_TX;
@@ -276,6 +284,8 @@ ikphy_status(struct mii_softc *sc)
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t pssr, bmcr, gtsr, kmrn;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
Index: dev/mii/inphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/inphy.c,v
retrieving revision 1.59
diff -u -p -r1.59 inphy.c
--- dev/mii/inphy.c	27 Nov 2019 10:19:20 -0000	1.59
+++ dev/mii/inphy.c	6 Feb 2020 00:10:01 -0000
@@ -133,11 +133,15 @@ inphyattach(device_t parent, device_t se
 	sc->mii_pdata = mii;
 	sc->mii_flags = ma->mii_flags;
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
 	sc->mii_capabilities &= ma->mii_capmask;
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 }
 
@@ -147,6 +151,8 @@ inphy_service(struct mii_softc *sc, stru
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -201,6 +207,8 @@ inphy_status(struct mii_softc *sc)
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmsr, bmcr, scr;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
Index: dev/mii/iophy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/iophy.c,v
retrieving revision 1.42
diff -u -p -r1.42 iophy.c
--- dev/mii/iophy.c	27 Nov 2019 10:19:20 -0000	1.42
+++ dev/mii/iophy.c	6 Feb 2020 00:10:01 -0000
@@ -125,11 +125,15 @@ iophyattach(device_t parent, device_t se
 	sc->mii_pdata = mii;
 	sc->mii_flags = ma->mii_flags;
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
 	sc->mii_capabilities &= ma->mii_capmask;
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 }
 
@@ -139,6 +143,8 @@ iophy_service(struct mii_softc *sc, stru
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -193,6 +199,8 @@ iophy_status(struct mii_softc *sc)
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmsr, bmcr, ext0;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
Index: dev/mii/ipgphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/ipgphy.c,v
retrieving revision 1.9
diff -u -p -r1.9 ipgphy.c
--- dev/mii/ipgphy.c	14 Jan 2020 09:49:26 -0000	1.9
+++ dev/mii/ipgphy.c	6 Feb 2020 00:10:01 -0000
@@ -121,6 +121,8 @@ ipgphy_attach(device_t parent, device_t 
 		    &isc->need_loaddspcode);
 	}
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
@@ -129,6 +131,8 @@ ipgphy_attach(device_t parent, device_t 
 	if (sc->mii_capabilities & BMSR_EXTSTAT)
 		PHY_READ(sc, MII_EXTSR, &sc->mii_extcapabilities);
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 }
 
@@ -138,6 +142,8 @@ ipgphy_service(struct mii_softc *sc, str
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg, speed;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -254,6 +260,8 @@ ipgphy_status(struct mii_softc *sc)
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmsr, bmcr, stat, gtsr;
 
+	KASSERT(mii_locked(mii));
+
 	/* For IP1000A, use generic way */
 	if (sc->mii_mpd_model == MII_MODEL_xxICPLUS_IP1000A) {
 		ukphy_status(sc);
@@ -318,6 +326,8 @@ ipgphy_mii_phy_auto(struct mii_softc *sc
 	uint16_t reg = 0;
 	u_int subtype = IFM_SUBTYPE(media);
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	/* XXX Is it requreid ? */
 	if (sc->mii_mpd_model == MII_MODEL_xxICPLUS_IP1001) {
 		PHY_READ(sc, MII_ANAR, &reg);
@@ -371,6 +381,8 @@ ipgphy_reset(struct mii_softc *sc)
 	struct ipgphy_softc *isc = device_private(sc->mii_dev);
 	uint16_t reg;
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	mii_phy_reset(sc);
 
 	/* Clear autoneg/full-duplex as we don't want it after reset */
Index: dev/mii/jmphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/jmphy.c,v
retrieving revision 1.3
diff -u -p -r1.3 jmphy.c
--- dev/mii/jmphy.c	27 Nov 2019 10:19:20 -0000	1.3
+++ dev/mii/jmphy.c	6 Feb 2020 00:10:01 -0000
@@ -103,6 +103,8 @@ jmphy_attach(device_t parent, device_t s
 
 	sc->mii_flags |= MIIF_NOISOLATE | MIIF_NOLOOP;
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
@@ -110,6 +112,8 @@ jmphy_attach(device_t parent, device_t s
 	if (sc->mii_capabilities & BMSR_EXTSTAT)
 		PHY_READ(sc, MII_EXTSR, &sc->mii_extcapabilities);
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 }
 
@@ -119,6 +123,8 @@ jmphy_service(struct mii_softc *sc, stru
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmcr, ssr;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -190,6 +196,8 @@ jmphy_status(struct mii_softc *sc)
 	struct mii_data *mii = sc->mii_pdata;
 	uint16_t bmcr, ssr, gtsr;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
@@ -254,6 +262,8 @@ jmphy_reset(struct mii_softc *sc)
 	int i;
 	uint16_t val;
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	/* Disable sleep mode. */
 	PHY_READ(sc, JMPHY_TMCTL, &val);
 	PHY_WRITE(sc, JMPHY_TMCTL, val & ~JMPHY_TMCTL_SLEEP_ENB);
@@ -299,6 +309,8 @@ jmphy_auto(struct mii_softc *sc, struct 
 {
 	uint16_t anar, bmcr, gig;
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	gig = 0;
 	PHY_READ(sc, MII_BMCR, &bmcr);
 	switch (IFM_SUBTYPE(ife->ifm_media)) {
Index: dev/mii/lxtphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/lxtphy.c,v
retrieving revision 1.54
diff -u -p -r1.54 lxtphy.c
--- dev/mii/lxtphy.c	27 Nov 2019 10:19:20 -0000	1.54
+++ dev/mii/lxtphy.c	6 Feb 2020 00:10:01 -0000
@@ -138,14 +138,20 @@ lxtphyattach(device_t parent, device_t s
 	sc->mii_pdata = mii;
 	sc->mii_flags = ma->mii_flags;
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
 	sc->mii_capabilities &= ma->mii_capmask;
 
+	mii_unlock(mii);
+
 	if (sc->mii_flags & MIIF_HAVEFIBER) {
 #define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
+		mii_lock(mii);
 		sc->mii_anegticks = MII_ANEGTICKS;
+		mii_unlock(mii);
 		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_FX, 0, sc->mii_inst),
 		    MII_MEDIA_100_TX);
 		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_FX, IFM_FDX, sc->mii_inst),
@@ -163,6 +169,8 @@ lxtphy_service(struct mii_softc *sc, str
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -222,6 +230,8 @@ lxtphy_status(struct mii_softc *sc)
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmcr, bmsr, csr;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
@@ -270,6 +280,8 @@ lxtphy_reset(struct mii_softc *sc)
 {
 	uint16_t ier;
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	mii_phy_reset(sc);
 	PHY_READ(sc, MII_LXTPHY_IER, &ier);
 	ier &= ~IER_INTEN;
Index: dev/mii/makphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/makphy.c,v
retrieving revision 1.64
diff -u -p -r1.64 makphy.c
--- dev/mii/makphy.c	28 Jan 2020 05:08:02 -0000	1.64
+++ dev/mii/makphy.c	6 Feb 2020 00:10:01 -0000
@@ -181,6 +181,8 @@ makphyattach(device_t parent, device_t s
 	sc->mii_pdata = mii;
 	sc->mii_flags = ma->mii_flags;
 
+	mii_lock(mii);
+
 	switch (model) {
 	case MII_MODEL_xxMARVELL_E1000:
 		if ((maksc->sc_flags & MAKPHY_F_I210) != 0)
@@ -255,6 +257,7 @@ page0:
 			sc->mii_flags &= ~MIIF_IS_1000X;
 		}
 	}
+	mii_unlock(mii);
 	mii_phy_add_media(sc);
 }
 
@@ -264,6 +267,8 @@ makphy_reset(struct mii_softc *sc)
 	struct makphy_softc *maksc = (struct makphy_softc *)sc;
 	uint16_t reg;
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	mii_phy_reset(sc);
 
 	/* Initialize PHY Specific Control Register. */
@@ -345,6 +350,8 @@ makphy_service(struct mii_softc *sc, str
 	if (!device_is_active(sc->mii_dev))
 		return ENXIO;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -413,6 +420,8 @@ makphy_status(struct mii_softc *sc)
 	struct mii_data *mii = sc->mii_pdata;
 	uint16_t bmcr, gsr, pssr, essr;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
Index: dev/mii/micphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/micphy.c,v
retrieving revision 1.11
diff -u -p -r1.11 micphy.c
--- dev/mii/micphy.c	13 Dec 2019 08:30:26 -0000	1.11
+++ dev/mii/micphy.c	6 Feb 2020 00:10:02 -0000
@@ -232,6 +232,8 @@ micphyattach(device_t parent, device_t s
 	} else
 		msc->sc_lstype = MICPHYF_LSTYPE_DEFAULT;
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	micphy_fixup(sc, model, rev, parent);
@@ -241,6 +243,8 @@ micphyattach(device_t parent, device_t s
 	if (sc->mii_capabilities & BMSR_EXTSTAT)
 		PHY_READ(sc, MII_EXTSR, &sc->mii_extcapabilities);
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 }
 
@@ -249,6 +253,8 @@ micphy_reset(struct mii_softc *sc)
 {
 	uint16_t reg;
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	/*
 	 * The 8081 has no "sticky bits" that survive a soft reset; several
 	 * bits in the Phy Control Register 2 must be preserved across the
@@ -269,6 +275,8 @@ micphy_service(struct mii_softc *sc, str
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -331,6 +339,9 @@ micphy_writexreg(struct mii_softc *sc, u
 static void
 micphy_fixup(struct mii_softc *sc, int model, int rev, device_t parent)
 {
+
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	switch (model) {
 	case MII_MODEL_MICREL_KSZ9021_8001_8721:
 		if (!device_is_a(parent, "cpsw"))
@@ -360,6 +371,8 @@ micphy_status(struct mii_softc *sc)
 	struct mii_data *mii = sc->mii_pdata;
 	uint16_t bmsr, bmcr, sr;
 
+	KASSERT(mii_locked(mii));
+
 	/* For unknown devices */
 	if (msc->sc_lstype == MICPHYF_LSTYPE_DEFAULT) {
 		ukphy_status(sc);
Index: dev/mii/mii.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/mii.c,v
retrieving revision 1.54
diff -u -p -r1.54 mii.c
--- dev/mii/mii.c	9 Apr 2019 11:28:45 -0000	1.54
+++ dev/mii/mii.c	6 Feb 2020 00:10:02 -0000
@@ -38,6 +38,8 @@
 #include <sys/cdefs.h>
 __KERNEL_RCSID(0, "$NetBSD: mii.c,v 1.54 2019/04/09 11:28:45 msaitoh Exp $");
 
+#define	__IFMEDIA_PRIVATE
+
 #include <sys/param.h>
 #include <sys/device.h>
 #include <sys/systm.h>
@@ -69,6 +71,8 @@ mii_attach(device_t parent, struct mii_d
 	int locs[MIICF_NLOCS];
 	int rv;
 
+	KASSERT(mii->mii_media.ifm_lock != NULL);
+
 	if (phyloc != MII_PHY_ANY && offloc != MII_OFFSET_ANY)
 		panic("mii_attach: phyloc and offloc specified");
 
@@ -78,11 +82,35 @@ mii_attach(device_t parent, struct mii_d
 	} else
 		phymin = phymax = phyloc;
 
+	mii_lock(mii);
+
 	if ((mii->mii_flags & MIIF_INITDONE) == 0) {
 		LIST_INIT(&mii->mii_phys);
+		cv_init(&mii->mii_probe_cv, "mii_attach");
 		mii->mii_flags |= MIIF_INITDONE;
 	}
 
+	/*
+	 * Probing temporarily unlocks the MII; wait until anyone
+	 * else who might be doing this to finish.
+	 */
+	mii->mii_probe_waiters++;
+	for (;;) {
+		if (mii->mii_flags & MIIF_EXITING) {
+			mii->mii_probe_waiters--;
+			cv_signal(&mii->mii_probe_cv);
+			mii_unlock(mii);
+			return;
+		}
+		if ((mii->mii_flags & MIIF_PROBING) == 0)
+			break;
+		cv_wait(&mii->mii_probe_cv, mii->mii_media.ifm_lock);
+	}
+	mii->mii_probe_waiters--;
+
+	/* ...and old others off. */
+	mii->mii_flags |= MIIF_PROBING;
+
 	for (ma.mii_phyno = phymin; ma.mii_phyno <= phymax; ma.mii_phyno++) {
 		/*
 		 * Make sure we haven't already configured a PHY at this
@@ -142,17 +170,27 @@ mii_attach(device_t parent, struct mii_d
 
 		locs[MIICF_PHY] = ma.mii_phyno;
 
+		mii_unlock(mii);
+
 		child = device_private(config_found_sm_loc(parent, "mii",
 			locs, &ma, mii_print, config_stdsubmatch));
 		if (child) {
 			/* Link it up in the parent's MII data. */
 			callout_init(&child->mii_nway_ch, 0);
+			mii_lock(mii);
 			LIST_INSERT_HEAD(&mii->mii_phys, child, mii_list);
 			child->mii_offset = offset;
 			mii->mii_instance++;
+		} else {
+			mii_lock(mii);
 		}
 		offset++;
 	}
+
+	/* All done! */
+	mii->mii_flags &= ~MIIF_PROBING;
+	cv_signal(&mii->mii_probe_cv);
+	mii_unlock(mii);
 }
 
 void
@@ -163,12 +201,38 @@ mii_detach(struct mii_data *mii, int phy
 	if (phyloc != MII_PHY_ANY && offloc != MII_PHY_ANY)
 		panic("mii_detach: phyloc and offloc specified");
 
-	if ((mii->mii_flags & MIIF_INITDONE) == 0)
+	mii_lock(mii);
+
+	if ((mii->mii_flags & MIIF_INITDONE) == 0 ||
+	    (mii->mii_flags & MIIF_EXITING) != 0) {
+		mii_unlock(mii);
 		return;
+	}
+
+	/* XXX This is probably not the best hueristic. */
+	if (phyloc == MII_PHY_ANY && MII_PHY_ANY == MII_OFFSET_ANY) {
+		mii->mii_flags |= MIIF_EXITING;
+		cv_broadcast(&mii->mii_probe_cv);
+	}
+
+	/* Wait for everyone else to get out. */
+	for (;;) {
+		if (mii->mii_flags & MIIF_EXITING) {
+			cv_signal(&mii->mii_probe_cv);
+			mii_unlock(mii);
+			return;
+		}
+		if ((mii->mii_flags & MIIF_PROBING) == 0 &&
+		    ((mii->mii_flags & MIIF_EXITING) == 0 ||
+		     mii->mii_probe_waiters == 0))
+			break;
+		cv_wait(&mii->mii_probe_cv, mii->mii_media.ifm_lock);
+	}
+
+	if ((mii->mii_flags & MIIF_EXITING) == 0)
+		mii->mii_flags |= MIIF_PROBING;
 
-	for (child = LIST_FIRST(&mii->mii_phys);
-	     child != NULL; child = nchild) {
-		nchild = LIST_NEXT(child, mii_list);
+	LIST_FOREACH_SAFE(child, &mii->mii_phys, mii_list, nchild) {
 		if (phyloc != MII_PHY_ANY || offloc != MII_OFFSET_ANY) {
 			if (phyloc != MII_PHY_ANY &&
 			    phyloc != child->mii_phy)
@@ -177,8 +241,18 @@ mii_detach(struct mii_data *mii, int phy
 			    offloc != child->mii_offset)
 				continue;
 		}
+		LIST_REMOVE(child, mii_list);
+		mii_unlock(mii);
 		(void)config_detach(child->mii_dev, DETACH_FORCE);
+		mii_lock(mii);
+	}
+
+	if ((mii->mii_flags & MIIF_EXITING) == 0) {
+		mii->mii_flags &= ~MIIF_PROBING;
+		cv_signal(&mii->mii_probe_cv);
 	}
+
+	mii_unlock(mii);
 }
 
 static int
@@ -207,7 +281,13 @@ phy_service(struct mii_softc *sc, struct
 int
 mii_ifmedia_change(struct mii_data *mii)
 {
-	return ifmedia_change(&mii->mii_media, mii->mii_ifp);
+
+	IFMEDIA_LOCK_FOR_LEGACY(&mii->mii_media);
+	KASSERT(mii_locked(mii));
+	int rv = ifmedia_change(&mii->mii_media, mii->mii_ifp);
+	IFMEDIA_UNLOCK_FOR_LEGACY(&mii->mii_media);
+
+	return rv;
 }
 
 /*
@@ -217,7 +297,10 @@ int
 mii_mediachg(struct mii_data *mii)
 {
 	struct mii_softc *child;
-	int rv;
+	int rv = 0;
+
+	IFMEDIA_LOCK_FOR_LEGACY(&mii->mii_media);
+	KASSERT(mii_locked(mii));
 
 	mii->mii_media_status = 0;
 	mii->mii_media_active = IFM_NONE;
@@ -225,9 +308,10 @@ mii_mediachg(struct mii_data *mii)
 	LIST_FOREACH(child, &mii->mii_phys, mii_list) {
 		rv = phy_service(child, mii, MII_MEDIACHG);
 		if (rv)
-			return rv;
+			break;
 	}
-	return 0;
+	IFMEDIA_UNLOCK_FOR_LEGACY(&mii->mii_media);
+	return rv;
 }
 
 /*
@@ -238,8 +322,13 @@ mii_tick(struct mii_data *mii)
 {
 	struct mii_softc *child;
 
+	IFMEDIA_LOCK_FOR_LEGACY(&mii->mii_media);
+	KASSERT(mii_locked(mii));
+
 	LIST_FOREACH(child, &mii->mii_phys, mii_list)
 		(void)phy_service(child, mii, MII_TICK);
+
+	IFMEDIA_UNLOCK_FOR_LEGACY(&mii->mii_media);
 }
 
 /*
@@ -250,11 +339,16 @@ mii_pollstat(struct mii_data *mii)
 {
 	struct mii_softc *child;
 
+	IFMEDIA_LOCK_FOR_LEGACY(&mii->mii_media);
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = 0;
 	mii->mii_media_active = IFM_NONE;
 
 	LIST_FOREACH(child, &mii->mii_phys, mii_list)
 		(void)phy_service(child, mii, MII_POLLSTAT);
+
+	IFMEDIA_UNLOCK_FOR_LEGACY(&mii->mii_media);
 }
 
 /*
@@ -265,8 +359,13 @@ mii_down(struct mii_data *mii)
 {
 	struct mii_softc *child;
 
+	IFMEDIA_LOCK_FOR_LEGACY(&mii->mii_media);
+	KASSERT(mii_locked(mii));
+
 	LIST_FOREACH(child, &mii->mii_phys, mii_list)
 		(void)phy_service(child, mii, MII_DOWN);
+
+	IFMEDIA_UNLOCK_FOR_LEGACY(&mii->mii_media);
 }
 
 static unsigned char
Index: dev/mii/mii_ethersubr.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/mii_ethersubr.c,v
retrieving revision 1.2
diff -u -p -r1.2 mii_ethersubr.c
--- dev/mii/mii_ethersubr.c	25 Mar 2019 07:09:54 -0000	1.2
+++ dev/mii/mii_ethersubr.c	6 Feb 2020 00:10:02 -0000
@@ -63,6 +63,8 @@
 #include <sys/cdefs.h>
 __KERNEL_RCSID(0, "$NetBSD: mii_ethersubr.c,v 1.2 2019/03/25 07:09:54 msaitoh Exp $");
 
+#define	__IFMEDIA_PRIVATE
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
@@ -84,6 +86,8 @@ ether_mediachange(struct ifnet *ifp)
 	int rc;
 
 	KASSERT(ec->ec_mii != NULL);
+	KASSERT(mii_locked(ec->ec_mii) ||
+		ifmedia_islegacy(&ec->ec_mii->mii_media));
 
 	if ((ifp->if_flags & IFF_UP) == 0)
 		return 0;
@@ -99,6 +103,8 @@ ether_mediastatus(struct ifnet *ifp, str
 	struct mii_data		*mii;
 
 	KASSERT(ec->ec_mii != NULL);
+	KASSERT(mii_locked(ec->ec_mii) ||
+		ifmedia_islegacy(&ec->ec_mii->mii_media));
 
 #ifdef notyet
 	if ((ifp->if_flags & IFF_RUNNING) == 0) {
Index: dev/mii/mii_physubr.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/mii_physubr.c,v
retrieving revision 1.89
diff -u -p -r1.89 mii_physubr.c
--- dev/mii/mii_physubr.c	27 Nov 2019 10:19:20 -0000	1.89
+++ dev/mii/mii_physubr.c	6 Feb 2020 00:10:02 -0000
@@ -125,6 +125,7 @@ static const struct mii_media mii_media_
 };
 
 static void	mii_phy_auto_timeout(void *);
+static void	mii_phy_auto_timeout_locked(struct mii_softc *);
 
 void
 mii_phy_setmedia(struct mii_softc *sc)
@@ -133,6 +134,8 @@ mii_phy_setmedia(struct mii_softc *sc)
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmcr, anar, gtcr;
 
+	KASSERT(mii_locked(mii));
+
 	if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
 		/*
 		 * Force renegotiation if MIIF_DOPAUSE.
@@ -202,6 +205,8 @@ mii_phy_auto(struct mii_softc *sc, int w
 	struct mii_data *mii = sc->mii_pdata;
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 
+	KASSERT(mii_locked(mii));
+
 	sc->mii_ticks = 0;
 	if ((sc->mii_flags & MIIF_DOINGAUTO) == 0) {
 		/*
@@ -285,9 +290,10 @@ mii_phy_auto(struct mii_softc *sc, int w
 	 * delays all the time while the system is running!
 	 */
 	if (sc->mii_flags & MIIF_AUTOTSLEEP) {
+		ASSERT_SLEEPABLE();
 		sc->mii_flags |= MIIF_DOINGAUTO;
-		tsleep(&sc->mii_flags, PZERO, "miiaut", hz >> 1);
-		mii_phy_auto_timeout(sc);
+		kpause("miiaut", false, hz >> 1, mii->mii_media.ifm_lock);
+		mii_phy_auto_timeout_locked(sc);
 	} else if ((sc->mii_flags & MIIF_DOINGAUTO) == 0) {
 		sc->mii_flags |= MIIF_DOINGAUTO;
 		callout_reset(&sc->mii_nway_ch, hz >> 1,
@@ -297,20 +303,29 @@ mii_phy_auto(struct mii_softc *sc, int w
 }
 
 static void
-mii_phy_auto_timeout(void *arg)
+mii_phy_auto_timeout_locked(struct mii_softc *sc)
 {
-	struct mii_softc *sc = arg;
-	int s;
 
 	if (!device_is_active(sc->mii_dev))
 		return;
-
-	s = splnet();
+	
 	sc->mii_flags &= ~MIIF_DOINGAUTO;
 
 	/* Update the media status. */
 	(void) PHY_SERVICE(sc, sc->mii_pdata, MII_POLLSTAT);
-	splx(s);
+}
+
+static void
+mii_phy_auto_timeout(void *arg)
+{
+	struct mii_softc *sc = arg;
+
+	if (!device_is_active(sc->mii_dev))
+		return;
+
+	mii_lock(sc->mii_pdata);
+	mii_phy_auto_timeout_locked(sc);
+	mii_unlock(sc->mii_pdata);
 }
 
 int
@@ -320,6 +335,8 @@ mii_phy_tick(struct mii_softc *sc)
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg;
 
+	KASSERT(mii_locked(mii));
+
 	/* Just bail now if the interface is down. */
 	if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
 		return EJUSTRETURN;
@@ -385,6 +402,8 @@ mii_phy_reset(struct mii_softc *sc)
 	int i;
 	uint16_t reg;
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	if (sc->mii_flags & MIIF_NOISOLATE)
 		reg = BMCR_RESET;
 	else
@@ -407,6 +426,8 @@ void
 mii_phy_down(struct mii_softc *sc)
 {
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	if (sc->mii_flags & MIIF_DOINGAUTO) {
 		sc->mii_flags &= ~MIIF_DOINGAUTO;
 		callout_stop(&sc->mii_nway_ch);
@@ -417,6 +438,7 @@ void
 mii_phy_status(struct mii_softc *sc)
 {
 
+	KASSERT(mii_locked(sc->mii_pdata));
 	PHY_STATUS(sc);
 }
 
@@ -425,6 +447,8 @@ mii_phy_update(struct mii_softc *sc, int
 {
 	struct mii_data *mii = sc->mii_pdata;
 
+	KASSERT(mii_locked(mii));
+
 	if (sc->mii_media_active != mii->mii_media_active ||
 	    sc->mii_media_status != mii->mii_media_status ||
 	    cmd == MII_MEDIACHG) {
@@ -441,6 +465,8 @@ mii_phy_statusmsg(struct mii_softc *sc)
 	struct mii_data *mii = sc->mii_pdata;
 	struct ifnet *ifp = mii->mii_ifp;
 
+	KASSERT(mii_locked(mii));
+
 	if (mii->mii_media_status & IFM_AVALID) {
 		if (mii->mii_media_status & IFM_ACTIVE)
 			if_link_state_change(ifp, LINK_STATE_UP);
@@ -449,6 +475,7 @@ mii_phy_statusmsg(struct mii_softc *sc)
 	} else
 		if_link_state_change(ifp, LINK_STATE_UNKNOWN);
 
+	/* XXX NET_MPSAFE */
 	ifp->if_baudrate = ifmedia_baudrate(mii->mii_media_active);
 }
 
@@ -476,11 +503,14 @@ mii_phy_add_media(struct mii_softc *sc)
 	 * Set the autonegotiation timer for 10/100 media.  Gigabit media is
 	 * handled below.
 	 */
+	mii_lock(mii);
 	sc->mii_anegticks = MII_ANEGTICKS;
+	mii_unlock(mii);
 
 #define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
 #define	PRINT(n)	aprint_normal("%s%s", sep, (n)); sep = ", "
 
+	/* This flag is static; no need to lock. */
 	if ((sc->mii_flags & MIIF_NOISOLATE) == 0)
 		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
 		    MII_MEDIA_NONE);
@@ -488,7 +518,8 @@ mii_phy_add_media(struct mii_softc *sc)
 	/*
 	 * There are different interpretations for the bits in
 	 * HomePNA PHYs.  And there is really only one media type
-	 * that is supported.
+	 * that is supported.  This flag is also static, and so
+	 * no need to lock.
 	 */
 	if (sc->mii_flags & MIIF_IS_HPNA) {
 		if (sc->mii_capabilities & BMSR_10THDX) {
@@ -538,15 +569,19 @@ mii_phy_add_media(struct mii_softc *sc)
 		 * all the gigabit media types.
 		 */
 		if (sc->mii_extcapabilities & EXTSR_1000XHDX) {
+			mii_lock(mii);
 			sc->mii_anegticks = MII_ANEGTICKS_GIGE;
 			sc->mii_flags |= MIIF_IS_1000X;
+			mii_unlock(mii);
 			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, 0,
 			    sc->mii_inst), MII_MEDIA_1000_X);
 			PRINT("1000baseSX");
 		}
 		if (sc->mii_extcapabilities & EXTSR_1000XFDX) {
+			mii_lock(mii);
 			sc->mii_anegticks = MII_ANEGTICKS_GIGE;
 			sc->mii_flags |= MIIF_IS_1000X;
+			mii_unlock(mii);
 			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX,
 			    sc->mii_inst), MII_MEDIA_1000_X_FDX);
 			PRINT("1000baseSX-FDX");
@@ -562,17 +597,21 @@ mii_phy_add_media(struct mii_softc *sc)
 		 * All 1000baseT PHYs have a 1000baseT control register.
 		 */
 		if (sc->mii_extcapabilities & EXTSR_1000THDX) {
+			mii_lock(mii);
 			sc->mii_anegticks = MII_ANEGTICKS_GIGE;
 			sc->mii_flags |= MIIF_HAVE_GTCR;
 			mii->mii_media.ifm_mask |= IFM_ETH_MASTER;
+			mii_unlock(mii);
 			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0,
 			    sc->mii_inst), MII_MEDIA_1000_T);
 			PRINT("1000baseT");
 		}
 		if (sc->mii_extcapabilities & EXTSR_1000TFDX) {
+			mii_lock(mii);
 			sc->mii_anegticks = MII_ANEGTICKS_GIGE;
 			sc->mii_flags |= MIIF_HAVE_GTCR;
 			mii->mii_media.ifm_mask |= IFM_ETH_MASTER;
+			mii_unlock(mii);
 			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX,
 			    sc->mii_inst), MII_MEDIA_1000_T_FDX);
 			PRINT("1000baseT-FDX");
@@ -587,8 +626,12 @@ mii_phy_add_media(struct mii_softc *sc)
 	}
 #undef ADD
 #undef PRINT
-	if (fdx != 0 && (sc->mii_flags & MIIF_DOPAUSE))
+	/* This flag is static; no need to lock. */
+	if (fdx != 0 && (sc->mii_flags & MIIF_DOPAUSE)) {
+		mii_lock(mii);
 		mii->mii_media.ifm_mask |= IFM_ETH_FMASK;
+		mii_unlock(mii);
+	}
 out:
 	aprint_normal("\n");
 	if (!pmf_device_register(self, NULL, mii_phy_resume)) {
@@ -623,15 +666,14 @@ mii_phy_detach(device_t self, int flags)
 {
 	struct mii_softc *sc = device_private(self);
 
-	/* XXX Invalidate parent's media setting? */
-
+	mii_lock(sc->mii_pdata);
 	if (sc->mii_flags & MIIF_DOINGAUTO)
 		callout_halt(&sc->mii_nway_ch, NULL);
+	mii_unlock(sc->mii_pdata);
 
 	callout_destroy(&sc->mii_nway_ch);
 
 	mii_phy_delete_media(sc);
-	LIST_REMOVE(sc, mii_list);
 
 	return 0;
 }
@@ -656,6 +698,8 @@ mii_phy_flowstatus(struct mii_softc *sc)
 {
 	uint16_t anar, anlpar;
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	if ((sc->mii_flags & MIIF_DOPAUSE) == 0)
 		return 0;
 
@@ -704,8 +748,12 @@ mii_phy_resume(device_t dv, const pmf_qu
 {
 	struct mii_softc *sc = device_private(dv);
 
+	mii_lock(sc->mii_pdata);
 	PHY_RESET(sc);
-	return PHY_SERVICE(sc, sc->mii_pdata, MII_MEDIACHG) == 0;
+	bool rv = PHY_SERVICE(sc, sc->mii_pdata, MII_MEDIACHG) == 0;
+	mii_unlock(sc->mii_pdata);
+
+	return rv;
 }
 
 
Index: dev/mii/miivar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/miivar.h,v
retrieving revision 1.69
diff -u -p -r1.69 miivar.h
--- dev/mii/miivar.h	20 Nov 2019 08:50:59 -0000	1.69
+++ dev/mii/miivar.h	6 Feb 2020 00:10:02 -0000
@@ -35,6 +35,7 @@
 
 #include <sys/queue.h>
 #include <sys/callout.h>
+#include <sys/condvar.h>
 
 #include <dev/mii/mii.h>
 #include <dev/mii/mii_verbose.h>
@@ -57,32 +58,53 @@ typedef	void (*mii_statchg_t)(struct ifn
  * A network interface driver has one of these structures in its softc.
  * It is the interface from the network interface driver to the MII
  * layer.
+ *
+ * LOCKING
+ * =======
+ *
+ * The MII shares the lock with its ifmedia (which in turn shares the
+ * lock with its driver).
+ *
+ * MII routines can be called from a driver's interrupt handler, as well
+ * as via administrative input (e.g. ifmedia_ioctl()).  We therefore requie
+ * that most MII APIs are called with the ifmedia lock HELD.  (See below.)
+ *
+ * Field markings and the corresponding locks:
+ *
+ * m:	ifmedia lock
+ * ::	unlocked, stable
  */
 struct mii_data {
-	struct ifmedia mii_media;	/* media information */
-	struct ifnet *mii_ifp;		/* pointer back to network interface */
+	struct ifmedia mii_media;	/* m: media information */
+	struct ifnet *mii_ifp;		/* :: pointer back to interface */
 
-	int mii_flags;			/* misc. flags; see below */
+	kcondvar_t mii_probe_cv;	/* m: serialize probing the MII */
+	u_int mii_probe_waiters;	/* m: # threads waiting to probe */
+	int mii_flags;			/* m: misc. flags; see below */
 
 	/*
-	 * For network interfaces with multiple PHYs, a list of all
+	 * m: For network interfaces with multiple PHYs, a list of all
 	 * PHYs is required so they can all be notified when a media
 	 * request is made.
 	 */
 	LIST_HEAD(mii_listhead, mii_softc) mii_phys;
 	u_int mii_instance;
 
-	/* PHY driver fills this in with active media status. */
+	/* m: PHY driver fills this in with active media status. */
 	int mii_media_status;
 	u_int mii_media_active;
 
-	/* Calls from MII layer into network interface driver. */
+	/* :: Calls from MII layer into network interface driver. */
 	mii_readreg_t mii_readreg;
 	mii_writereg_t mii_writereg;
 	mii_statchg_t mii_statchg;
 };
 typedef struct mii_data mii_data_t;
 
+#define	mii_lock(mii)		ifmedia_lock(&(mii)->mii_media)
+#define	mii_unlock(mii)		ifmedia_unlock(&(mii)->mii_media)
+#define	mii_locked(mii)		ifmedia_locked(&(mii)->mii_media)
+
 /*
  * Functions provided by the PHY to perform various functions.
  */
@@ -108,30 +130,30 @@ struct mii_phy_funcs {
 struct mii_softc {
 	device_t mii_dev;		/* generic device glue */
 
-	LIST_ENTRY(mii_softc) mii_list;	/* entry on parent's PHY list */
+	LIST_ENTRY(mii_softc) mii_list;	/* m: entry on parent's PHY list */
 
-	uint32_t mii_mpd_oui;		/* the PHY's OUI (MII_OUI())*/
-	uint32_t mii_mpd_model;		/* the PHY's model (MII_MODEL())*/
-	uint32_t mii_mpd_rev;		/* the PHY's revision (MII_REV())*/
-	int mii_phy;			/* our MII address */
-	int mii_offset;			/* first PHY, second PHY, etc. */
-	u_int mii_inst;			/* instance for ifmedia */
+	uint32_t mii_mpd_oui;		/* :: the PHY's OUI (MII_OUI())*/
+	uint32_t mii_mpd_model;		/* :: the PHY's model (MII_MODEL())*/
+	uint32_t mii_mpd_rev;		/* :: the PHY's revision (MII_REV())*/
+	int mii_phy;			/* :: our MII address */
+	int mii_offset;			/* :: first PHY, second PHY, etc. */
+	u_int mii_inst;			/* :: instance for ifmedia */
 
-	/* Our PHY functions. */
+	/* :: Our PHY functions. */
 	const struct mii_phy_funcs *mii_funcs;
 
-	struct mii_data *mii_pdata;	/* pointer to parent's mii_data */
+	struct mii_data *mii_pdata;	/* :: pointer to parent's mii_data */
 
-	int mii_flags;			/* misc. flags; see below */
-	uint16_t mii_capabilities;	/* capabilities from BMSR */
-	uint16_t mii_extcapabilities;	/* extended capabilities from EXTSR */
-	int mii_ticks;			/* MII_TICK counter */
-	int mii_anegticks;		/* ticks before retrying aneg */
+	int mii_flags;			/* m: misc. flags; see below */
+	uint16_t mii_capabilities;	/* :: capabilities from BMSR */
+	uint16_t mii_extcapabilities;	/* :: extended caps from EXTSR */
+	int mii_ticks;			/* m: MII_TICK counter */
+	int mii_anegticks;		/* m: ticks before retrying aneg */
 
-	struct callout mii_nway_ch;	/* NWAY callout */
+	struct callout mii_nway_ch;	/* m: NWAY callout */
 
-	u_int mii_media_active;		/* last active media */
-	int mii_media_status;		/* last active status */
+	u_int mii_media_active;		/* m: last active media */
+	int mii_media_status;		/* m: last active status */
 };
 typedef struct mii_softc mii_softc_t;
 
@@ -144,13 +166,15 @@ typedef struct mii_softc mii_softc_t;
 #define	MIIF_NOISOLATE	0x0002		/* do not isolate the PHY */
 #define	MIIF_NOLOOP	0x0004		/* no loopback capability */
 #define	MIIF_DOINGAUTO	0x0008		/* doing autonegotiation (mii_softc) */
-#define MIIF_AUTOTSLEEP	0x0010		/* use tsleep(), not callout() */
+#define MIIF_AUTOTSLEEP	0x0010		/* use kpause(), not callout() */
 #define MIIF_HAVEFIBER	0x0020		/* from parent: has fiber interface */
 #define	MIIF_HAVE_GTCR	0x0040		/* has 100base-T2/1000base-T CR */
 #define	MIIF_IS_1000X	0x0080		/* is a 1000BASE-X device */
 #define	MIIF_DOPAUSE	0x0100		/* advertise PAUSE capability */
 #define	MIIF_IS_HPNA	0x0200		/* is a HomePNA device */
 #define	MIIF_FORCEANEG	0x0400		/* force auto-negotiation */
+#define	MIIF_PROBING	0x0800		/* PHY probe in-progress */
+#define	MIIF_EXITING	0x1000		/* MII is exiting */
 
 #define	MIIF_INHERIT_MASK (MIIF_NOISOLATE | MIIF_NOLOOP | MIIF_AUTOTSLEEP)
 
@@ -270,47 +294,54 @@ MMD_INDIRECT_WRITE(struct mii_softc *sc,
 	return PHY_WRITE(sc, MII_MMDAADR, val);
 }
 
+/* MII must be LOCKED. */
 #define	PHY_SERVICE(p, d, o) \
 	(*(p)->mii_funcs->pf_service)((p), (d), (o))
-
 #define	PHY_STATUS(p) \
 	(*(p)->mii_funcs->pf_status)((p))
-
 #define	PHY_RESET(p) \
 	(*(p)->mii_funcs->pf_reset)((p))
 
+/* MII must be UNLOCKED */
 void	mii_attach(device_t, struct mii_data *, int, int, int, int);
 void	mii_detach(struct mii_data *, int, int);
-bool	mii_phy_resume(device_t, const pmf_qual_t *);
 
+/* MII must be LOCKED. */
 int	mii_mediachg(struct mii_data *);
 void	mii_tick(struct mii_data *);
 void	mii_pollstat(struct mii_data *);
 void	mii_down(struct mii_data *);
-uint16_t mii_anar(struct ifmedia_entry *);
-
 int	mii_ifmedia_change(struct mii_data *);
 
+uint16_t mii_anar(struct ifmedia_entry *);
+
+/* MII must be UNLOCKED */
 int	mii_phy_activate(device_t, enum devact);
 int	mii_phy_detach(device_t, int);
+bool	mii_phy_resume(device_t, const pmf_qual_t *);
 
 const struct mii_phydesc *mii_phy_match(const struct mii_attach_args *,
 	    const struct mii_phydesc *);
 
+/* MII must be UNLOCKED */
 void	mii_phy_add_media(struct mii_softc *);
 void	mii_phy_delete_media(struct mii_softc *);
 
+/* MII must be LOCKED */
 void	mii_phy_setmedia(struct mii_softc *);
 int	mii_phy_auto(struct mii_softc *, int);
 void	mii_phy_reset(struct mii_softc *);
 void	mii_phy_down(struct mii_softc *);
 int	mii_phy_tick(struct mii_softc *);
 
+/* MII must be LOCKED */
 void	mii_phy_status(struct mii_softc *);
 void	mii_phy_update(struct mii_softc *, int);
 
+/* MII must be LOCKED */
 u_int	mii_phy_flowstatus(struct mii_softc *);
 
+/* MII must be LOCKED */
 void	ukphy_status(struct mii_softc *);
 
 u_int	mii_oui(uint16_t, uint16_t);
Index: dev/mii/mvphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/mvphy.c,v
retrieving revision 1.14
diff -u -p -r1.14 mvphy.c
--- dev/mii/mvphy.c	27 Nov 2019 10:19:20 -0000	1.14
+++ dev/mii/mvphy.c	6 Feb 2020 00:10:02 -0000
@@ -192,6 +192,8 @@ mvphyattach(device_t parent, device_t se
 	sc->mii_pdata = mii;
 	sc->mii_flags = ma->mii_flags;
 
+	mii_lock(mii);
+
 	if (MV_PORT(sc) == 0) {		/* NB: only when attaching first PHY */
 		/*
 		 * Set the global switch settings and configure the
@@ -207,6 +209,8 @@ mvphyattach(device_t parent, device_t se
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
 	sc->mii_capabilities &= ma->mii_capmask;
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 }
 
@@ -215,6 +219,8 @@ mvphy_service(struct mii_softc *sc, stru
 {
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -267,6 +273,8 @@ mvphy_status(struct mii_softc *sc)
 	struct mii_data *mii = sc->mii_pdata;
 	uint16_t hwstatus;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
@@ -292,6 +300,8 @@ static void
 mvphy_reset(struct mii_softc *sc)
 {
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	/* XXX handle fixed media config */
 	PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN);
 	mvphy_switchconfig(sc, MV_PORT(sc));
Index: dev/mii/nsphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/nsphy.c,v
retrieving revision 1.65
diff -u -p -r1.65 nsphy.c
--- dev/mii/nsphy.c	27 Nov 2019 10:19:20 -0000	1.65
+++ dev/mii/nsphy.c	6 Feb 2020 00:10:02 -0000
@@ -127,11 +127,15 @@ nsphyattach(device_t parent, device_t se
 	sc->mii_pdata = mii;
 	sc->mii_flags = ma->mii_flags;
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
 	sc->mii_capabilities &= ma->mii_capmask;
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 }
 
@@ -141,6 +145,8 @@ nsphy_service(struct mii_softc *sc, stru
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -230,6 +236,8 @@ nsphy_status(struct mii_softc *sc)
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmsr, bmcr, aner, anar, par, anlpar, result;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
@@ -305,6 +313,8 @@ nsphy_reset(struct mii_softc *sc)
 	int i;
 	uint16_t reg;
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	if (sc->mii_flags & MIIF_NOISOLATE)
 		reg = BMCR_RESET;
 	else
Index: dev/mii/nsphyter.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/nsphyter.c,v
retrieving revision 1.44
diff -u -p -r1.44 nsphyter.c
--- dev/mii/nsphyter.c	27 Nov 2019 10:19:21 -0000	1.44
+++ dev/mii/nsphyter.c	6 Feb 2020 00:10:02 -0000
@@ -134,11 +134,15 @@ nsphyterattach(device_t parent, device_t
 	sc->mii_pdata = mii;
 	sc->mii_flags = ma->mii_flags;
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
 	sc->mii_capabilities &= ma->mii_capmask;
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 }
 
@@ -148,6 +152,8 @@ nsphyter_service(struct mii_softc *sc, s
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -202,6 +208,8 @@ nsphyter_status(struct mii_softc *sc)
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmsr, bmcr, physts;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
@@ -253,6 +261,8 @@ nsphyter_reset(struct mii_softc *sc)
 	int i;
 	uint16_t reg;
 
+	KASSERT(mii_locked(sc->mii_pdata));
+
 	if (sc->mii_flags & MIIF_NOISOLATE)
 		reg = BMCR_RESET;
 	else
Index: dev/mii/pnaphy.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/pnaphy.c,v
retrieving revision 1.25
diff -u -p -r1.25 pnaphy.c
--- dev/mii/pnaphy.c	27 Nov 2019 10:19:21 -0000	1.25
+++ dev/mii/pnaphy.c	6 Feb 2020 00:10:02 -0000
@@ -116,11 +116,15 @@ pnaphyattach(device_t parent, device_t s
 	sc->mii_pdata = mii;
 	sc->mii_flags = ma->mii_flags | MIIF_IS_HPNA; /* Force HomePNA */
 
+	mii_lock(mii);
+
 	PHY_RESET(sc);
 
 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
 	sc->mii_capabilities &= ma->mii_capmask;
 
+	mii_unlock(mii);
+
 	mii_phy_add_media(sc);
 }
 
@@ -130,6 +134,8 @@ pnaphy_service(struct mii_softc *sc, str
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t reg;
 
+	KASSERT(mii_locked(mii));
+
 	switch (cmd) {
 	case MII_POLLSTAT:
 		/* If we're not polling our PHY instance, just return. */
@@ -184,6 +190,8 @@ pnaphy_status(struct mii_softc *sc)
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmsr, bmcr;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
Index: dev/mii/ukphy_subr.c
===================================================================
RCS file: /cvsroot/src/sys/dev/mii/ukphy_subr.c,v
retrieving revision 1.16
diff -u -p -r1.16 ukphy_subr.c
--- dev/mii/ukphy_subr.c	24 Oct 2019 03:37:58 -0000	1.16
+++ dev/mii/ukphy_subr.c	6 Feb 2020 00:10:02 -0000
@@ -60,6 +60,8 @@ ukphy_status(struct mii_softc *phy)
 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
 	uint16_t bmsr, bmcr, anar, anlpar, gtcr, gtsr, result;
 
+	KASSERT(mii_locked(mii));
+
 	mii->mii_media_status = IFM_AVALID;
 	mii->mii_media_active = IFM_ETHER;
 
Index: dev/pci/if_wm.c
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/if_wm.c,v
retrieving revision 1.666
diff -u -p -r1.666 if_wm.c
--- dev/pci/if_wm.c	31 Jan 2020 12:09:13 -0000	1.666
+++ dev/pci/if_wm.c	6 Feb 2020 00:10:02 -0000
@@ -2880,6 +2880,12 @@ alloc_retry:
 	snprintb(buf, sizeof(buf), WM_FLAGS, sc->sc_flags);
 	aprint_verbose_dev(sc->sc_dev, "%s\n", buf);
 
+#ifdef WM_MPSAFE
+	sc->sc_core_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET);
+#else
+	sc->sc_core_lock = NULL;
+#endif
+
 	/* Initialize the media structures accordingly. */
 	if (sc->sc_mediatype == WM_MEDIATYPE_COPPER)
 		wm_gmii_mediainit(sc, wmp->wmp_product);
@@ -3016,12 +3022,6 @@ alloc_retry:
 	sc->sc_rx_process_limit = WM_RX_PROCESS_LIMIT_DEFAULT;
 	sc->sc_rx_intr_process_limit = WM_RX_INTR_PROCESS_LIMIT_DEFAULT;
 
-#ifdef WM_MPSAFE
-	sc->sc_core_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET);
-#else
-	sc->sc_core_lock = NULL;
-#endif
-
 	/* Attach the interface. */
 	error = if_initialize(ifp);
 	if (error != 0) {
@@ -3102,13 +3102,13 @@ wm_detach(device_t self, int flags __unu
 
 	mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY);
 
-	/* Delete all remaining media. */
-	ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY);
-
 	ether_ifdetach(ifp);
 	if_detach(ifp);
 	if_percpuq_destroy(sc->sc_ipq);
 
+	/* Delete all remaining media. */
+	ifmedia_fini(&sc->sc_mii.mii_media);
+
 	/* Unload RX dmamaps and free mbufs */
 	for (i = 0; i < sc->sc_nqueues; i++) {
 		struct wm_rxqueue *rxq = &sc->sc_queue[i].wmq_rxq;
@@ -10423,8 +10423,8 @@ wm_gmii_mediainit(struct wm_softc *sc, p
 	wm_gmii_reset(sc);
 
 	sc->sc_ethercom.ec_mii = &sc->sc_mii;
-	ifmedia_init(&mii->mii_media, IFM_IMASK, wm_gmii_mediachange,
-	    wm_gmii_mediastatus);
+	ifmedia_init_with_lock(&mii->mii_media, IFM_IMASK, wm_gmii_mediachange,
+	    wm_gmii_mediastatus, sc->sc_core_lock);
 
 	if ((sc->sc_type == WM_T_82575) || (sc->sc_type == WM_T_82576)
 	    || (sc->sc_type == WM_T_82580)
@@ -11970,12 +11970,14 @@ wm_tbi_mediainit(struct wm_softc *sc)
 	sc->sc_ethercom.ec_mii = &sc->sc_mii;
 
 	if (((sc->sc_type >= WM_T_82575) && (sc->sc_type <= WM_T_I211))
-	    && (sc->sc_mediatype == WM_MEDIATYPE_SERDES))
-		ifmedia_init(&sc->sc_mii.mii_media, IFM_IMASK,
-		    wm_serdes_mediachange, wm_serdes_mediastatus);
-	else
-		ifmedia_init(&sc->sc_mii.mii_media, IFM_IMASK,
-		    wm_tbi_mediachange, wm_tbi_mediastatus);
+	    && (sc->sc_mediatype == WM_MEDIATYPE_SERDES)) {
+		ifmedia_init_with_lock(&sc->sc_mii.mii_media, IFM_IMASK,
+		    wm_serdes_mediachange, wm_serdes_mediastatus,
+		    sc->sc_core_lock);
+	} else {
+		ifmedia_init_with_lock(&sc->sc_mii.mii_media, IFM_IMASK,
+		    wm_tbi_mediachange, wm_tbi_mediastatus, sc->sc_core_lock);
+	}
 
 	/*
 	 * SWD Pins:
Index: net/if_media.c
===================================================================
RCS file: /cvsroot/src/sys/net/if_media.c,v
retrieving revision 1.51
diff -u -p -r1.51 if_media.c
--- net/if_media.c	1 Feb 2020 20:56:16 -0000	1.51
+++ net/if_media.c	6 Feb 2020 00:10:03 -0000
@@ -1,7 +1,7 @@
 /*	$NetBSD: if_media.c,v 1.51 2020/02/01 20:56:16 thorpej Exp $	*/
 
 /*-
- * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * Copyright (c) 1998, 2020 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This code is derived from software contributed to The NetBSD Foundation
@@ -78,6 +78,8 @@
 #include <sys/cdefs.h>
 __KERNEL_RCSID(0, "$NetBSD: if_media.c,v 1.51 2020/02/01 20:56:16 thorpej Exp $");
 
+#define	__IFMEDIA_PRIVATE
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/errno.h>
@@ -90,9 +92,12 @@ __KERNEL_RCSID(0, "$NetBSD: if_media.c,v
 #include <net/netisr.h>
 
 static void	ifmedia_status(struct ifmedia *, struct ifnet *,
-    struct ifmediareq *);
+		    struct ifmediareq *);
 static int	ifmedia_ioctl_locked(struct ifnet *, struct ifreq *,
-    struct ifmedia *, u_long);
+		    struct ifmedia *, u_long);
+
+static struct ifmedia_entry *
+		ifmedia_match_locked(struct ifmedia *, u_int, u_int);
 
 /*
  * Compile-time options:
@@ -107,19 +112,113 @@ static	void ifmedia_printword(int);
 #endif
 
 /*
+ * We need to implement a recursive mutex to handle the un-converted
+ * driver case.  For a fully MP-safe driver, the media lock will be
+ * held before calling any of the entry points that require it.  However,
+ * this is not necessarily the case for a driver that hasn't yet been
+ * converted, and the entry point calls may be nested (for example
+ * mii_ifmedia_change -> ether_mediachange -> mii_mediachg).  Luckily,
+ * the nesting won't be very deep, and 4 nested holds should be plenty.
+ */
+#define	IFM_F_OWNLOCK		0x01
+#define	IFM_F_COUNT_MASK	0x3UL
+#define	IFM_F_CPU_MASK		~(IFM_F_COUNT_MASK)
+
+void
+ifmedia_lock_for_legacy(struct ifmedia *ifm)
+{
+	uintptr_t cnt = IFM_F_OWNLOCK;
+	uintptr_t ci;
+
+	if (mutex_tryenter(ifm->ifm_lock)) {
+		goto gotit;
+	}
+
+	kpreempt_disable();
+	ci = (uintptr_t)curcpu();
+	if ((ifm->ifm_legacy & IFM_F_CPU_MASK) == ci) {
+		cnt = ifm->ifm_legacy & IFM_F_COUNT_MASK;
+		KASSERT(cnt < IFM_F_COUNT_MASK);
+		cnt++;
+		kpreempt_enable();
+		goto gotit;
+	}
+	kpreempt_enable();
+
+	mutex_enter(ifm->ifm_lock);
+ gotit:
+	KASSERT(kpreempt_disabled());
+	ci = (uintptr_t)curcpu();
+	KASSERT((ci & IFM_F_CPU_MASK) == ci);
+	ifm->ifm_legacy = ci | cnt;
+}
+
+void
+ifmedia_unlock_for_legacy(struct ifmedia *ifm)
+{
+	uintptr_t cnt;
+	uintptr_t ci = (uintptr_t)curcpu();
+
+	KASSERT(kpreempt_disabled());
+	KASSERT((ifm->ifm_legacy & IFM_F_CPU_MASK) == ci);
+	cnt = ifm->ifm_legacy & IFM_F_COUNT_MASK;
+	KASSERT(cnt != 0);
+	if (cnt == IFM_F_OWNLOCK) {
+		ifm->ifm_legacy = IFM_F_OWNLOCK;
+		mutex_exit(ifm->ifm_lock);
+		return;
+	}
+	cnt--;
+	ifm->ifm_legacy = ci | cnt;
+}
+
+/*
  * Initialize if_media struct for a specific interface instance.
  */
 void
-ifmedia_init(struct ifmedia *ifm, int dontcare_mask,
-    ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback)
+ifmedia_init_with_lock(struct ifmedia *ifm, int dontcare_mask,
+    ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback,
+    kmutex_t *lock)
 {
 
+	/*
+	 * XXX Would really like to assert:
+	 *
+	 *	((if_is_mpsafe(ifp) && lock != NULL) ||
+	 *	 (!if_is_mpsafe(ifp) && lock == NULL))
+	 *
+	 * ...but we don't have acccess to the ifnet here.
+	 */
+
 	TAILQ_INIT(&ifm->ifm_list);
 	ifm->ifm_cur = NULL;
 	ifm->ifm_media = IFM_NONE;
 	ifm->ifm_mask = dontcare_mask;		/* IF don't-care bits */
 	ifm->ifm_change = change_callback;
 	ifm->ifm_status = status_callback;
+	ifm->ifm_legacy = 0;
+
+	if (lock == NULL) {
+		/*
+		 * This is to support drivers that are not yet MP-safe
+		 * with regard to the ifmedia layer.  In these cases,
+		 * we supply the lock and we ensure it's taken upon entry
+		 * to various routines that expect it to be held.  When
+		 * we do this, we expect that the driver is in general a
+		 * non-MP-safe driver and has already gone to splnet().
+		 */
+		lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET);
+		ifm->ifm_legacy = IFM_F_OWNLOCK;
+	}
+	ifm->ifm_lock = lock;
+}
+
+void
+ifmedia_init(struct ifmedia *ifm, int dontcare_mask,
+    ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback)
+{
+	ifmedia_init_with_lock(ifm, dontcare_mask, change_callback,
+	    status_callback, NULL);
 }
 
 /*
@@ -130,12 +229,20 @@ ifmedia_fini(struct ifmedia *ifm)
 {
 
 	ifmedia_removeall(ifm);
+
+	if (ifm->ifm_legacy) {
+		KASSERT(ifm->ifm_legacy == IFM_F_OWNLOCK);
+		mutex_obj_free(ifm->ifm_lock);
+	}
+	ifm->ifm_legacy = 0;
+	ifm->ifm_lock = NULL;
 }
 
 int
 ifmedia_change(struct ifmedia *ifm, struct ifnet *ifp)
 {
 
+	KASSERT(ifmedia_locked(ifm));
 	if (ifm->ifm_change == NULL)
 		return -1;
 	return (*ifm->ifm_change)(ifp);
@@ -145,6 +252,7 @@ static void
 ifmedia_status(struct ifmedia *ifm, struct ifnet *ifp, struct ifmediareq *ifmr)
 {
 
+	KASSERT(ifmedia_locked(ifm));
 	if (ifm->ifm_status == NULL)
 		return;
 	(*ifm->ifm_status)(ifp, ifmr);
@@ -154,10 +262,10 @@ ifmedia_status(struct ifmedia *ifm, stru
  * Add a media configuration to the list of supported media
  * for a specific interface instance.
  */
-void
-ifmedia_add(struct ifmedia *ifm, int mword, int data, void *aux)
+static void
+ifmedia_add_entry(struct ifmedia *ifm, int mword, int data, void *aux,
+    struct ifmedia_entry *entry)
 {
-	struct ifmedia_entry *entry;
 
 #ifdef IFMEDIA_DEBUG
 	if (ifmedia_debug) {
@@ -170,13 +278,23 @@ ifmedia_add(struct ifmedia *ifm, int mwo
 	}
 #endif
 
-	entry = kmem_zalloc(sizeof(*entry), KM_SLEEP);
 	entry->ifm_media = mword;
 	entry->ifm_data = data;
 	entry->ifm_aux = aux;
 	TAILQ_INSERT_TAIL(&ifm->ifm_list, entry, ifm_list);
 }
 
+void
+ifmedia_add(struct ifmedia *ifm, int mword, int data, void *aux)
+{
+	struct ifmedia_entry *entry;
+
+	entry = kmem_zalloc(sizeof(*entry), KM_SLEEP);
+	ifmedia_lock(ifm);
+	ifmedia_add_entry(ifm, mword, data, aux, entry);
+	ifmedia_unlock(ifm);
+}
+
 /*
  * Add an array of media configurations to the list of
  * supported media for a specific interface instance.
@@ -201,9 +319,10 @@ ifmedia_list_add(struct ifmedia *ifm, st
 void
 ifmedia_set(struct ifmedia *ifm, int target)
 {
-	struct ifmedia_entry *match;
+	struct ifmedia_entry *match, *entry = NULL;
 
-	match = ifmedia_match(ifm, target, ifm->ifm_mask);
+	ifmedia_lock(ifm);
+	match = ifmedia_match_locked(ifm, target, ifm->ifm_mask);
 
 	/*
 	 * If we didn't find the requested media, then we try to fall
@@ -223,15 +342,28 @@ ifmedia_set(struct ifmedia *ifm, int tar
 		printf("ifmedia_set: no match for 0x%x/0x%x\n",
 		    target, ~ifm->ifm_mask);
 		target = (target & IFM_NMASK) | IFM_NONE;
-		match = ifmedia_match(ifm, target, ifm->ifm_mask);
+		match = ifmedia_match_locked(ifm, target, ifm->ifm_mask);
 		if (match == NULL) {
-			ifmedia_add(ifm, target, 0, NULL);
-			match = ifmedia_match(ifm, target, ifm->ifm_mask);
+			ifmedia_unlock(ifm);
+			entry = kmem_zalloc(sizeof(*entry), KM_SLEEP);
+			ifmedia_lock(ifm);
+			match = ifmedia_match_locked(ifm, target,
+			    ifm->ifm_mask);
+			if (match == NULL) {
+				ifmedia_add_entry(ifm, target, 0, NULL, entry);
+				entry = NULL;
+			}
+			match = ifmedia_match_locked(ifm, target,
+			    ifm->ifm_mask);
 			if (match == NULL)
 				panic("ifmedia_set failed");
 		}
 	}
 	ifm->ifm_cur = match;
+	ifmedia_unlock(ifm);
+
+	if (entry)
+		kmem_free(entry, sizeof(*entry));
 
 #ifdef IFMEDIA_DEBUG
 	if (ifmedia_debug) {
@@ -249,6 +381,8 @@ ifmedia_getwords(struct ifmedia * const 
 	struct ifmedia_entry *ep;
 	int nwords = 0;
 
+	KASSERT(ifmedia_locked(ifm));
+
 	TAILQ_FOREACH(ep, &ifm->ifm_list, ifm_list) {
 		if (words != NULL && nwords < maxwords) {
 			words[nwords] = ep->ifm_media;
@@ -259,6 +393,22 @@ ifmedia_getwords(struct ifmedia * const 
 	return nwords;
 }
 
+#define	IFMEDIA_IOCTL_LOCK(ifm)						\
+do {									\
+	if (ifmedia_islegacy(ifm))					\
+		ifmedia_lock_for_legacy(ifm);				\
+	else								\
+		ifmedia_lock(ifm);					\
+} while (/*CONSTCOND*/0)
+
+#define	IFMEDIA_IOCTL_UNLOCK(ifm)					\
+do {									\
+	if (ifmedia_islegacy(ifm))					\
+		ifmedia_unlock_for_legacy(ifm);				\
+	else								\
+		ifmedia_unlock(ifm);					\
+} while (/*CONSTCOND*/0)
+
 /*
  * Device-independent media ioctl support function.
  */
@@ -280,7 +430,9 @@ ifmedia_ioctl_locked(struct ifnet *ifp, 
 		u_int oldmedia;
 		u_int newmedia = ifr->ifr_media;
 
-		match = ifmedia_match(ifm, newmedia, ifm->ifm_mask);
+		IFMEDIA_IOCTL_LOCK(ifm);
+
+		match = ifmedia_match_locked(ifm, newmedia, ifm->ifm_mask);
 		if (match == NULL) {
 #ifdef IFMEDIA_DEBUG
 			if (ifmedia_debug) {
@@ -288,6 +440,7 @@ ifmedia_ioctl_locked(struct ifnet *ifp, 
 				    "0x%08x\n", newmedia);
 			}
 #endif
+			IFMEDIA_IOCTL_UNLOCK(ifm);
 			return EINVAL;
 		}
 
@@ -298,8 +451,10 @@ ifmedia_ioctl_locked(struct ifnet *ifp, 
 		 *     Similarly, if best match changed (kernel debugger?).
 		 */
 		if ((IFM_SUBTYPE(newmedia) != IFM_AUTO) &&
-		    (newmedia == ifm->ifm_media) && (match == ifm->ifm_cur))
+		    (newmedia == ifm->ifm_media) && (match == ifm->ifm_cur)) {
+			IFMEDIA_IOCTL_UNLOCK(ifm);
 			return 0;
+		}
 
 		/*
 		 * We found a match, now make the driver switch to it.
@@ -322,6 +477,7 @@ ifmedia_ioctl_locked(struct ifnet *ifp, 
 			ifm->ifm_cur = oldentry;
 			ifm->ifm_media = oldmedia;
 		}
+		IFMEDIA_IOCTL_UNLOCK(ifm);
 		break;
 	}
 
@@ -333,6 +489,7 @@ ifmedia_ioctl_locked(struct ifnet *ifp, 
 		if (ifmr->ifm_count < 0)
 			return EINVAL;
 
+		IFMEDIA_IOCTL_LOCK(ifm);
 		ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ?
 		    ifm->ifm_cur->ifm_media : IFM_NONE;
 		ifmr->ifm_mask = ifm->ifm_mask;
@@ -340,17 +497,20 @@ ifmedia_ioctl_locked(struct ifnet *ifp, 
 		ifmedia_status(ifm, ifp, ifmr);
 
 		/*
-		 * Count them so we know a-priori how much is the max we'll
+		 * Count them so we know how much is the max we'll
 		 * need.
 		 */
 		nwords1 = nwords2 = ifmedia_getwords(ifm, NULL, 0);
+		IFMEDIA_IOCTL_UNLOCK(ifm);
 
 		if (ifmr->ifm_count != 0) {
 			int maxwords = MIN(nwords1, ifmr->ifm_count);
 			int *kptr = kmem_zalloc(maxwords * sizeof(int),
 			    KM_SLEEP);
 
+			ifmedia_lock(ifm);
 			nwords2 = ifmedia_getwords(ifm, kptr, maxwords);
+			ifmedia_unlock(ifm);
 			error = copyout(kptr, ifmr->ifm_ulist,
 			    maxwords * sizeof(int));
 			if (error == 0 && nwords2 > nwords1)
@@ -375,21 +535,9 @@ ifmedia_ioctl(struct ifnet *ifp, struct 
 {
 	int e;
 
-	/*
-	 * If if_is_mpsafe(ifp), KERNEL_LOCK isn't held here and
-	 * ipl will not have been raised (well, maybe it has, but
-	 * it doesn't matter), but ifmedia_ioctl_locked isn't MP-safe
-	 * yet, so we go to splnet and grab the KERNEL_LOCK.
-	 *
-	 * In the non-mpsafe case, the interface's ioctl routine
-	 * will already be running at splnet() and so raising it
-	 * again is redundant, but also harmless.
-	 */
-	int s = splnet();
-	KERNEL_LOCK_IF_IFP_MPSAFE(ifp);
+	KERNEL_LOCK_UNLESS_IFP_MPSAFE(ifp);
 	e = ifmedia_ioctl_locked(ifp, ifr, ifm, cmd);
-	KERNEL_UNLOCK_IF_IFP_MPSAFE(ifp);
-	splx(s);
+	KERNEL_UNLOCK_UNLESS_IFP_MPSAFE(ifp);
 
 	return e;
 }
@@ -397,8 +545,8 @@ ifmedia_ioctl(struct ifnet *ifp, struct 
 /*
  * Find media entry matching a given ifm word.
  */
-struct ifmedia_entry *
-ifmedia_match(struct ifmedia *ifm, u_int target, u_int mask)
+static struct ifmedia_entry *
+ifmedia_match_locked(struct ifmedia *ifm, u_int target, u_int mask)
 {
 	struct ifmedia_entry *match, *next;
 
@@ -422,6 +570,22 @@ ifmedia_match(struct ifmedia *ifm, u_int
 	return match;
 }
 
+struct ifmedia_entry *
+ifmedia_match(struct ifmedia *ifm, u_int target, u_int mask)
+{
+	struct ifmedia_entry *match;
+
+	/*
+	 * N.B. We expect the caller is responsible fot the lifecycle
+	 * of the media entries.  Use with extreme caution.
+	 */
+
+	ifmedia_lock(ifm);
+	match = ifmedia_match_locked(ifm, target, mask);
+	ifmedia_unlock(ifm);
+	return match;
+}
+
 /*
  * Delete all media for a given instance.
  */
@@ -429,7 +593,11 @@ void
 ifmedia_delete_instance(struct ifmedia *ifm, u_int inst)
 {
 	struct ifmedia_entry *ife, *nife;
+	TAILQ_HEAD(, ifmedia_entry) dead_entries;
 
+	TAILQ_INIT(&dead_entries);
+
+	ifmedia_lock(ifm);
 	TAILQ_FOREACH_SAFE(ife, &ifm->ifm_list, ifm_list, nife) {
 		if (inst == IFM_INST_ANY ||
 		    inst == IFM_INST(ife->ifm_media)) {
@@ -438,9 +606,15 @@ ifmedia_delete_instance(struct ifmedia *
 				ifm->ifm_media = IFM_NONE;
 			}
 			TAILQ_REMOVE(&ifm->ifm_list, ife, ifm_list);
-			kmem_free(ife, sizeof(*ife));
+			TAILQ_INSERT_TAIL(&dead_entries, ife, ifm_list);
 		}
 	}
+	ifmedia_unlock(ifm);
+
+	TAILQ_FOREACH_SAFE(ife, &dead_entries, ifm_list, nife) {
+		TAILQ_REMOVE(&dead_entries, ife, ifm_list);
+		kmem_free(ife, sizeof(*ife));
+	}
 }
 
 void
@@ -450,7 +624,6 @@ ifmedia_removeall(struct ifmedia *ifm)
 	ifmedia_delete_instance(ifm, IFM_INST_ANY);
 }
 
-
 /*
  * Compute the interface `baudrate' from the media, for the interface
  * metrics (used by routing daemons).
Index: net/if_media.h
===================================================================
RCS file: /cvsroot/src/sys/net/if_media.h,v
retrieving revision 1.69
diff -u -p -r1.69 if_media.h
--- net/if_media.h	1 Feb 2020 20:56:16 -0000	1.69
+++ net/if_media.h	6 Feb 2020 00:10:03 -0000
@@ -1,7 +1,7 @@
 /*	$NetBSD: if_media.h,v 1.69 2020/02/01 20:56:16 thorpej Exp $	*/
 
 /*-
- * Copyright (c) 1998, 2000, 2001 The NetBSD Foundation, Inc.
+ * Copyright (c) 1998, 2000, 2001, 2020 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This code is derived from software contributed to The NetBSD Foundation
@@ -79,10 +79,6 @@
  * to implement this interface.
  */
 
-#ifdef _KERNEL
-#include <sys/queue.h>
-#endif /*_KERNEL */
-
 /*
  * Status bits. THIS IS NOT A MEDIA WORD.
  */
@@ -861,6 +857,9 @@ struct ifmedia_status_description {
 }
 
 #ifdef _KERNEL
+#include <sys/mutex.h>
+#include <sys/queue.h>
+
 /*
  * Driver callbacks for media status and change requests.
  */
@@ -880,25 +879,78 @@ struct ifmedia_entry {
 /*
  * One of these goes into a network interface's softc structure.
  * It is used to keep general media state.
+ *
+ * LOCKING
+ * =======
+ * The ifmedia it protected by a lock provided by the interface
+ * driver.  All ifmedia API entry points are expect to be called
+ * with this mutex NOT HELD.
+ *
+ * ifmedia_ioctl() is called with the interface's if_ioctl_lock held,
+ * and thus the locking order is:
+ *
+ *	IFNET_LOCK -> ifm_lock
+ *
+ * Driver callbacks (ifm_change / ifm_status) are called with ifm_lock HELD.
+ *
+ * Field markings and the corresponding locks:
+ *
+ * m:	ifm_lock
+ * ::	unlocked, stable
  */
 struct ifmedia {
-	u_int	ifm_mask;	/* IFMWD: mask of changes we don't care */
+	kmutex_t *ifm_lock;	/* :: mutex (provided by interface driver) */
+	u_int	ifm_mask;	/* :: IFMWD: mask of changes we don't care */
 	u_int	ifm_media;	/*
-				 * IFMWD: current use-set media word.
+				 * m: IFMWD: current user-set media word.
 				 *
 				 * XXX some drivers misuse this entry as
 				 * current active media word. Don't use this
 				 * entry as this purpose but use driver
 				 * specific entry if you don't use mii(4).
 				 */
-	struct ifmedia_entry *ifm_cur;	/* current user-selected media entry */
-	TAILQ_HEAD(, ifmedia_entry) ifm_list; /* list of all supported media */
-	ifm_change_cb_t	ifm_change;	/* media change driver callback */
-	ifm_stat_cb_t	ifm_status;	/* media status driver callback */
+	struct ifmedia_entry *ifm_cur;	/*
+					 * m: entry corresponding to
+					 * ifm_media
+					 */
+	TAILQ_HEAD(, ifmedia_entry) ifm_list; /*
+					       * m: list of all supported
+					       * media
+					       */
+	ifm_change_cb_t	ifm_change;	/* :: media change driver callback */
+	ifm_stat_cb_t	ifm_status;	/* :: media status driver callback */
+	uintptr_t	ifm_legacy;	/* m: legacy driver handling */
 };
 
+#define	ifmedia_lock(ifm)	mutex_enter((ifm)->ifm_lock)
+#define	ifmedia_unlock(ifm)	mutex_exit((ifm)->ifm_lock)
+#define	ifmedia_locked(ifm)	mutex_owned((ifm)->ifm_lock)
+
+#ifdef __IFMEDIA_PRIVATE
+#define	ifmedia_islegacy(ifm)	((ifm)->ifm_legacy)
+void	ifmedia_lock_for_legacy(struct ifmedia *);
+void	ifmedia_unlock_for_legacy(struct ifmedia *);
+
+#define	IFMEDIA_LOCK_FOR_LEGACY(ifm)					\
+do {									\
+	if (ifmedia_islegacy(ifm))					\
+		ifmedia_lock_for_legacy(ifm);				\
+} while (/*CONSTCOND*/0)
+
+#define	IFMEDIA_UNLOCK_FOR_LEGACY(ifm)					\
+do {									\
+	if (ifmedia_islegacy(ifm))					\
+		ifmedia_unlock_for_legacy(ifm);				\
+} while (/*CONSTCOND*/0)
+#endif /* __IFMEDIA_PRIVATE */
+
 /* Initialize an interface's struct if_media field. */
 void	ifmedia_init(struct ifmedia *, int, ifm_change_cb_t, ifm_stat_cb_t);
+void	ifmedia_init_with_lock(struct ifmedia *, int, ifm_change_cb_t,
+	    ifm_stat_cb_t, kmutex_t *);
+
+/* Release resourecs associated with an ifmedia. */
+void	ifmedia_fini(struct ifmedia *);
 
 /* Release resourecs associated with an ifmedia. */
 void	ifmedia_fini(struct ifmedia *);
-- thorpej



Home | Main Index | Thread Index | Old Index