From 303841c92ead93581f40e2deb88258febe832a89 Mon Sep 17 00:00:00 2001 From: christos Date: Sat, 10 Dec 1994 14:52:02 +0000 Subject: [PATCH] 3c509 fixes to avoid adapter getting stuck in high traffic conditions. I don't particularly like the fixes, but at least now the 3c509 never gets stuck. On the other hand, performance still sucks in some cases. --- sys/arch/i386/isa/if_ep.c | 195 +++++++++++++++++++++++++++-------- sys/arch/i386/isa/if_epreg.h | 41 +++++++- sys/dev/isa/if_ep.c | 195 +++++++++++++++++++++++++++-------- sys/dev/isa/if_epreg.h | 41 +++++++- 4 files changed, 386 insertions(+), 86 deletions(-) diff --git a/sys/arch/i386/isa/if_ep.c b/sys/arch/i386/isa/if_ep.c index 28d9afbaf2a1..79c1218ed8c4 100644 --- a/sys/arch/i386/isa/if_ep.c +++ b/sys/arch/i386/isa/if_ep.c @@ -1,4 +1,4 @@ -/* $NetBSD: if_ep.c,v 1.62 1994/11/25 23:19:03 christos Exp $ */ +/* $NetBSD: if_ep.c,v 1.63 1994/12/10 14:52:02 christos Exp $ */ /* * Copyright (c) 1994 Herb Peyerl @@ -90,8 +90,9 @@ struct ep_softc { int next_mb; /* Which mbuf to use next. */ int last_mb; /* Last mbuf. */ int tx_start_thresh; /* Current TX_start_thresh. */ - int tx_succ_ok; /* # packets sent in sequence w/o underrun */ - char bus32bit; /* 32bit access possible */ + int tx_succ_ok; /* # packets sent in sequence */ + /* w/o underrun */ + char bus32bit; /* 32bit access possible */ }; static int epprobe __P((struct device *, void *, void *)); @@ -102,6 +103,8 @@ struct cfdriver epcd = { }; int epintr __P((struct ep_softc *)); +static void epxstat __P((struct ep_softc *)); +static int epstatus __P((struct ep_softc *)); static void epinit __P((struct ep_softc *)); static int epioctl __P((struct ifnet *, u_long, caddr_t)); static int epstart __P((struct ifnet *)); @@ -330,7 +333,8 @@ epattach(parent, self, aux) ether_ifattach(ifp); #if NBPFILTER > 0 - bpfattach(&sc->sc_arpcom.ac_if.if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); + bpfattach(&sc->sc_arpcom.ac_if.if_bpf, ifp, DLT_EN10MB, + sizeof(struct ether_header)); #endif sc->tx_start_thresh = 20; /* probably a good starting point. */ @@ -376,10 +380,10 @@ epinit(sc) for (i = 0; i < 31; i++) inb(BASE + EP_W1_TX_STATUS); - outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_CARD_FAILURE | S_RX_COMPLETE | - S_TX_COMPLETE | S_TX_AVAIL); - outw(BASE + EP_COMMAND, SET_INTR_MASK | S_CARD_FAILURE | S_RX_COMPLETE | - S_TX_COMPLETE | S_TX_AVAIL); + outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_CARD_FAILURE | + S_RX_COMPLETE | S_TX_COMPLETE | S_TX_AVAIL); + outw(BASE + EP_COMMAND, SET_INTR_MASK | S_CARD_FAILURE | + S_RX_COMPLETE | S_TX_COMPLETE | S_TX_AVAIL); /* * Attempt to get rid of any stray interrupts that occured during @@ -526,22 +530,23 @@ startagain: if (sc->bus32bit) { for (m = m0; m; ) { if (m->m_len > 3) - outsl(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), - m->m_len/4); + outsl(BASE + EP_W1_TX_PIO_WR_1, + mtod(m, caddr_t), m->m_len / 4); if (m->m_len & 3) outsb(BASE + EP_W1_TX_PIO_WR_1, - mtod(m, caddr_t) + (m->m_len & ~3), m->m_len & 3); + mtod(m, caddr_t) + (m->m_len & ~3), + m->m_len & 3); MFREE(m, m0); m = m0; } } else { for (m = m0; m; ) { if (m->m_len > 1) - outsw(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), - m->m_len/2); + outsw(BASE + EP_W1_TX_PIO_WR_1, + mtod(m, caddr_t), m->m_len / 2); if (m->m_len & 1) outb(BASE + EP_W1_TX_PIO_WR_1, - *(mtod(m, caddr_t) + m->m_len - 1)); + *(mtod(m, caddr_t) + m->m_len - 1)); MFREE(m, m0); m = m0; } @@ -554,17 +559,86 @@ startagain: ++sc->sc_arpcom.ac_if.if_opackets; readcheck: - /* - * If a packet is being received, stop outputting now so we can catch - * the receive interrupt and avoid overflowing the receive FIFO. - */ - if (inw(BASE + EP_W1_RX_STATUS) & RX_BYTES_MASK) - return; + if ((inw(BASE + EP_W1_RX_STATUS) & ERR_INCOMPLETE) == 0) { + /* We received a complete packet. */ + u_short status = inw(BASE + EP_STATUS); + + if ((status & S_INTR_LATCH) == 0) { + /* + * No interrupt, read the packet and continue + * Is this supposed to happen? Is my motherboard + * completely busted? + */ + epread(sc); + } + else + /* Got an interrupt, return so that it gets serviced. */ + return; + } + else { + /* Check if we are stuck and reset [see XXX comment] */ + if (epstatus(sc)) { + if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) + printf("%s: adapter reset\n", + sc->sc_dev.dv_xname); + epreset(sc); + } + } goto startagain; } +/* + * XXX: The 3c509 card can get in a mode where both the fifo status bit + * FIFOS_RX_OVERRUN and the status bit ERR_INCOMPLETE are set + * We detect this situation and we reset the adapter. + * It happens at times when there is a lot of broadcast traffic + * on the cable (once in a blue moon). + */ +static int +epstatus(sc) + register struct ep_softc *sc; +{ + u_short fifost; + + /* + * Check the FIFO status and act accordingly + */ + GO_WINDOW(4); + fifost = inw(BASE + EP_W4_FIFO_DIAG); + GO_WINDOW(1); + + if (fifost & FIFOS_RX_UNDERRUN) { + if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) + printf("%s: RX underrun\n", sc->sc_dev.dv_xname); + epreset(sc); + return 0; + } + + if (fifost & FIFOS_RX_STATUS_OVERRUN) { + if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) + printf("%s: RX Status overrun\n", sc->sc_dev.dv_xname); + return 1; + } + + if (fifost & FIFOS_RX_OVERRUN) { + if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) + printf("%s: RX overrun\n", sc->sc_dev.dv_xname); + return 1; + } + + if (fifost & FIFOS_TX_OVERRUN) { + if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) + printf("%s: TX overrun\n", sc->sc_dev.dv_xname); + epreset(sc); + return 0; + } + + return 0; +} + + static void eptxstat(sc) register struct ep_softc *sc; @@ -611,12 +685,17 @@ epintr(sc) { struct ifnet *ifp = &sc->sc_arpcom.ac_if; u_short status; - int i, ret = 0; + int ret = 0; for (;;) { - status = inw(BASE + EP_STATUS) & - (S_TX_COMPLETE | S_TX_AVAIL | S_RX_COMPLETE | S_CARD_FAILURE); - if (status == 0) + status = inw(BASE + EP_STATUS); + + if ((status & S_INTR_LATCH) == 0) + /* Not from this card; maybe a chained interrupt? */ + return; + + if ((status & (S_TX_COMPLETE | S_TX_AVAIL | + S_RX_COMPLETE | S_CARD_FAILURE)) == 0) break; ret = 1; @@ -637,7 +716,7 @@ epintr(sc) } if (status & S_CARD_FAILURE) { printf("%s: adapter failure (%x)\n", - sc->sc_dev.dv_xname, status); + sc->sc_dev.dv_xname, status); outw(BASE + EP_COMMAND, C_INTR_LATCH); epreset(sc); return (1); @@ -659,10 +738,11 @@ epread(sc) { struct ether_header *eh; struct mbuf *mcur, *m, *m0; - int totlen, lenthisone; - int save_totlen, sh; + u_short totlen, mlen, save_totlen; + int sh; totlen = inw(BASE + EP_W1_RX_STATUS); +again: m0 = 0; if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) { @@ -688,7 +768,10 @@ epread(sc) printf("%s: %s\n", sc->sc_dev.dv_xname, s); } - if (totlen & (ERR_INCOMPLETE | ERR_RX)) { + if (totlen & ERR_INCOMPLETE) + return; + + if (totlen & ERR_RX) { ++sc->sc_arpcom.ac_if.if_ierrors; goto abort; } @@ -730,8 +813,8 @@ epread(sc) /* Read packet data. */ while (totlen > 0) { - lenthisone = min(totlen, M_TRAILINGSPACE(m)); - if (lenthisone < 4) { + mlen = min(totlen, M_TRAILINGSPACE(m)); + if (mlen < 4) { /* not enough room in this mbuf */ mcur = m; m = sc->mb[sc->next_mb]; @@ -750,29 +833,27 @@ epread(sc) MCLGET(m, M_DONTWAIT); m->m_len = 0; mcur->m_next = m; - lenthisone = min(totlen, M_TRAILINGSPACE(m)); + mlen = min(totlen, M_TRAILINGSPACE(m)); } if (sc->bus32bit) { - if (totlen > 3) { - lenthisone &= ~3; + if (mlen > 3) { + mlen &= ~3; insl(BASE + EP_W1_RX_PIO_RD_1, - mtod(m, caddr_t) + m->m_len, lenthisone/4); + mtod(m, caddr_t) + m->m_len, mlen / 4); } else insb(BASE + EP_W1_RX_PIO_RD_1, - mtod(m, caddr_t) + m->m_len, lenthisone); - m->m_len += lenthisone; - totlen -= lenthisone; + mtod(m, caddr_t) + m->m_len, mlen); } else { - if (totlen > 1) { - lenthisone &= ~1; + if (mlen > 1) { + mlen &= ~1; insw(BASE + EP_W1_RX_PIO_RD_1, - mtod(m, caddr_t) + m->m_len, lenthisone / 2); + mtod(m, caddr_t) + m->m_len, mlen / 2); } else *(mtod(m, caddr_t) + m->m_len) = inb(BASE + EP_W1_RX_PIO_RD_1); - m->m_len += lenthisone; - totlen -= lenthisone; } + m->m_len += mlen; + totlen -= mlen; } outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); @@ -807,6 +888,36 @@ epread(sc) m_adj(m0, sizeof(struct ether_header)); ether_input(&sc->sc_arpcom.ac_if, eh, m0); + + /* + * In periods of high traffic we can actually receive enough + * packets so that the fifo overrun bit will be set at this point, + * even though we just read a packet. In this case we + * are not going to receive any more interrupts. We check for + * this condition and read again until the fifo is not full. + * We could simplify this test by not using epstatus(), but + * rechecking the RX_STATUS register directly. This test could + * result in unnecessary looping in cases where there is a new + * packet but the fifo is not full, but it will not fix the + * stuck behavior. + * + * Even with this improvement, we still get packet overrun errors + * which are hurting performance. Maybe when I get some more time + * I'll modify epread() so that it can handle RX_EARLY interrupts. + */ + if (epstatus(sc)) { + totlen = inw(BASE + EP_W1_RX_STATUS); + /* Check if we are stuck and reset [see XXX comment] */ + if (totlen & ERR_INCOMPLETE) { + if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) + printf("%s: adapter reset\n", + sc->sc_dev.dv_xname); + epreset(sc); + return; + } + goto again; + } + return; abort: diff --git a/sys/arch/i386/isa/if_epreg.h b/sys/arch/i386/isa/if_epreg.h index 6e6f61509e54..3ff86fdb5778 100644 --- a/sys/arch/i386/isa/if_epreg.h +++ b/sys/arch/i386/isa/if_epreg.h @@ -1,4 +1,4 @@ -/* $NetBSD: if_epreg.h,v 1.10 1994/10/27 04:17:32 cgd Exp $ */ +/* $NetBSD: if_epreg.h,v 1.11 1994/12/10 14:52:04 christos Exp $ */ /* * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) @@ -264,6 +264,45 @@ #define TXS_MAX_COLLISION 0x08 #define TXS_STATUS_OVERFLOW 0x04 +/* + * FIFO Status (Window 4) + * + * Supports FIFO diagnostics + * + * Window 4/Port 0x04.1 + * + * 15: 1=RX receiving (RO). Set when a packet is being received + * into the RX FIFO. + * 14: Reserved + * 13: 1=RX underrun (RO). Generates Adapter Failure interrupt. + * Requires RX Reset or Global Reset command to recover. + * It is generated when you read past the end of a packet - + * reading past what has been received so far will give bad + * data. + * 12: 1=RX status overrun (RO). Set when there are already 8 + * packets in the RX FIFO. While this bit is set, no additional + * packets are received. Requires no action on the part of + * the host. The condition is cleared once a packet has been + * read out of the RX FIFO. + * 11: 1=RX overrun (RO). Set when the RX FIFO is full (there + * may not be an overrun packet yet). While this bit is set, + * no additional packets will be received (some additional + * bytes can still be pending between the wire and the RX + * FIFO). Requires no action on the part of the host. The + * condition is cleared once a few bytes have been read out + * from the RX FIFO. + * 10: 1=TX overrun (RO). Generates adapter failure interrupt. + * Requires TX Reset or Global Reset command to recover. + * Disables Transmitter. + * 9-8: Unassigned. + * 7-0: Built in self test bits for the RX and TX FIFO's. + */ +#define FIFOS_RX_RECEIVING (u_short) 0x8000 +#define FIFOS_RX_UNDERRUN (u_short) 0x2000 +#define FIFOS_RX_STATUS_OVERRUN (u_short) 0x1000 +#define FIFOS_RX_OVERRUN (u_short) 0x0800 +#define FIFOS_TX_OVERRUN (u_short) 0x0400 + /* * Misc defines for various things. */ diff --git a/sys/dev/isa/if_ep.c b/sys/dev/isa/if_ep.c index 28d9afbaf2a1..79c1218ed8c4 100644 --- a/sys/dev/isa/if_ep.c +++ b/sys/dev/isa/if_ep.c @@ -1,4 +1,4 @@ -/* $NetBSD: if_ep.c,v 1.62 1994/11/25 23:19:03 christos Exp $ */ +/* $NetBSD: if_ep.c,v 1.63 1994/12/10 14:52:02 christos Exp $ */ /* * Copyright (c) 1994 Herb Peyerl @@ -90,8 +90,9 @@ struct ep_softc { int next_mb; /* Which mbuf to use next. */ int last_mb; /* Last mbuf. */ int tx_start_thresh; /* Current TX_start_thresh. */ - int tx_succ_ok; /* # packets sent in sequence w/o underrun */ - char bus32bit; /* 32bit access possible */ + int tx_succ_ok; /* # packets sent in sequence */ + /* w/o underrun */ + char bus32bit; /* 32bit access possible */ }; static int epprobe __P((struct device *, void *, void *)); @@ -102,6 +103,8 @@ struct cfdriver epcd = { }; int epintr __P((struct ep_softc *)); +static void epxstat __P((struct ep_softc *)); +static int epstatus __P((struct ep_softc *)); static void epinit __P((struct ep_softc *)); static int epioctl __P((struct ifnet *, u_long, caddr_t)); static int epstart __P((struct ifnet *)); @@ -330,7 +333,8 @@ epattach(parent, self, aux) ether_ifattach(ifp); #if NBPFILTER > 0 - bpfattach(&sc->sc_arpcom.ac_if.if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); + bpfattach(&sc->sc_arpcom.ac_if.if_bpf, ifp, DLT_EN10MB, + sizeof(struct ether_header)); #endif sc->tx_start_thresh = 20; /* probably a good starting point. */ @@ -376,10 +380,10 @@ epinit(sc) for (i = 0; i < 31; i++) inb(BASE + EP_W1_TX_STATUS); - outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_CARD_FAILURE | S_RX_COMPLETE | - S_TX_COMPLETE | S_TX_AVAIL); - outw(BASE + EP_COMMAND, SET_INTR_MASK | S_CARD_FAILURE | S_RX_COMPLETE | - S_TX_COMPLETE | S_TX_AVAIL); + outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_CARD_FAILURE | + S_RX_COMPLETE | S_TX_COMPLETE | S_TX_AVAIL); + outw(BASE + EP_COMMAND, SET_INTR_MASK | S_CARD_FAILURE | + S_RX_COMPLETE | S_TX_COMPLETE | S_TX_AVAIL); /* * Attempt to get rid of any stray interrupts that occured during @@ -526,22 +530,23 @@ startagain: if (sc->bus32bit) { for (m = m0; m; ) { if (m->m_len > 3) - outsl(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), - m->m_len/4); + outsl(BASE + EP_W1_TX_PIO_WR_1, + mtod(m, caddr_t), m->m_len / 4); if (m->m_len & 3) outsb(BASE + EP_W1_TX_PIO_WR_1, - mtod(m, caddr_t) + (m->m_len & ~3), m->m_len & 3); + mtod(m, caddr_t) + (m->m_len & ~3), + m->m_len & 3); MFREE(m, m0); m = m0; } } else { for (m = m0; m; ) { if (m->m_len > 1) - outsw(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), - m->m_len/2); + outsw(BASE + EP_W1_TX_PIO_WR_1, + mtod(m, caddr_t), m->m_len / 2); if (m->m_len & 1) outb(BASE + EP_W1_TX_PIO_WR_1, - *(mtod(m, caddr_t) + m->m_len - 1)); + *(mtod(m, caddr_t) + m->m_len - 1)); MFREE(m, m0); m = m0; } @@ -554,17 +559,86 @@ startagain: ++sc->sc_arpcom.ac_if.if_opackets; readcheck: - /* - * If a packet is being received, stop outputting now so we can catch - * the receive interrupt and avoid overflowing the receive FIFO. - */ - if (inw(BASE + EP_W1_RX_STATUS) & RX_BYTES_MASK) - return; + if ((inw(BASE + EP_W1_RX_STATUS) & ERR_INCOMPLETE) == 0) { + /* We received a complete packet. */ + u_short status = inw(BASE + EP_STATUS); + + if ((status & S_INTR_LATCH) == 0) { + /* + * No interrupt, read the packet and continue + * Is this supposed to happen? Is my motherboard + * completely busted? + */ + epread(sc); + } + else + /* Got an interrupt, return so that it gets serviced. */ + return; + } + else { + /* Check if we are stuck and reset [see XXX comment] */ + if (epstatus(sc)) { + if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) + printf("%s: adapter reset\n", + sc->sc_dev.dv_xname); + epreset(sc); + } + } goto startagain; } +/* + * XXX: The 3c509 card can get in a mode where both the fifo status bit + * FIFOS_RX_OVERRUN and the status bit ERR_INCOMPLETE are set + * We detect this situation and we reset the adapter. + * It happens at times when there is a lot of broadcast traffic + * on the cable (once in a blue moon). + */ +static int +epstatus(sc) + register struct ep_softc *sc; +{ + u_short fifost; + + /* + * Check the FIFO status and act accordingly + */ + GO_WINDOW(4); + fifost = inw(BASE + EP_W4_FIFO_DIAG); + GO_WINDOW(1); + + if (fifost & FIFOS_RX_UNDERRUN) { + if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) + printf("%s: RX underrun\n", sc->sc_dev.dv_xname); + epreset(sc); + return 0; + } + + if (fifost & FIFOS_RX_STATUS_OVERRUN) { + if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) + printf("%s: RX Status overrun\n", sc->sc_dev.dv_xname); + return 1; + } + + if (fifost & FIFOS_RX_OVERRUN) { + if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) + printf("%s: RX overrun\n", sc->sc_dev.dv_xname); + return 1; + } + + if (fifost & FIFOS_TX_OVERRUN) { + if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) + printf("%s: TX overrun\n", sc->sc_dev.dv_xname); + epreset(sc); + return 0; + } + + return 0; +} + + static void eptxstat(sc) register struct ep_softc *sc; @@ -611,12 +685,17 @@ epintr(sc) { struct ifnet *ifp = &sc->sc_arpcom.ac_if; u_short status; - int i, ret = 0; + int ret = 0; for (;;) { - status = inw(BASE + EP_STATUS) & - (S_TX_COMPLETE | S_TX_AVAIL | S_RX_COMPLETE | S_CARD_FAILURE); - if (status == 0) + status = inw(BASE + EP_STATUS); + + if ((status & S_INTR_LATCH) == 0) + /* Not from this card; maybe a chained interrupt? */ + return; + + if ((status & (S_TX_COMPLETE | S_TX_AVAIL | + S_RX_COMPLETE | S_CARD_FAILURE)) == 0) break; ret = 1; @@ -637,7 +716,7 @@ epintr(sc) } if (status & S_CARD_FAILURE) { printf("%s: adapter failure (%x)\n", - sc->sc_dev.dv_xname, status); + sc->sc_dev.dv_xname, status); outw(BASE + EP_COMMAND, C_INTR_LATCH); epreset(sc); return (1); @@ -659,10 +738,11 @@ epread(sc) { struct ether_header *eh; struct mbuf *mcur, *m, *m0; - int totlen, lenthisone; - int save_totlen, sh; + u_short totlen, mlen, save_totlen; + int sh; totlen = inw(BASE + EP_W1_RX_STATUS); +again: m0 = 0; if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) { @@ -688,7 +768,10 @@ epread(sc) printf("%s: %s\n", sc->sc_dev.dv_xname, s); } - if (totlen & (ERR_INCOMPLETE | ERR_RX)) { + if (totlen & ERR_INCOMPLETE) + return; + + if (totlen & ERR_RX) { ++sc->sc_arpcom.ac_if.if_ierrors; goto abort; } @@ -730,8 +813,8 @@ epread(sc) /* Read packet data. */ while (totlen > 0) { - lenthisone = min(totlen, M_TRAILINGSPACE(m)); - if (lenthisone < 4) { + mlen = min(totlen, M_TRAILINGSPACE(m)); + if (mlen < 4) { /* not enough room in this mbuf */ mcur = m; m = sc->mb[sc->next_mb]; @@ -750,29 +833,27 @@ epread(sc) MCLGET(m, M_DONTWAIT); m->m_len = 0; mcur->m_next = m; - lenthisone = min(totlen, M_TRAILINGSPACE(m)); + mlen = min(totlen, M_TRAILINGSPACE(m)); } if (sc->bus32bit) { - if (totlen > 3) { - lenthisone &= ~3; + if (mlen > 3) { + mlen &= ~3; insl(BASE + EP_W1_RX_PIO_RD_1, - mtod(m, caddr_t) + m->m_len, lenthisone/4); + mtod(m, caddr_t) + m->m_len, mlen / 4); } else insb(BASE + EP_W1_RX_PIO_RD_1, - mtod(m, caddr_t) + m->m_len, lenthisone); - m->m_len += lenthisone; - totlen -= lenthisone; + mtod(m, caddr_t) + m->m_len, mlen); } else { - if (totlen > 1) { - lenthisone &= ~1; + if (mlen > 1) { + mlen &= ~1; insw(BASE + EP_W1_RX_PIO_RD_1, - mtod(m, caddr_t) + m->m_len, lenthisone / 2); + mtod(m, caddr_t) + m->m_len, mlen / 2); } else *(mtod(m, caddr_t) + m->m_len) = inb(BASE + EP_W1_RX_PIO_RD_1); - m->m_len += lenthisone; - totlen -= lenthisone; } + m->m_len += mlen; + totlen -= mlen; } outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); @@ -807,6 +888,36 @@ epread(sc) m_adj(m0, sizeof(struct ether_header)); ether_input(&sc->sc_arpcom.ac_if, eh, m0); + + /* + * In periods of high traffic we can actually receive enough + * packets so that the fifo overrun bit will be set at this point, + * even though we just read a packet. In this case we + * are not going to receive any more interrupts. We check for + * this condition and read again until the fifo is not full. + * We could simplify this test by not using epstatus(), but + * rechecking the RX_STATUS register directly. This test could + * result in unnecessary looping in cases where there is a new + * packet but the fifo is not full, but it will not fix the + * stuck behavior. + * + * Even with this improvement, we still get packet overrun errors + * which are hurting performance. Maybe when I get some more time + * I'll modify epread() so that it can handle RX_EARLY interrupts. + */ + if (epstatus(sc)) { + totlen = inw(BASE + EP_W1_RX_STATUS); + /* Check if we are stuck and reset [see XXX comment] */ + if (totlen & ERR_INCOMPLETE) { + if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) + printf("%s: adapter reset\n", + sc->sc_dev.dv_xname); + epreset(sc); + return; + } + goto again; + } + return; abort: diff --git a/sys/dev/isa/if_epreg.h b/sys/dev/isa/if_epreg.h index 6e6f61509e54..3ff86fdb5778 100644 --- a/sys/dev/isa/if_epreg.h +++ b/sys/dev/isa/if_epreg.h @@ -1,4 +1,4 @@ -/* $NetBSD: if_epreg.h,v 1.10 1994/10/27 04:17:32 cgd Exp $ */ +/* $NetBSD: if_epreg.h,v 1.11 1994/12/10 14:52:04 christos Exp $ */ /* * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) @@ -264,6 +264,45 @@ #define TXS_MAX_COLLISION 0x08 #define TXS_STATUS_OVERFLOW 0x04 +/* + * FIFO Status (Window 4) + * + * Supports FIFO diagnostics + * + * Window 4/Port 0x04.1 + * + * 15: 1=RX receiving (RO). Set when a packet is being received + * into the RX FIFO. + * 14: Reserved + * 13: 1=RX underrun (RO). Generates Adapter Failure interrupt. + * Requires RX Reset or Global Reset command to recover. + * It is generated when you read past the end of a packet - + * reading past what has been received so far will give bad + * data. + * 12: 1=RX status overrun (RO). Set when there are already 8 + * packets in the RX FIFO. While this bit is set, no additional + * packets are received. Requires no action on the part of + * the host. The condition is cleared once a packet has been + * read out of the RX FIFO. + * 11: 1=RX overrun (RO). Set when the RX FIFO is full (there + * may not be an overrun packet yet). While this bit is set, + * no additional packets will be received (some additional + * bytes can still be pending between the wire and the RX + * FIFO). Requires no action on the part of the host. The + * condition is cleared once a few bytes have been read out + * from the RX FIFO. + * 10: 1=TX overrun (RO). Generates adapter failure interrupt. + * Requires TX Reset or Global Reset command to recover. + * Disables Transmitter. + * 9-8: Unassigned. + * 7-0: Built in self test bits for the RX and TX FIFO's. + */ +#define FIFOS_RX_RECEIVING (u_short) 0x8000 +#define FIFOS_RX_UNDERRUN (u_short) 0x2000 +#define FIFOS_RX_STATUS_OVERRUN (u_short) 0x1000 +#define FIFOS_RX_OVERRUN (u_short) 0x0800 +#define FIFOS_TX_OVERRUN (u_short) 0x0400 + /* * Misc defines for various things. */