Page MenuHomeFreeBSD

D55925.id174722.diff
No OneTemporary

D55925.id174722.diff

diff --git a/sys/dev/usb/net/if_axe.c b/sys/dev/usb/net/if_axe.c
--- a/sys/dev/usb/net/if_axe.c
+++ b/sys/dev/usb/net/if_axe.c
@@ -192,6 +192,7 @@
static miibus_readreg_t axe_miibus_readreg;
static miibus_writereg_t axe_miibus_writereg;
static miibus_statchg_t axe_miibus_statchg;
+static miibus_linkchg_t axe_miibus_linkchg;
static uether_fn_t axe_attach_post;
static uether_fn_t axe_init;
@@ -261,6 +262,7 @@
DEVMETHOD(miibus_readreg, axe_miibus_readreg),
DEVMETHOD(miibus_writereg, axe_miibus_writereg),
DEVMETHOD(miibus_statchg, axe_miibus_statchg),
+ DEVMETHOD(miibus_linkchg, axe_miibus_linkchg),
DEVMETHOD_END
};
@@ -371,8 +373,9 @@
struct axe_softc *sc = device_get_softc(dev);
struct mii_data *mii = GET_MII(sc);
if_t ifp;
- uint16_t val;
- int err, locked;
+ int err;
+ uint16_t bmsr, val;
+ bool locked, new_link, old_link;
locked = mtx_owned(&sc->sc_mtx);
if (!locked)
@@ -383,6 +386,7 @@
(if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
goto done;
+ old_link = (sc->sc_flags & AXE_FLAG_LINK) != 0;
sc->sc_flags &= ~AXE_FLAG_LINK;
if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
(IFM_ACTIVE | IFM_AVALID)) {
@@ -401,6 +405,46 @@
}
}
+ new_link = (sc->sc_flags & AXE_FLAG_LINK) != 0;
+ if (old_link != new_link) {
+ if (!new_link) {
+ /*
+ * MII layer reports link down. Verify by
+ * reading the PHY BMSR register directly.
+ * BMSR link status is latched-low, so read
+ * twice: first clears any stale latch,
+ * second gives current state.
+ */
+ axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL);
+ axe_cmd(sc, AXE_CMD_MII_READ_REG, MII_BMSR,
+ sc->sc_phyno, &val);
+ axe_cmd(sc, AXE_CMD_MII_READ_REG, MII_BMSR,
+ sc->sc_phyno, &val);
+ axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL);
+ bmsr = le16toh(val);
+
+ if ((bmsr & BMSR_LINK) == BMSR_LINK) {
+ /*
+ * PHY still has link. This is a
+ * spurious link-down event from the
+ * MII polling race (see PR 252165).
+ * Restore IFM_ACTIVE so the
+ * subsequent MIIBUS_LINKCHG check in
+ * mii_phy_update sees no status
+ * change and doesn't fire.
+ */
+ device_printf(dev,
+ "spurious link down (PHY link up), overriding\n");
+ sc->sc_flags |= AXE_FLAG_LINK;
+ mii->mii_media_status |= IFM_ACTIVE;
+ goto done;
+ }
+
+ /* PHY confirms link is genuinely down. */
+ goto done;
+ }
+ }
+
/* Lost link, do nothing. */
if ((sc->sc_flags & AXE_FLAG_LINK) == 0)
goto done;
@@ -441,6 +485,43 @@
AXE_UNLOCK(sc);
}
+static void
+axe_miibus_linkchg(device_t dev)
+{
+ struct axe_softc *sc;
+ struct mii_data *mii;
+ uint16_t bmsr, val;
+ bool locked;
+
+ sc = device_get_softc(dev);
+ mii = GET_MII(sc);
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ AXE_LOCK(sc);
+
+ /*
+ * This is called by the default miibus linkchg handler
+ * before it calls if_link_state_change(). If the PHY
+ * still has link but the MII layer lost IFM_ACTIVE due
+ * to the polling race (see PR 252165), restore it so the
+ * notification goes out as LINK_STATE_UP rather than DOWN.
+ */
+ if (mii != NULL && (mii->mii_media_status & IFM_ACTIVE) == 0) {
+ axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL);
+ axe_cmd(sc, AXE_CMD_MII_READ_REG, MII_BMSR,
+ sc->sc_phyno, &val);
+ axe_cmd(sc, AXE_CMD_MII_READ_REG, MII_BMSR,
+ sc->sc_phyno, &val);
+ axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL);
+ bmsr = le16toh(val);
+ if ((bmsr & BMSR_LINK) == BMSR_LINK)
+ mii->mii_media_status |= IFM_ACTIVE;
+ }
+
+ if (!locked)
+ AXE_UNLOCK(sc);
+}
+
/*
* Set media options.
*/
@@ -454,9 +535,17 @@
AXE_LOCK_ASSERT(sc, MA_OWNED);
+ AXE_UNLOCK(sc);
+ AXE_MII_LOCK(sc);
+ AXE_LOCK(sc);
+
LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
PHY_RESET(miisc);
error = mii_mediachg(mii);
+
+ AXE_UNLOCK(sc);
+ AXE_MII_UNLOCK(sc);
+ AXE_LOCK(sc);
return (error);
}
@@ -469,11 +558,13 @@
struct axe_softc *sc = if_getsoftc(ifp);
struct mii_data *mii = GET_MII(sc);
+ AXE_MII_LOCK(sc);
AXE_LOCK(sc);
mii_pollstat(mii);
ifmr->ifm_active = mii->mii_media_active;
ifmr->ifm_status = mii->mii_media_status;
AXE_UNLOCK(sc);
+ AXE_MII_UNLOCK(sc);
}
static u_int
@@ -940,6 +1031,7 @@
device_set_usb_desc(dev);
mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+ sx_init(&sc->sc_mii_lock, "axemii");
iface_index = AXE_IFACE_IDX;
error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
@@ -975,6 +1067,7 @@
usbd_transfer_unsetup(sc->sc_xfer, AXE_N_TRANSFER);
uether_ifdetach(ue);
+ sx_destroy(&sc->sc_mii_lock);
mtx_destroy(&sc->sc_mtx);
return (0);
@@ -1270,12 +1363,23 @@
AXE_LOCK_ASSERT(sc, MA_OWNED);
+ AXE_UNLOCK(sc);
+ if (!AXE_MII_TRYLOCK(sc)) {
+ AXE_LOCK(sc);
+ return;
+ }
+ AXE_LOCK(sc);
+
mii_tick(mii);
if ((sc->sc_flags & AXE_FLAG_LINK) == 0) {
axe_miibus_statchg(ue->ue_dev);
if ((sc->sc_flags & AXE_FLAG_LINK) != 0)
axe_start(ue);
}
+
+ AXE_UNLOCK(sc);
+ AXE_MII_UNLOCK(sc);
+ AXE_LOCK(sc);
}
static void
diff --git a/sys/dev/usb/net/if_axereg.h b/sys/dev/usb/net/if_axereg.h
--- a/sys/dev/usb/net/if_axereg.h
+++ b/sys/dev/usb/net/if_axereg.h
@@ -333,6 +333,7 @@
struct axe_softc {
struct usb_ether sc_ue;
struct mtx sc_mtx;
+ struct sx sc_mii_lock;
struct usb_xfer *sc_xfer[AXE_N_TRANSFER];
int sc_phyno;
@@ -361,3 +362,6 @@
#define AXE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
#define AXE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
#define AXE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t)
+#define AXE_MII_LOCK(_sc) sx_xlock(&(_sc)->sc_mii_lock)
+#define AXE_MII_UNLOCK(_sc) sx_xunlock(&(_sc)->sc_mii_lock)
+#define AXE_MII_TRYLOCK(_sc) sx_try_xlock(&(_sc)->sc_mii_lock)

File Metadata

Mime Type
text/plain
Expires
Sun, Apr 12, 8:03 PM (6 h, 24 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
31368096
Default Alt Text
D55925.id174722.diff (5 KB)

Event Timeline