aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/le
diff options
context:
space:
mode:
authorMarius Strobl <marius@FreeBSD.org>2006-02-21 20:20:43 +0000
committerMarius Strobl <marius@FreeBSD.org>2006-02-21 20:20:43 +0000
commit0c05ab41ab49810945b69e861df3bf3ac9e13308 (patch)
tree2e39817b4a0c8db6c3b907c814975bb7f547f5b7 /sys/dev/le
parent65986ff98dfea5e24e2a29bbda4dcc98de8ef043 (diff)
- In the interrupt handler clear the interrupt source flags before
processing the interrupt events. If we clear them afterwards we can completely miss some events as the NIC can change the source flags while we're in the handler. In order to not get another interrupt while we're in ifp->if_input() with the driver lock dropped we now turn off NIC interrupts while in the interrupt handler. Previously this was meant to be achieved by clearing the interrupt source flags after processing the interrupt events but didn't really work as clearing these flags doesn't actually acknowledge and re-enable the events. This fixes the device timeouts seen with the VMware LANCE. - Relax the watchdog timer somewhat; don't enable it until the last packet is enqueued and if there is a TX interrupt but there are still outstanding ones reload the timer. Reported and tested by: Morten Rodal <morten@rodal.no> MFC after: 3 days
Notes
Notes: svn path=/head/; revision=155881
Diffstat (limited to 'sys/dev/le')
-rw-r--r--sys/dev/le/am7990.c41
-rw-r--r--sys/dev/le/am79900.c41
2 files changed, 48 insertions, 34 deletions
diff --git a/sys/dev/le/am7990.c b/sys/dev/le/am7990.c
index b626ce9d6867..f45a48d8e1ef 100644
--- a/sys/dev/le/am7990.c
+++ b/sys/dev/le/am7990.c
@@ -357,10 +357,7 @@ am7990_tint(struct lance_softc *sc)
sc->sc_first_td = bix;
- am7990_start_locked(sc);
-
- if (sc->sc_no_td == 0)
- ifp->if_timer = 0;
+ ifp->if_timer = sc->sc_no_td > 0 ? 5 : 0;
}
/*
@@ -392,6 +389,18 @@ am7990_intr(void *arg)
return;
}
+ /*
+ * Clear interrupt source flags and turn off interrupts. If we
+ * don't clear these flags before processing their sources we
+ * could completely miss some interrupt events as the the NIC
+ * can change these flags while we're in this handler. We turn
+ * of interrupts while processing them so we don't get another
+ * one while we still process the previous one in ifp->if_input()
+ * with the driver lock dropped.
+ */
+ (*sc->sc_wrcsr)(sc, LE_CSR0, isr & ~(LE_C0_INEA | LE_C0_TDMD |
+ LE_C0_STOP | LE_C0_STRT | LE_C0_INIT));
+
if (isr & LE_C0_ERR) {
if (isr & LE_C0_BABL) {
#ifdef LEDEBUG
@@ -446,16 +455,11 @@ am7990_intr(void *arg)
if (isr & LE_C0_TINT)
am7990_tint(sc);
- /*
- * Note that since we drop the driver lock in lance_read() we might
- * get another interrupt while in ifp->if_input(). Consequently we
- * don't want to acknowledge a receive interrupt before it's fully
- * serviced. We could acknowledge interrupts of other types earlier
- * but that won't buy us much as as the driver lock is held until
- * the end of this ISR.
- */
- (*sc->sc_wrcsr)(sc, LE_CSR0, isr & (LE_C0_INEA | LE_C0_BABL |
- LE_C0_MISS | LE_C0_MERR | LE_C0_RINT | LE_C0_TINT | LE_C0_IDON));
+ /* Enable interrupts again. */
+ (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA);
+
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ am7990_start_locked(sc);
LE_UNLOCK(sc);
}
@@ -471,7 +475,7 @@ am7990_start_locked(struct lance_softc *sc)
struct ifnet *ifp = sc->sc_ifp;
struct letmd tmd;
struct mbuf *m;
- int bix, len, rp;
+ int bix, enq, len, rp;
LE_LOCK_ASSERT(sc, MA_OWNED);
@@ -480,6 +484,7 @@ am7990_start_locked(struct lance_softc *sc)
return;
bix = sc->sc_last_td;
+ enq = 0;
for (; sc->sc_no_td < sc->sc_ntbuf &&
!IFQ_DRV_IS_EMPTY(&ifp->if_snd);) {
@@ -513,8 +518,6 @@ am7990_start_locked(struct lance_softc *sc)
if_printf(ifp, "packet length %d\n", len);
#endif
- ifp->if_timer = 5;
-
/*
* Init transmit registers, and set transmit start flag.
*/
@@ -530,6 +533,7 @@ am7990_start_locked(struct lance_softc *sc)
#endif
(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA | LE_C0_TDMD);
+ enq++;
if (++bix == sc->sc_ntbuf)
bix = 0;
@@ -541,6 +545,9 @@ am7990_start_locked(struct lance_softc *sc)
}
sc->sc_last_td = bix;
+
+ if (enq > 0)
+ ifp->if_timer = 5;
}
#ifdef LEDEBUG
diff --git a/sys/dev/le/am79900.c b/sys/dev/le/am79900.c
index 356c83c7f82e..09f604aa2e98 100644
--- a/sys/dev/le/am79900.c
+++ b/sys/dev/le/am79900.c
@@ -399,10 +399,7 @@ am79900_tint(struct lance_softc *sc)
sc->sc_first_td = bix;
- am79900_start_locked(sc);
-
- if (sc->sc_no_td == 0)
- ifp->if_timer = 0;
+ ifp->if_timer = sc->sc_no_td > 0 ? 5 : 0;
}
/*
@@ -434,6 +431,18 @@ am79900_intr(void *arg)
return;
}
+ /*
+ * Clear interrupt source flags and turn off interrupts. If we
+ * don't clear these flags before processing their sources we
+ * could completely miss some interrupt events as the the NIC
+ * can change these flags while we're in this handler. We turn
+ * of interrupts while processing them so we don't get another
+ * one while we still process the previous one in ifp->if_input()
+ * with the driver lock dropped.
+ */
+ (*sc->sc_wrcsr)(sc, LE_CSR0, isr & ~(LE_C0_INEA | LE_C0_TDMD |
+ LE_C0_STOP | LE_C0_STRT | LE_C0_INIT));
+
if (isr & LE_C0_ERR) {
if (isr & LE_C0_BABL) {
#ifdef LEDEBUG
@@ -488,16 +497,11 @@ am79900_intr(void *arg)
if (isr & LE_C0_TINT)
am79900_tint(sc);
- /*
- * Note that since we drop the driver lock in lance_read() we might
- * get another interrupt while in ifp->if_input(). Consequently we
- * don't want to acknowledge a receive interrupt before it's fully
- * serviced. We could acknowledge interrupts of other types earlier
- * but that won't buy us much as as the driver lock is held until
- * the end of this ISR.
- */
- (*sc->sc_wrcsr)(sc, LE_CSR0, isr & (LE_C0_INEA | LE_C0_BABL |
- LE_C0_MISS | LE_C0_MERR | LE_C0_RINT | LE_C0_TINT | LE_C0_IDON));
+ /* Enable interrupts again. */
+ (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA);
+
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ am79900_start_locked(sc);
LE_UNLOCK(sc);
}
@@ -513,7 +517,7 @@ am79900_start_locked(struct lance_softc *sc)
struct ifnet *ifp = sc->sc_ifp;
struct letmd tmd;
struct mbuf *m;
- int bix, len, rp;
+ int bix, enq, len, rp;
LE_LOCK_ASSERT(sc, MA_OWNED);
@@ -522,6 +526,7 @@ am79900_start_locked(struct lance_softc *sc)
return;
bix = sc->sc_last_td;
+ enq = 0;
for (; sc->sc_no_td < sc->sc_ntbuf &&
!IFQ_DRV_IS_EMPTY(&ifp->if_snd);) {
@@ -555,8 +560,6 @@ am79900_start_locked(struct lance_softc *sc)
if_printf(ifp, "packet length %d\n", len);
#endif
- ifp->if_timer = 5;
-
/*
* Init transmit registers, and set transmit start flag.
*/
@@ -573,6 +576,7 @@ am79900_start_locked(struct lance_softc *sc)
#endif
(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA | LE_C0_TDMD);
+ enq++;
if (++bix == sc->sc_ntbuf)
bix = 0;
@@ -584,6 +588,9 @@ am79900_start_locked(struct lance_softc *sc)
}
sc->sc_last_td = bix;
+
+ if (enq > 0)
+ ifp->if_timer = 5;
}
#ifdef LEDEBUG