Module Name: src
Committed By: ozaki-r
Date: Tue Jul 1 10:35:18 UTC 2014
Modified Files:
src/sys/dev/pci: if_wm.c
Log Message:
Make if_wm MPSAFE
- Make it MPSAFE only when NET_MPSAFE
- otherwise, its instructions are almost same as before
- the only change is IFQ_POLL/IFQ_DEQUEUE which
is now single IFQ_DEQUEUE
- Protect driver operations with a lock
- further work would make it separate
- Apply MPSAFE flag to
- callout_init
- pci_intr_establish
- Stop proceeding packets when the driver is likely
to stop for graceful ifconfig down
Tested on Rangeley (I354) and KVM (e1000)
Reviewed by msaitoh@
To generate a diff of this commit:
cvs rdiff -u -r1.271 -r1.272 src/sys/dev/pci/if_wm.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/pci/if_wm.c
diff -u src/sys/dev/pci/if_wm.c:1.271 src/sys/dev/pci/if_wm.c:1.272
--- src/sys/dev/pci/if_wm.c:1.271 Mon Jun 30 06:09:44 2014
+++ src/sys/dev/pci/if_wm.c Tue Jul 1 10:35:18 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: if_wm.c,v 1.271 2014/06/30 06:09:44 ozaki-r Exp $ */
+/* $NetBSD: if_wm.c,v 1.272 2014/07/01 10:35:18 ozaki-r Exp $ */
/*
* Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc.
@@ -76,7 +76,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.271 2014/06/30 06:09:44 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.272 2014/07/01 10:35:18 ozaki-r Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -141,6 +141,10 @@ int wm_debug = WM_DEBUG_TX | WM_DEBUG_RX
#define DPRINTF(x, y) /* nothing */
#endif /* WM_DEBUG */
+#ifdef NET_MPSAFE
+#define WM_MPSAFE 1
+#endif
+
/*
* Transmit descriptor list size. Due to errata, we can only have
* 256 hardware descriptors in the ring on < 82544, but we use 4096
@@ -275,6 +279,7 @@ struct wm_softc {
void *sc_ih; /* interrupt cookie */
callout_t sc_tick_ch; /* tick callout */
+ bool sc_stopping;
int sc_ee_addrbits; /* EEPROM address bits */
int sc_ich8_flash_base;
@@ -380,8 +385,21 @@ struct wm_softc {
int sc_mchash_type; /* multicast filter offset */
krndsource_t rnd_source; /* random source */
+
+ kmutex_t *sc_txrx_lock; /* lock for tx/rx operations */
+ /* XXX need separation? */
};
+#define WM_LOCK(_sc) if ((_sc)->sc_txrx_lock) mutex_enter((_sc)->sc_txrx_lock)
+#define WM_UNLOCK(_sc) if ((_sc)->sc_txrx_lock) mutex_exit((_sc)->sc_txrx_lock)
+#define WM_LOCKED(_sc) (!(_sc)->sc_txrx_lock || mutex_owned((_sc)->sc_txrx_lock))
+
+#ifdef WM_MPSAFE
+#define CALLOUT_FLAGS CALLOUT_MPSAFE
+#else
+#define CALLOUT_FLAGS 0
+#endif
+
#define WM_RXCHAIN_RESET(sc) \
do { \
(sc)->sc_rxtailp = &(sc)->sc_rxhead; \
@@ -495,12 +513,16 @@ do { \
} while (/*CONSTCOND*/0)
static void wm_start(struct ifnet *);
+static void wm_start_locked(struct ifnet *);
static void wm_nq_start(struct ifnet *);
+static void wm_nq_start_locked(struct ifnet *);
static void wm_watchdog(struct ifnet *);
static int wm_ifflags_cb(struct ethercom *);
static int wm_ioctl(struct ifnet *, u_long, void *);
static int wm_init(struct ifnet *);
+static int wm_init_locked(struct ifnet *);
static void wm_stop(struct ifnet *, int);
+static void wm_stop_locked(struct ifnet *, int);
static bool wm_suspend(device_t, const pmf_qual_t *);
static bool wm_resume(device_t, const pmf_qual_t *);
@@ -1184,7 +1206,8 @@ wm_attach(device_t parent, device_t self
char intrbuf[PCI_INTRSTR_LEN];
sc->sc_dev = self;
- callout_init(&sc->sc_tick_ch, 0);
+ callout_init(&sc->sc_tick_ch, CALLOUT_FLAGS);
+ sc->sc_stopping = false;
sc->sc_wmp = wmp = wm_lookup(pa);
if (wmp == NULL) {
@@ -1315,6 +1338,9 @@ wm_attach(device_t parent, device_t self
return;
}
intrstr = pci_intr_string(pc, ih, intrbuf, sizeof(intrbuf));
+#ifdef WM_MPSAFE
+ pci_intr_setattr(pc, &ih, PCI_INTR_MPSAFE, true);
+#endif
sc->sc_ih = pci_intr_establish(pc, ih, IPL_NET, wm_intr, sc);
if (sc->sc_ih == NULL) {
aprint_error_dev(sc->sc_dev, "unable to establish interrupt");
@@ -1354,7 +1380,7 @@ wm_attach(device_t parent, device_t self
aprint_verbose_dev(sc->sc_dev,
"Communication Streaming Architecture\n");
if (sc->sc_type == WM_T_82547) {
- callout_init(&sc->sc_txfifo_ch, 0);
+ callout_init(&sc->sc_txfifo_ch, CALLOUT_FLAGS);
callout_setfunc(&sc->sc_txfifo_ch,
wm_82547_txfifo_stall, sc);
aprint_verbose_dev(sc->sc_dev,
@@ -2051,6 +2077,12 @@ wm_attach(device_t parent, device_t self
ifp->if_capabilities |= IFCAP_TSOv6;
}
+#ifdef WM_MPSAFE
+ sc->sc_txrx_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET);
+#else
+ sc->sc_txrx_lock = NULL;
+#endif
+
/*
* Attach the interface.
*/
@@ -2159,18 +2191,26 @@ wm_detach(device_t self, int flags __unu
{
struct wm_softc *sc = device_private(self);
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
- int i, s;
+ int i;
+#ifndef WM_MPSAFE
+ int s;
s = splnet();
+#endif
/* Stop the interface. Callouts are stopped in it. */
wm_stop(ifp, 1);
+
+#ifndef WM_MPSAFE
splx(s);
+#endif
pmf_device_deregister(self);
/* Tell the firmware about the release */
+ WM_LOCK(sc);
wm_release_manageability(sc);
wm_release_hw_control(sc);
+ WM_UNLOCK(sc);
mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY);
@@ -2182,7 +2222,10 @@ wm_detach(device_t self, int flags __unu
/* Unload RX dmamaps and free mbufs */
+ WM_LOCK(sc);
wm_rxdrain(sc);
+ WM_UNLOCK(sc);
+ /* Must unlock here */
/* Free dmamap. It's the same as the end of the wm_attach() function */
for (i = 0; i < WM_NRXDESC; i++) {
@@ -2218,6 +2261,9 @@ wm_detach(device_t self, int flags __unu
sc->sc_ios = 0;
}
+ if (sc->sc_txrx_lock)
+ mutex_obj_free(sc->sc_txrx_lock);
+
return 0;
}
@@ -2441,9 +2487,15 @@ static void
wm_82547_txfifo_stall(void *arg)
{
struct wm_softc *sc = arg;
+#ifndef WM_MPSAFE
int s;
s = splnet();
+#endif
+ WM_LOCK(sc);
+
+ if (sc->sc_stopping)
+ goto out;
if (sc->sc_txfifo_stall) {
if (CSR_READ(sc, WMREG_TDT) == CSR_READ(sc, WMREG_TDH) &&
@@ -2465,7 +2517,7 @@ wm_82547_txfifo_stall(void *arg)
sc->sc_txfifo_head = 0;
sc->sc_txfifo_stall = 0;
- wm_start(&sc->sc_ethercom.ec_if);
+ wm_start_locked(&sc->sc_ethercom.ec_if);
} else {
/*
* Still waiting for packets to drain; try again in
@@ -2475,7 +2527,11 @@ wm_82547_txfifo_stall(void *arg)
}
}
+out:
+ WM_UNLOCK(sc);
+#ifndef WM_MPSAFE
splx(s);
+#endif
}
static void
@@ -2546,6 +2602,17 @@ static void
wm_start(struct ifnet *ifp)
{
struct wm_softc *sc = ifp->if_softc;
+
+ WM_LOCK(sc);
+ if (!sc->sc_stopping)
+ wm_start_locked(ifp);
+ WM_UNLOCK(sc);
+}
+
+static void
+wm_start_locked(struct ifnet *ifp)
+{
+ struct wm_softc *sc = ifp->if_softc;
struct mbuf *m0;
struct m_tag *mtag;
struct wm_txsoft *txs;
@@ -2556,6 +2623,8 @@ wm_start(struct ifnet *ifp)
uint32_t cksumcmd;
uint8_t cksumfields;
+ KASSERT(WM_LOCKED(sc));
+
if ((ifp->if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING)
return;
@@ -2570,14 +2639,7 @@ wm_start(struct ifnet *ifp)
* descriptors.
*/
for (;;) {
- /* Grab a packet off the queue. */
- IFQ_POLL(&ifp->if_snd, m0);
- if (m0 == NULL)
- break;
-
- DPRINTF(WM_DEBUG_TX,
- ("%s: TX: have packet to transmit: %p\n",
- device_xname(sc->sc_dev), m0));
+ m0 = NULL;
/* Get a work queue entry. */
if (sc->sc_txsfree < WM_TXQUEUE_GC(sc)) {
@@ -2591,6 +2653,15 @@ wm_start(struct ifnet *ifp)
}
}
+ /* Grab a packet off the queue. */
+ IFQ_DEQUEUE(&ifp->if_snd, m0);
+ if (m0 == NULL)
+ break;
+
+ DPRINTF(WM_DEBUG_TX,
+ ("%s: TX: have packet to transmit: %p\n",
+ device_xname(sc->sc_dev), m0));
+
txs = &sc->sc_txsoft[sc->sc_txsnext];
dmamap = txs->txs_dmamap;
@@ -2627,7 +2698,6 @@ wm_start(struct ifnet *ifp)
log(LOG_ERR, "%s: Tx packet consumes too many "
"DMA segments, dropping...\n",
device_xname(sc->sc_dev));
- IFQ_DEQUEUE(&ifp->if_snd, m0);
wm_dump_mbuf_chain(sc, m0);
m_freem(m0);
continue;
@@ -2688,8 +2758,6 @@ wm_start(struct ifnet *ifp)
break;
}
- IFQ_DEQUEUE(&ifp->if_snd, m0);
-
/*
* WE ARE NOW COMMITTED TO TRANSMITTING THE PACKET.
*/
@@ -2833,6 +2901,13 @@ wm_start(struct ifnet *ifp)
bpf_mtap(ifp, m0);
}
+ if (m0 != NULL) {
+ ifp->if_flags |= IFF_OACTIVE;
+ WM_EVCNT_INCR(&sc->sc_ev_txdrop);
+ DPRINTF(WM_DEBUG_TX, ("%s: TX: error after IFQ_DEQUEUE\n", __func__));
+ m_freem(m0);
+ }
+
if (sc->sc_txsfree == 0 || sc->sc_txfree <= 2) {
/* No more slots; notify upper layer. */
ifp->if_flags |= IFF_OACTIVE;
@@ -3053,6 +3128,17 @@ static void
wm_nq_start(struct ifnet *ifp)
{
struct wm_softc *sc = ifp->if_softc;
+
+ WM_LOCK(sc);
+ if (!sc->sc_stopping)
+ wm_nq_start_locked(ifp);
+ WM_UNLOCK(sc);
+}
+
+static void
+wm_nq_start_locked(struct ifnet *ifp)
+{
+ struct wm_softc *sc = ifp->if_softc;
struct mbuf *m0;
struct m_tag *mtag;
struct wm_txsoft *txs;
@@ -3060,6 +3146,8 @@ wm_nq_start(struct ifnet *ifp)
int error, nexttx, lasttx = -1, seg, segs_needed;
bool do_csum, sent;
+ KASSERT(WM_LOCKED(sc));
+
if ((ifp->if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING)
return;
@@ -3071,14 +3159,7 @@ wm_nq_start(struct ifnet *ifp)
* descriptors.
*/
for (;;) {
- /* Grab a packet off the queue. */
- IFQ_POLL(&ifp->if_snd, m0);
- if (m0 == NULL)
- break;
-
- DPRINTF(WM_DEBUG_TX,
- ("%s: TX: have packet to transmit: %p\n",
- device_xname(sc->sc_dev), m0));
+ m0 = NULL;
/* Get a work queue entry. */
if (sc->sc_txsfree < WM_TXQUEUE_GC(sc)) {
@@ -3092,6 +3173,15 @@ wm_nq_start(struct ifnet *ifp)
}
}
+ /* Grab a packet off the queue. */
+ IFQ_DEQUEUE(&ifp->if_snd, m0);
+ if (m0 == NULL)
+ break;
+
+ DPRINTF(WM_DEBUG_TX,
+ ("%s: TX: have packet to transmit: %p\n",
+ device_xname(sc->sc_dev), m0));
+
txs = &sc->sc_txsoft[sc->sc_txsnext];
dmamap = txs->txs_dmamap;
@@ -3111,7 +3201,6 @@ wm_nq_start(struct ifnet *ifp)
log(LOG_ERR, "%s: Tx packet consumes too many "
"DMA segments, dropping...\n",
device_xname(sc->sc_dev));
- IFQ_DEQUEUE(&ifp->if_snd, m0);
wm_dump_mbuf_chain(sc, m0);
m_freem(m0);
continue;
@@ -3152,8 +3241,6 @@ wm_nq_start(struct ifnet *ifp)
break;
}
- IFQ_DEQUEUE(&ifp->if_snd, m0);
-
/*
* WE ARE NOW COMMITTED TO TRANSMITTING THE PACKET.
*/
@@ -3310,6 +3397,13 @@ wm_nq_start(struct ifnet *ifp)
bpf_mtap(ifp, m0);
}
+ if (m0 != NULL) {
+ ifp->if_flags |= IFF_OACTIVE;
+ WM_EVCNT_INCR(&sc->sc_ev_txdrop);
+ DPRINTF(WM_DEBUG_TX, ("%s: TX: error after IFQ_DEQUEUE\n", __func__));
+ m_freem(m0);
+ }
+
if (sc->sc_txsfree == 0 || sc->sc_txfree <= 2) {
/* No more slots; notify upper layer. */
ifp->if_flags |= IFF_OACTIVE;
@@ -3335,7 +3429,9 @@ wm_watchdog(struct ifnet *ifp)
* Since we're using delayed interrupts, sweep up
* before we report an error.
*/
+ WM_LOCK(sc);
wm_txintr(sc);
+ WM_UNLOCK(sc);
if (sc->sc_txfree != WM_NTXDESC(sc)) {
#ifdef WM_DEBUG
@@ -3379,19 +3475,27 @@ wm_ifflags_cb(struct ethercom *ec)
struct ifnet *ifp = &ec->ec_if;
struct wm_softc *sc = ifp->if_softc;
int change = ifp->if_flags ^ sc->sc_if_flags;
+ int rc = 0;
+
+ WM_LOCK(sc);
if (change != 0)
sc->sc_if_flags = ifp->if_flags;
- if ((change & ~(IFF_CANTCHANGE|IFF_DEBUG)) != 0)
- return ENETRESET;
+ if ((change & ~(IFF_CANTCHANGE|IFF_DEBUG)) != 0) {
+ rc = ENETRESET;
+ goto out;
+ }
if ((change & (IFF_PROMISC | IFF_ALLMULTI)) != 0)
wm_set_filter(sc);
wm_set_vlan(sc);
- return 0;
+out:
+ WM_UNLOCK(sc);
+
+ return rc;
}
/*
@@ -3408,7 +3512,10 @@ wm_ioctl(struct ifnet *ifp, u_long cmd,
struct sockaddr_dl *sdl;
int s, error;
+#ifndef WM_MPSAFE
s = splnet();
+#endif
+ WM_LOCK(sc);
switch (cmd) {
case SIOCSIFMEDIA:
@@ -3439,14 +3546,27 @@ wm_ioctl(struct ifnet *ifp, u_long cmd,
}
/*FALLTHROUGH*/
default:
- if ((error = ether_ioctl(ifp, cmd, data)) != ENETRESET)
+ WM_UNLOCK(sc);
+#ifdef WM_MPSAFE
+ s = splnet();
+#endif
+ /* It may call wm_start, so unlock here */
+ error = ether_ioctl(ifp, cmd, data);
+#ifdef WM_MPSAFE
+ splx(s);
+#endif
+ WM_LOCK(sc);
+
+ if (error != ENETRESET)
break;
error = 0;
- if (cmd == SIOCSIFCAP)
+ if (cmd == SIOCSIFCAP) {
+ WM_UNLOCK(sc);
error = (*ifp->if_init)(ifp);
- else if (cmd != SIOCADDMULTI && cmd != SIOCDELMULTI)
+ WM_LOCK(sc);
+ } else if (cmd != SIOCADDMULTI && cmd != SIOCDELMULTI)
;
else if (ifp->if_flags & IFF_RUNNING) {
/*
@@ -3458,10 +3578,14 @@ wm_ioctl(struct ifnet *ifp, u_long cmd,
break;
}
+ WM_UNLOCK(sc);
+
/* Try to get more packets going. */
ifp->if_start(ifp);
+#ifndef WM_MPSAFE
splx(s);
+#endif
return error;
}
@@ -3484,6 +3608,13 @@ wm_intr(void *arg)
break;
rnd_add_uint32(&sc->rnd_source, icr);
+ WM_LOCK(sc);
+
+ if (sc->sc_stopping) {
+ WM_UNLOCK(sc);
+ break;
+ }
+
handled = 1;
#if defined(WM_DEBUG) || defined(WM_EVENT_COUNTERS)
@@ -3512,6 +3643,8 @@ wm_intr(void *arg)
wm_linkintr(sc, icr);
}
+ WM_UNLOCK(sc);
+
if (icr & ICR_RXO) {
#if defined(WM_DEBUG)
log(LOG_WARNING, "%s: Receive overrun\n",
@@ -3541,6 +3674,9 @@ wm_txintr(struct wm_softc *sc)
uint8_t status;
int i;
+ if (sc->sc_stopping)
+ return;
+
ifp->if_flags &= ~IFF_OACTIVE;
/*
@@ -3814,11 +3950,18 @@ wm_rxintr(struct wm_softc *sc)
ifp->if_ipackets++;
+ WM_UNLOCK(sc);
+
/* Pass this up to any BPF listeners. */
bpf_mtap(ifp, m);
/* Pass it on. */
(*ifp->if_input)(ifp, m);
+
+ WM_LOCK(sc);
+
+ if (sc->sc_stopping)
+ break;
}
/* Update the receive pointer. */
@@ -3837,6 +3980,8 @@ static void
wm_linkintr_gmii(struct wm_softc *sc, uint32_t icr)
{
+ KASSERT(WM_LOCKED(sc));
+
DPRINTF(WM_DEBUG_LINK, ("%s: %s:\n", device_xname(sc->sc_dev),
__func__));
@@ -3997,9 +4142,16 @@ wm_tick(void *arg)
{
struct wm_softc *sc = arg;
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
+#ifndef WM_MPSAFE
int s;
s = splnet();
+#endif
+
+ WM_LOCK(sc);
+
+ if (sc->sc_stopping)
+ goto out;
if (sc->sc_type >= WM_T_82542_2_1) {
WM_EVCNT_ADD(&sc->sc_ev_rx_xon, CSR_READ(sc, WMREG_XONRXC));
@@ -4025,9 +4177,14 @@ wm_tick(void *arg)
else
wm_tbi_check_link(sc);
+out:
+ WM_UNLOCK(sc);
+#ifndef WM_MPSAFE
splx(s);
+#endif
- callout_reset(&sc->sc_tick_ch, hz, wm_tick, sc);
+ if (!sc->sc_stopping)
+ callout_reset(&sc->sc_tick_ch, hz, wm_tick, sc);
}
/*
@@ -4406,16 +4563,30 @@ wm_set_vlan(struct wm_softc *sc)
/*
* wm_init: [ifnet interface function]
*
- * Initialize the interface. Must be called at splnet().
+ * Initialize the interface.
*/
static int
wm_init(struct ifnet *ifp)
{
struct wm_softc *sc = ifp->if_softc;
+ int ret;
+
+ WM_LOCK(sc);
+ ret = wm_init_locked(ifp);
+ WM_UNLOCK(sc);
+
+ return ret;
+}
+
+static int
+wm_init_locked(struct ifnet *ifp)
+{
+ struct wm_softc *sc = ifp->if_softc;
struct wm_rxsoft *rxs;
int i, j, trynum, error = 0;
uint32_t reg;
+ KASSERT(WM_LOCKED(sc));
/*
* *_HDR_ALIGNED_P is constant 1 if __NO_STRICT_ALIGMENT is set.
* There is a small but measurable benefit to avoiding the adjusment
@@ -4437,7 +4608,7 @@ wm_init(struct ifnet *ifp)
#endif /* __NO_STRICT_ALIGNMENT */
/* Cancel any pending I/O. */
- wm_stop(ifp, 0);
+ wm_stop_locked(ifp, 0);
/* update statistics before reset */
ifp->if_collisions += CSR_READ(sc, WMREG_COLC);
@@ -4854,6 +5025,8 @@ wm_init(struct ifnet *ifp)
for (i = 0; i < WM_NRXDESC; i++)
WM_INIT_RXDESC(sc, i);
+ sc->sc_stopping = false;
+
/* Start the one second link check clock. */
callout_reset(&sc->sc_tick_ch, hz, wm_tick, sc);
@@ -4880,6 +5053,8 @@ wm_rxdrain(struct wm_softc *sc)
struct wm_rxsoft *rxs;
int i;
+ KASSERT(WM_LOCKED(sc));
+
for (i = 0; i < WM_NRXDESC; i++) {
rxs = &sc->sc_rxsoft[i];
if (rxs->rxs_mbuf != NULL) {
@@ -4899,9 +5074,23 @@ static void
wm_stop(struct ifnet *ifp, int disable)
{
struct wm_softc *sc = ifp->if_softc;
+
+ WM_LOCK(sc);
+ wm_stop_locked(ifp, disable);
+ WM_UNLOCK(sc);
+}
+
+static void
+wm_stop_locked(struct ifnet *ifp, int disable)
+{
+ struct wm_softc *sc = ifp->if_softc;
struct wm_txsoft *txs;
int i;
+ KASSERT(WM_LOCKED(sc));
+
+ sc->sc_stopping = true;
+
/* Stop the one second clock. */
callout_stop(&sc->sc_tick_ch);
@@ -5680,6 +5869,8 @@ wm_add_rxbuf(struct wm_softc *sc, int id
struct mbuf *m;
int error;
+ KASSERT(WM_LOCKED(sc));
+
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL)
return ENOBUFS;
@@ -6131,6 +6322,8 @@ wm_tbi_check_link(struct wm_softc *sc)
struct ifmedia_entry *ife = sc->sc_mii.mii_media.ifm_cur;
uint32_t status;
+ KASSERT(WM_LOCKED(sc));
+
status = CSR_READ(sc, WMREG_STATUS);
/* XXX is this needed? */
@@ -6156,8 +6349,10 @@ wm_tbi_check_link(struct wm_softc *sc)
/* RXCFG storm! */
DPRINTF(WM_DEBUG_LINK, ("RXCFG storm! (%d)\n",
sc->sc_tbi_nrxcfg - sc->sc_tbi_lastnrxcfg));
- wm_init(ifp);
+ wm_init_locked(ifp);
+ WM_UNLOCK(sc);
ifp->if_start(ifp);
+ WM_LOCK(sc);
} else if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
/* If the timer expired, retry autonegotiation */
if (++sc->sc_tbi_ticks >= sc->sc_tbi_anegticks) {