midway update:

[1] add EN_ENIDMAFIX - the byte-aligner on the ENI version of the card
	appears to lose under heavy load so avoid using it.  see comment
	in midway.c for full details.

	note that the Adaptec version of the card works properly under
	load.

	detected by Kenjiro Cho <kjc@csl.sony.co.jp> [confirmed by chuck]
	fix by chuck & kjc

[2] update some freebsd specific code [from kjc]

[3] for circular buffers: ensure there is always one free slot so
	that we can easily tell the difference between a full and
	empty list.   re-structure a few loops to reflect this.
	fixes a problem with mbufs being free'd while still in DMA,
	and makes drive a bit more robust.

	detected and fixed by kjc
This commit is contained in:
chuck 1997-09-29 17:40:38 +00:00
parent 9f482818bc
commit d3a7613827
1 changed files with 65 additions and 15 deletions

View File

@ -1,5 +1,5 @@
/* $NetBSD: midway.c,v 1.29 1997/07/07 22:52:17 cgd Exp $ */
/* (sync'd to midway.c 1.67) */
/* $NetBSD: midway.c,v 1.30 1997/09/29 17:40:38 chuck Exp $ */
/* (sync'd to midway.c 1.68) */
/*
*
@ -61,6 +61,33 @@
#define EN_NOWMAYBE 1 /* hook to disable word maybe DMA */
/* XXX: WMAYBE doesn't work, needs debugging */
#define EN_DDBHOOK 1 /* compile in ddb functions */
#if defined(MIDWAY_ADPONLY)
#define EN_ENIDMAFIX 0 /* no ENI cards to worry about */
#else
#define EN_ENIDMAFIX 1 /* avoid byte DMA on the ENI card (see below) */
#endif
/*
* note on EN_ENIDMAFIX: the byte aligner on the ENI version of the card
* appears to be broken. it works just fine if there is no load... however
* when the card is loaded the data get corrupted. to see this, one only
* has to use "telnet" over ATM. do the following command in "telnet":
* cat /usr/share/misc/termcap
* "telnet" seems to generate lots of 1023 byte mbufs (which make great
* use of the byte aligner). watch "netstat -s" for checksum errors.
*
* I further tested this by adding a function that compared the transmit
* data on the card's SRAM with the data in the mbuf chain _after_ the
* "transmit DMA complete" interrupt. using the "telnet" test I got data
* mismatches where the byte-aligned data should have been. using ddb
* and en_dumpmem() I verified that the DTQs fed into the card were
* absolutely correct. thus, we are forced to concluded that the ENI
* hardware is buggy. note that the Adaptec version of the card works
* just fine with byte DMA.
*
* bottom line: we set EN_ENIDMAFIX to 1 to avoid byte DMAs on the ENI
* card.
*/
#if defined(DIAGNOSTIC) && !defined(EN_DIAG)
#define EN_DIAG /* link in with master DIAG option */
@ -93,7 +120,11 @@
#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__)
#include <sys/device.h>
#endif
#if defined(__FreeBSD__)
#include <sys/sockio.h>
#else
#include <sys/ioctl.h>
#endif
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
@ -128,8 +159,8 @@
#elif defined(__FreeBSD__)
#include <machine/cpufunc.h> /* for rdtsc proto for clock.h below */
#include <machine/clock.h> /* for DELAY */
#include <pci/midwayreg.h>
#include <pci/midwayvar.h>
#include <dev/en/midwayreg.h>
#include <dev/en/midwayvar.h>
#include <vm/pmap.h> /* for vtophys proto */
/*
@ -755,6 +786,14 @@ done_probe:
printf("%s: EN_NTX/EN_TXSZ/EN_RXSZ too big\n", sc->sc_dev.dv_xname);
return;
}
/*
* ensure that there is always one VC slot on the service list free
* so that we can tell the difference between a full and empty list.
*/
if (sc->en_nrx >= MID_N_VC)
sc->en_nrx = MID_N_VC - 1;
for (lcv = 0 ; lcv < sc->en_nrx ; lcv++) {
sc->rxslot[lcv].rxhand = NULL;
sc->rxslot[lcv].oth_flags = ENOTHER_FREE;
@ -1336,20 +1375,30 @@ struct en_softc *sc;
/*
* init obmem data structures: vc tab, dma q's, slist.
*
* note that we set drq_free/dtq_free to one less than the total number
* of DTQ/DRQs present. we do this because the card uses the condition
* (drq_chip == drq_us) to mean "list is empty"... but if you allow the
* circular list to be completely full then (drq_chip == drq_us) [i.e.
* the drq_us pointer will wrap all the way around]. by restricting
* the number of active requests to (N - 1) we prevent the list from
* becoming completely full. note that the card will sometimes give
* us an interrupt for a DTQ/DRQ we have already processes... this helps
* keep that interrupt from messing us up.
*/
for (vc = 0 ; vc < MID_N_VC ; vc++)
en_loadvc(sc, vc);
bzero(&sc->drq, sizeof(sc->drq));
sc->drq_free = MID_DRQ_N;
sc->drq_free = MID_DRQ_N - 1; /* N - 1 */
sc->drq_chip = MID_DRQ_REG2A(EN_READ(sc, MID_DMA_RDRX));
EN_WRITE(sc, MID_DMA_WRRX, MID_DRQ_A2REG(sc->drq_chip));
/* ensure zero queue */
sc->drq_us = sc->drq_chip;
bzero(&sc->dtq, sizeof(sc->dtq));
sc->dtq_free = MID_DTQ_N;
sc->dtq_free = MID_DTQ_N - 1; /* N - 1 */
sc->dtq_chip = MID_DTQ_REG2A(EN_READ(sc, MID_DMA_RDTX));
EN_WRITE(sc, MID_DMA_WRTX, MID_DRQ_A2REG(sc->dtq_chip));
/* ensure zero queue */
@ -1474,7 +1523,8 @@ struct ifnet *ifp;
mlen = 0;
prev = NULL;
while (1) {
if (EN_NOTXDMA || !en_dma) { /* no DMA? */
/* no DMA? */
if ((!sc->is_adaptec && EN_ENIDMAFIX) || EN_NOTXDMA || !en_dma) {
if ( (mtod(lastm, unsigned long) % sizeof(u_int32_t)) != 0 ||
((lastm->m_len % sizeof(u_int32_t)) != 0 && lastm->m_next)) {
first = (lastm == m);
@ -2336,7 +2386,7 @@ void *arg;
printf("%s: cleared need DTQ condition\n", sc->sc_dev.dv_xname);
#endif
}
do { /* while idx != val */
while (idx != val) {
sc->dtq_free++;
if ((dtq = sc->dtq[idx]) != 0) {
sc->dtq[idx] = 0; /* don't forget to zero it out when done */
@ -2352,7 +2402,7 @@ void *arg;
m_freem(m);
}
EN_WRAPADD(0, MID_DTQ_N, idx, 1);
} while (idx != val);
}
sc->dtq_chip = MID_DTQ_REG2A(val); /* sync softc */
}
@ -2384,7 +2434,7 @@ void *arg;
if (reg & MID_INT_DMA_RX) {
val = EN_READ(sc, MID_DMA_RDRX); /* chip's current location */
idx = MID_DRQ_A2REG(sc->drq_chip);/* where we last saw chip */
do { /* while (idx != val) */
while (idx != val) {
sc->drq_free++;
if ((drq = sc->drq[idx]) != 0) {
sc->drq[idx] = 0; /* don't forget to zero it out when done */
@ -2431,7 +2481,7 @@ void *arg;
}
EN_WRAPADD(0, MID_DRQ_N, idx, 1);
} while (idx != val);
}
sc->drq_chip = MID_DRQ_REG2A(val); /* sync softc */
if (sc->need_drqs) { /* true if we had a DRQ shortage */
@ -2450,7 +2500,7 @@ void *arg;
if (reg & MID_INT_SERVICE) {
chip = MID_SL_REG2A(EN_READ(sc, MID_SERV_WRITE));
do { /* while sc->hwslistp != chip */
while (sc->hwslistp != chip) {
/* fetch and remove it from hardware service list */
vci = EN_READ(sc, sc->hwslistp);
@ -2483,7 +2533,7 @@ void *arg;
printf("%s: added VCI %d to swslist\n", sc->sc_dev.dv_xname, vci);
#endif
}
} while (sc->hwslistp != chip);
}
}
/*
@ -2633,8 +2683,8 @@ defer: /* defer processing */
pdu = EN_READ(sc, pdu); /* get PDU in correct byte order */
fill = tlen - MID_RBD_SIZE - MID_PDU_LEN(pdu);
if (fill < 0 || (rbd & MID_RBD_CRCERR) != 0) {
printf("%s: invalid AAL5 PDU length or CRC detected, dropping frame\n",
sc->sc_dev.dv_xname);
printf("%s: %s, dropping frame\n", sc->sc_dev.dv_xname,
(rbd & MID_RBD_CRCERR) ? "CRC error" : "invalid AAL5 PDU length");
printf("%s: got %d cells (%d bytes), AAL5 len is %d bytes (pdu=0x%x)\n",
sc->sc_dev.dv_xname, MID_RBD_CNT(rbd), tlen - MID_RBD_SIZE,
MID_PDU_LEN(pdu), pdu);