summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchuck <chuck@openbsd.org>1997-09-29 17:45:58 +0000
committerchuck <chuck@openbsd.org>1997-09-29 17:45:58 +0000
commit3047cab651999f263283320da0cd37db43c99984 (patch)
tree50959cb9849aa204bebb81445f9f654aaa73f7db
parentAdd entry for lastlog. (diff)
downloadwireguard-openbsd-3047cab651999f263283320da0cd37db43c99984.tar.xz
wireguard-openbsd-3047cab651999f263283320da0cd37db43c99984.zip
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 also, pull in the following changes from netbsd: revision 1.29 date: 1997/07/07 22:52:17; author: cgd; state: Exp; lines: +29 -22 fix static inline functions' prototypes so they're correctly marked 'inline' as well as static. mark prototypes for static inline functions as possibly unused (with __attribute__ ((unused))), to avoid generating warnings when compiling without optimization but with most ports' default warning flags. Clean up prototype list spacing, and make it more consistent. --------------------------- revision 1.28 date: 1997/04/28 17:04:06; author: mycroft; state: Exp; lines: +2 -1 Oops; forgot to GC the last mbuf allocated when out of clusters. --------------------------- revision 1.27 date: 1997/04/24 02:25:37; author: mycroft; state: Exp; lines: +2 -2 Do not force the initial part of a packet into a separate mbuf. --------------------------- revision 1.26 date: 1997/04/24 02:24:07; author: mycroft; state: Exp; lines: +6 -3 If we fail to allocate a cluster to hold a large packet, simply drop it rather than using a chain of tiny mbufs.
-rw-r--r--sys/dev/ic/midway.c141
1 files changed, 101 insertions, 40 deletions
diff --git a/sys/dev/ic/midway.c b/sys/dev/ic/midway.c
index c3f5c8b56ee..0eb366c7804 100644
--- a/sys/dev/ic/midway.c
+++ b/sys/dev/ic/midway.c
@@ -1,5 +1,5 @@
-/* $OpenBSD: midway.c,v 1.20 1997/03/20 22:03:03 chuck Exp $ */
-/* (sync'd to midway.c 1.67) */
+/* $OpenBSD: midway.c,v 1.21 1997/09/29 17:45:58 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 */
/*
@@ -240,29 +271,36 @@ static struct en_dmatab *en_dmaplan = en_dma_planA;
* prototypes
*/
-STATIC int en_b2sz __P((int));
+STATIC INLINE int en_b2sz __P((int)) __attribute__ ((unused));
#ifdef EN_DDBHOOK
-int en_dump __P((int,int));
-int en_dumpmem __P((int,int,int));
-#endif
-STATIC void en_dmaprobe __P((struct en_softc *));
-STATIC int en_dmaprobe_doit __P((struct en_softc *, u_int8_t *,
- u_int8_t *, int));
-STATIC int en_dqneed __P((struct en_softc *, caddr_t, u_int, u_int));
-STATIC void en_init __P((struct en_softc *));
-STATIC int en_ioctl __P((struct ifnet *, EN_IOCTL_CMDT, caddr_t));
-STATIC int en_k2sz __P((int));
-STATIC void en_loadvc __P((struct en_softc *, int));
-STATIC int en_mfix __P((struct en_softc *, struct mbuf **, struct mbuf *));
-STATIC struct mbuf *en_mget __P((struct en_softc *, u_int, u_int *));
-STATIC u_int32_t en_read __P((struct en_softc *, u_int32_t));
-STATIC int en_rxctl __P((struct en_softc *, struct atm_pseudoioctl *, int));
-STATIC void en_txdma __P((struct en_softc *, int));
-STATIC void en_txlaunch __P((struct en_softc *, int, struct en_launch *));
-STATIC void en_service __P((struct en_softc *));
-STATIC void en_start __P((struct ifnet *));
-STATIC int en_sz2b __P((int));
-STATIC void en_write __P((struct en_softc *, u_int32_t, u_int32_t));
+ int en_dump __P((int,int));
+ int en_dumpmem __P((int,int,int));
+#endif
+STATIC void en_dmaprobe __P((struct en_softc *));
+STATIC int en_dmaprobe_doit __P((struct en_softc *, u_int8_t *,
+ u_int8_t *, int));
+STATIC INLINE int en_dqneed __P((struct en_softc *, caddr_t, u_int,
+ u_int)) __attribute__ ((unused));
+STATIC void en_init __P((struct en_softc *));
+STATIC int en_ioctl __P((struct ifnet *, EN_IOCTL_CMDT, caddr_t));
+STATIC INLINE int en_k2sz __P((int)) __attribute__ ((unused));
+STATIC void en_loadvc __P((struct en_softc *, int));
+STATIC int en_mfix __P((struct en_softc *, struct mbuf **,
+ struct mbuf *));
+STATIC INLINE struct mbuf *en_mget __P((struct en_softc *, u_int,
+ u_int *)) __attribute__ ((unused));
+STATIC INLINE u_int32_t en_read __P((struct en_softc *,
+ u_int32_t)) __attribute__ ((unused));
+STATIC int en_rxctl __P((struct en_softc *, struct atm_pseudoioctl *,
+ int));
+STATIC void en_txdma __P((struct en_softc *, int));
+STATIC void en_txlaunch __P((struct en_softc *, int,
+ struct en_launch *));
+STATIC void en_service __P((struct en_softc *));
+STATIC void en_start __P((struct ifnet *));
+STATIC INLINE int en_sz2b __P((int)) __attribute__ ((unused));
+STATIC INLINE void en_write __P((struct en_softc *, u_int32_t,
+ u_int32_t)) __attribute__ ((unused));
/*
* macros/inline
@@ -591,10 +629,14 @@ u_int totlen, *drqneed;
}
m->m_len = MLEN;
}
- if (top && totlen >= MINCLSIZE) {
+ if (totlen >= MINCLSIZE) {
MCLGET(m, M_DONTWAIT);
- if (m->m_flags & M_EXT)
- m->m_len = MCLBYTES;
+ if ((m->m_flags & M_EXT) == 0) {
+ m_free(m);
+ m_freem(top);
+ return(NULL); /* out of mbuf clusters */
+ }
+ m->m_len = MCLBYTES;
}
m->m_len = min(totlen, m->m_len);
totlen -= m->m_len;
@@ -744,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;
@@ -1325,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 */
@@ -1463,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);
@@ -2325,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 */
@@ -2341,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 */
}
@@ -2373,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 */
@@ -2420,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 */
@@ -2439,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);
@@ -2472,7 +2533,7 @@ void *arg;
printf("%s: added VCI %d to swslist\n", sc->sc_dev.dv_xname, vci);
#endif
}
- } while (sc->hwslistp != chip);
+ }
}
/*
@@ -2622,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);