diff options
author | marius <marius@FreeBSD.org> | 2006-02-21 20:20:43 +0000 |
---|---|---|
committer | marius <marius@FreeBSD.org> | 2006-02-21 20:20:43 +0000 |
commit | 1e99c29ded9e228ce658fd5b4a5ddc1190959b9e (patch) | |
tree | 2e39817b4a0c8db6c3b907c814975bb7f547f5b7 /sys | |
parent | 2a35422e16857720631272ac528b667695f87071 (diff) | |
download | FreeBSD-src-1e99c29ded9e228ce658fd5b4a5ddc1190959b9e.zip FreeBSD-src-1e99c29ded9e228ce658fd5b4a5ddc1190959b9e.tar.gz |
- 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
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/le/am7990.c | 41 | ||||
-rw-r--r-- | sys/dev/le/am79900.c | 41 |
2 files changed, 48 insertions, 34 deletions
diff --git a/sys/dev/le/am7990.c b/sys/dev/le/am7990.c index b626ce9..f45a48d 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 356c83c..09f604a 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 |