From c228c1c4a2ccc21b5f4869a16515c43449faf6c2 Mon Sep 17 00:00:00 2001 From: Can Erkin Acar Date: Mon, 9 Aug 2004 16:33:56 +0000 Subject: update ste(4) with stability and performance fixes from FreeBSD if_ste.c revisions 1.28, 1.29, 1.38, 1.43, 1.63, 1.68, 1.70 ok henning@ --- sys/dev/pci/if_ste.c | 254 +++++++++++++++++++++++++++++------------------- sys/dev/pci/if_stereg.h | 30 +++--- 2 files changed, 174 insertions(+), 110 deletions(-) (limited to 'sys') diff --git a/sys/dev/pci/if_ste.c b/sys/dev/pci/if_ste.c index 4b866fe8b84..af7e714fe22 100644 --- a/sys/dev/pci/if_ste.c +++ b/sys/dev/pci/if_ste.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_ste.c,v 1.22 2004/06/06 17:56:36 mcbride Exp $ */ +/* $OpenBSD: if_ste.c,v 1.23 2004/08/09 16:33:55 canacar Exp $ */ /* * Copyright (c) 1997, 1998, 1999 * Bill Paul . All rights reserved. @@ -234,9 +234,9 @@ int ste_mii_readreg(sc, frame) /* Check for ack */ MII_CLR(STE_PHYCTL_MCLK); DELAY(1); + ack = CSR_READ_2(sc, STE_PHYCTL) & STE_PHYCTL_MDATA; MII_SET(STE_PHYCTL_MCLK); DELAY(1); - ack = CSR_READ_2(sc, STE_PHYCTL) & STE_PHYCTL_MDATA; /* * Now try reading data bits. If the ack failed, we still @@ -366,14 +366,29 @@ void ste_miibus_statchg(self) { struct ste_softc *sc = (struct ste_softc *)self; struct mii_data *mii; + int fdx, fcur; mii = &sc->sc_mii; - if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { + fcur = CSR_READ_2(sc, STE_MACCTL0) & STE_MACCTL0_FULLDUPLEX; + fdx = (mii->mii_media_active & IFM_GMASK) == IFM_FDX; + + if ((fcur && fdx) || (! fcur && ! fdx)) + return; + + STE_SETBIT4(sc, STE_DMACTL, + STE_DMACTL_RXDMA_STALL |STE_DMACTL_TXDMA_STALL); + ste_wait(sc); + + if (fdx) STE_SETBIT2(sc, STE_MACCTL0, STE_MACCTL0_FULLDUPLEX); - } else { + else STE_CLRBIT2(sc, STE_MACCTL0, STE_MACCTL0_FULLDUPLEX); - } + + printf("%s: %s-duplex\n", sc->sc_dev.dv_xname, fdx ? "full":"half"); + + STE_SETBIT4(sc, STE_DMACTL, + STE_DMACTL_RXDMA_UNSTALL | STE_DMACTL_TXDMA_UNSTALL); return; } @@ -389,8 +404,7 @@ int ste_ifmedia_upd(ifp) sc->ste_link = 0; if (mii->mii_instance) { struct mii_softc *miisc; - for (miisc = LIST_FIRST(&mii->mii_phys); miisc != NULL; - miisc = LIST_NEXT(miisc, mii_list)) + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) mii_phy_reset(miisc); } mii_mediachg(mii); @@ -509,8 +523,10 @@ allmulti: } /* first, zot all the existing hash bits */ - CSR_WRITE_4(sc, STE_MAR0, 0); - CSR_WRITE_4(sc, STE_MAR1, 0); + CSR_WRITE_2(sc, STE_MAR0, 0); + CSR_WRITE_2(sc, STE_MAR1, 0); + CSR_WRITE_2(sc, STE_MAR2, 0); + CSR_WRITE_2(sc, STE_MAR3, 0); /* now program new ones */ ETHER_FIRST_MULTI(step, ac, enm); @@ -528,8 +544,10 @@ allmulti: ETHER_NEXT_MULTI(step, enm); } - CSR_WRITE_4(sc, STE_MAR0, hashes[0]); - CSR_WRITE_4(sc, STE_MAR1, hashes[1]); + CSR_WRITE_2(sc, STE_MAR0, hashes[0] & 0xFFFF); + CSR_WRITE_2(sc, STE_MAR1, (hashes[0] >> 16) & 0xFFFF); + CSR_WRITE_2(sc, STE_MAR2, hashes[1] & 0xFFFF); + CSR_WRITE_2(sc, STE_MAR3, (hashes[1] >> 16) & 0xFFFF); STE_CLRBIT1(sc, STE_RX_MODE, STE_RXMODE_ALLMULTI); STE_SETBIT1(sc, STE_RX_MODE, STE_RXMODE_MULTIHASH); @@ -573,6 +591,10 @@ int ste_intr(xsc) ste_stats_update(sc); } + if (status & STE_ISR_LINKEVENT) + mii_pollstat(&sc->sc_mii); + + if (status & STE_ISR_HOSTERR) { ste_reset(sc); ste_init(sc); @@ -598,14 +620,30 @@ void ste_rxeof(sc) struct mbuf *m; struct ifnet *ifp; struct ste_chain_onefrag *cur_rx; - int total_len = 0; + int total_len = 0, count=0; u_int32_t rxstat; ifp = &sc->arpcom.ac_if; -again: + if (sc->ste_cdata.ste_rx_head->ste_ptr->ste_status == 0) { + cur_rx = sc->ste_cdata.ste_rx_head; + do { + cur_rx = cur_rx->ste_next; + /* If the ring is empty, just return. */ + if (cur_rx == sc->ste_cdata.ste_rx_head) + return; + } while (cur_rx->ste_ptr->ste_status == 0); + + if (sc->ste_cdata.ste_rx_head->ste_ptr->ste_status == 0) + /* We've fallen behind the chip: catch it. */ + sc->ste_cdata.ste_rx_head = cur_rx; + } + + while((rxstat = sc->ste_cdata.ste_rx_head->ste_ptr->ste_status) + & STE_RXSTAT_DMADONE) { + if ((STE_RX_LIST_CNT - count) < 3) + break; - while((rxstat = sc->ste_cdata.ste_rx_head->ste_ptr->ste_status)) { cur_rx = sc->ste_cdata.ste_rx_head; sc->ste_cdata.ste_rx_head = cur_rx->ste_next; @@ -662,29 +700,9 @@ again: /* pass it on. */ ether_input_mbuf(ifp, m); - } - /* - * Handle the 'end of channel' condition. When the upload - * engine hits the end of the RX ring, it will stall. This - * is our cue to flush the RX ring, reload the uplist pointer - * register and unstall the engine. - * XXX This is actually a little goofy. With the ThunderLAN - * chip, you get an interrupt when the receiver hits the end - * of the receive ring, which tells you exactly when you - * you need to reload the ring pointer. Here we have to - * fake it. I'm mad at myself for not being clever enough - * to avoid the use of a goto here. - */ - if (CSR_READ_4(sc, STE_RX_DMALIST_PTR) == 0 || - CSR_READ_4(sc, STE_DMACTL) & STE_DMACTL_RXDMA_STOPPED) { - STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_STALL); - ste_wait(sc); - CSR_WRITE_4(sc, STE_RX_DMALIST_PTR, - vtophys(&sc->ste_ldata->ste_rx_list[0])); - sc->ste_cdata.ste_rx_head = &sc->ste_cdata.ste_rx_chain[0]; - STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_UNSTALL); - goto again; + cur_rx->ste_ptr->ste_status = 0; + count++; } return; @@ -747,19 +765,17 @@ void ste_txeof(sc) if (cur_tx->ste_mbuf != NULL) { m_freem(cur_tx->ste_mbuf); cur_tx->ste_mbuf = NULL; + ifp->if_flags &= ~IFF_OACTIVE; } ifp->if_opackets++; - sc->ste_cdata.ste_tx_cnt--; STE_INC(idx, STE_TX_LIST_CNT); - ifp->if_timer = 0; } sc->ste_cdata.ste_tx_cons = idx; - - if (cur_tx != NULL) - ifp->if_flags &= ~IFF_OACTIVE; + if (idx == sc->ste_cdata.ste_tx_prod) + ifp->if_timer = 0; return; } @@ -768,11 +784,9 @@ void ste_stats_update(xsc) void *xsc; { struct ste_softc *sc; - struct ste_stats stats; struct ifnet *ifp; struct mii_data *mii; - int i, s; - u_int8_t *p; + int s; s = splimp(); @@ -780,22 +794,14 @@ void ste_stats_update(xsc) ifp = &sc->arpcom.ac_if; mii = &sc->sc_mii; - p = (u_int8_t *)&stats; - - for (i = 0; i < sizeof(stats); i++) { - *p = CSR_READ_1(sc, STE_STATS + i); - p++; - } - - ifp->if_collisions += stats.ste_single_colls + - stats.ste_multi_colls + stats.ste_late_colls; + ifp->if_collisions += CSR_READ_1(sc, STE_LATE_COLLS) + + CSR_READ_1(sc, STE_MULTI_COLLS) + + CSR_READ_1(sc, STE_SINGLE_COLLS); mii_tick(mii); - if (!sc->ste_link) { - mii_pollstat(mii); - if (mii->mii_media_status & IFM_ACTIVE && - IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) - sc->ste_link++; + if (!sc->ste_link && mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + sc->ste_link++; if (IFQ_IS_EMPTY(&ifp->if_snd) == 0) ste_start(ifp); } @@ -939,7 +945,7 @@ void ste_attach(parent, self, aux) printf(" address %s\n", ether_sprintf(sc->arpcom.ac_enaddr)); sc->ste_ldata_ptr = malloc(sizeof(struct ste_list_data) + 8, - M_DEVBUF, M_NOWAIT); + M_DEVBUF, M_DONTWAIT); if (sc->ste_ldata_ptr == NULL) { printf("%s: no memory for list buffers!\n", sc->sc_dev.dv_xname); goto fail; @@ -961,6 +967,8 @@ void ste_attach(parent, self, aux) IFQ_SET_READY(&ifp->if_snd); bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); + sc->ste_tx_thresh = STE_TXSTART_THRESH; + sc->sc_mii.mii_ifp = ifp; sc->sc_mii.mii_readreg = ste_miibus_readreg; sc->sc_mii.mii_writereg = ste_miibus_writereg; @@ -1045,7 +1053,7 @@ int ste_init_rx_list(sc) ld->ste_rx_list[i].ste_next = vtophys(&ld->ste_rx_list[i + 1]); } - + ld->ste_rx_list[i].ste_status = 0; } cd->ste_rx_head = &cd->ste_rx_chain[0]; @@ -1071,12 +1079,6 @@ void ste_init_tx_list(sc) else cd->ste_tx_chain[i].ste_next = &cd->ste_tx_chain[i + 1]; - if (i == 0) - cd->ste_tx_chain[i].ste_prev = - &cd->ste_tx_chain[STE_TX_LIST_CNT - 1]; - else - cd->ste_tx_chain[i].ste_prev = - &cd->ste_tx_chain[i - 1]; } bzero((char *)ld->ste_tx_list, @@ -1084,7 +1086,6 @@ void ste_init_tx_list(sc) cd->ste_tx_prod = 0; cd->ste_tx_cons = 0; - cd->ste_tx_cnt = 0; return; } @@ -1117,6 +1118,9 @@ void ste_init(xsc) return; } + /* Set RX polling interval */ + CSR_WRITE_1(sc, STE_RX_DMAPOLL_PERIOD, 64); + /* Init TX descriptors */ ste_init_tx_list(sc); @@ -1156,20 +1160,21 @@ void ste_init(xsc) STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_UNSTALL); STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_UNSTALL); - /* Set TX polling interval */ - CSR_WRITE_1(sc, STE_TX_DMAPOLL_PERIOD, 64); + /* Set TX polling interval (defer until we TX first packet) */ + CSR_WRITE_1(sc, STE_TX_DMAPOLL_PERIOD, 0); /* Load address of the TX list */ STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_STALL); ste_wait(sc); - CSR_WRITE_4(sc, STE_TX_DMALIST_PTR, - vtophys(&sc->ste_ldata->ste_tx_list[0])); + CSR_WRITE_4(sc, STE_TX_DMALIST_PTR, 0); STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL); STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL); ste_wait(sc); + sc->ste_tx_prev=NULL; /* Enable receiver and transmitter */ CSR_WRITE_2(sc, STE_MACCTL0, 0); + CSR_WRITE_2(sc, STE_MACCTL1, 0); STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_TX_ENABLE); STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_RX_ENABLE); @@ -1210,6 +1215,11 @@ void ste_stop(sc) STE_SETBIT2(sc, STE_DMACTL, STE_DMACTL_TXDMA_STALL); STE_SETBIT2(sc, STE_DMACTL, STE_DMACTL_RXDMA_STALL); ste_wait(sc); + /* + * Try really hard to stop the RX engine or under heavy RX + * data chip will write into de-allocated memory. + */ + ste_reset(sc); sc->ste_link = 0; @@ -1227,6 +1237,8 @@ void ste_stop(sc) } } + bzero(sc->ste_ldata, sizeof(struct ste_list_data)); + ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); return; @@ -1300,8 +1312,12 @@ int ste_ioctl(ifp, command, data) sc->ste_if_flags & IFF_PROMISC) { STE_CLRBIT1(sc, STE_RX_MODE, STE_RXMODE_PROMISC); - } else if (!(ifp->if_flags & IFF_RUNNING)) { - sc->ste_tx_thresh = STE_MIN_FRAMELEN; + } + if (ifp->if_flags & IFF_RUNNING && + (ifp->if_flags ^ sc->ste_if_flags) & IFF_ALLMULTI) + ste_setmulti(sc); + if (!(ifp->if_flags & IFF_RUNNING)) { + sc->ste_tx_thresh = STE_TXSTART_THRESH; ste_init(sc); } } else { @@ -1350,27 +1366,54 @@ int ste_encap(sc, c, m_head) struct ste_frag *f = NULL; struct mbuf *m; struct ste_desc *d; - int total_len = 0; d = c->ste_ptr; d->ste_ctl = 0; - d->ste_next = 0; +encap_retry: for (m = m_head, frag = 0; m != NULL; m = m->m_next) { if (m->m_len != 0) { if (frag == STE_MAXFRAGS) break; - total_len += m->m_len; - f = &c->ste_ptr->ste_frags[frag]; + f = &d->ste_frags[frag]; f->ste_addr = vtophys(mtod(m, vaddr_t)); f->ste_len = m->m_len; frag++; } } + if (m != NULL) { + struct mbuf *mn; + + /* + * We ran out of segments. We have to recopy this + * mbuf chain first. Bail out if we can't get the + * new buffers. + */ + MGETHDR(mn, M_DONTWAIT, MT_DATA); + if (mn == NULL) { + m_freem(m_head); + return ENOMEM; + } + if (m_head->m_pkthdr.len > MHLEN) { + MCLGET(mn, M_DONTWAIT); + if ((mn->m_flags & M_EXT) == 0) { + m_freem(mn); + m_freem(m_head); + return ENOMEM; + } + } + m_copydata(m_head, 0, m_head->m_pkthdr.len, + mtod(mn, caddr_t)); + mn->m_pkthdr.len = mn->m_len = m_head->m_pkthdr.len; + m_freem(m_head); + m_head = mn; + goto encap_retry; + } + c->ste_mbuf = m_head; - c->ste_ptr->ste_frags[frag - 1].ste_len |= STE_FRAG_LAST; - c->ste_ptr->ste_ctl = total_len; + d->ste_frags[frag - 1].ste_len |= STE_FRAG_LAST; + d->ste_ctl = 1; return(0); } @@ -1380,7 +1423,7 @@ void ste_start(ifp) { struct ste_softc *sc; struct mbuf *m_head = NULL; - struct ste_chain *prev = NULL, *cur_tx = NULL, *start_tx; + struct ste_chain *cur_tx = NULL; int idx; sc = ifp->if_softc; @@ -1392,10 +1435,14 @@ void ste_start(ifp) return; idx = sc->ste_cdata.ste_tx_prod; - start_tx = &sc->ste_cdata.ste_tx_chain[idx]; while(sc->ste_cdata.ste_tx_chain[idx].ste_mbuf == NULL) { - if ((STE_TX_LIST_CNT - sc->ste_cdata.ste_tx_cnt) < 3) { + /* + * We cannot re-use the last (free) descriptor; + * the chip may not have read its ste_next yet. + */ + if (STE_NEXT(idx, STE_TX_LIST_CNT) == + sc->ste_cdata.ste_tx_cons) { ifp->if_flags |= IFF_OACTIVE; break; } @@ -1406,11 +1453,32 @@ void ste_start(ifp) cur_tx = &sc->ste_cdata.ste_tx_chain[idx]; - ste_encap(sc, cur_tx, m_head); + if (ste_encap(sc, cur_tx, m_head) != 0) + break; + + cur_tx->ste_ptr->ste_next = 0; + + if (sc->ste_tx_prev == NULL) { + cur_tx->ste_ptr->ste_ctl = STE_TXCTL_DMAINTR | 1; + /* Load address of the TX list */ + STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_STALL); + ste_wait(sc); + + CSR_WRITE_4(sc, STE_TX_DMALIST_PTR, + vtophys(&sc->ste_ldata->ste_tx_list[0])); + + /* Set TX polling interval to start TX engine */ + CSR_WRITE_1(sc, STE_TX_DMAPOLL_PERIOD, 64); + + STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL); + ste_wait(sc); + }else{ + cur_tx->ste_ptr->ste_ctl = STE_TXCTL_DMAINTR | 1; + sc->ste_tx_prev->ste_ptr->ste_next + = cur_tx->ste_phys; + } - if (prev != NULL) - prev->ste_ptr->ste_next = cur_tx->ste_phys; - prev = cur_tx; + sc->ste_tx_prev = cur_tx; #if NBPFILTER > 0 /* @@ -1422,20 +1490,10 @@ void ste_start(ifp) #endif STE_INC(idx, STE_TX_LIST_CNT); - sc->ste_cdata.ste_tx_cnt++; + ifp->if_timer = 5; + sc->ste_cdata.ste_tx_prod = idx; } - if (cur_tx == NULL) - return; - - cur_tx->ste_ptr->ste_ctl |= STE_TXCTL_DMAINTR; - - /* Start transmission */ - sc->ste_cdata.ste_tx_prod = idx; - start_tx->ste_prev->ste_ptr->ste_next = start_tx->ste_phys; - - ifp->if_timer = 5; - return; } diff --git a/sys/dev/pci/if_stereg.h b/sys/dev/pci/if_stereg.h index 82ff0e98cd1..dde200ec9bb 100644 --- a/sys/dev/pci/if_stereg.h +++ b/sys/dev/pci/if_stereg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_stereg.h,v 1.6 2004/05/19 11:37:00 brad Exp $ */ +/* $OpenBSD: if_stereg.h,v 1.7 2004/08/09 16:33:55 canacar Exp $ */ /* * Copyright (c) 1997, 1998, 1999 * Bill Paul . All rights reserved. @@ -89,9 +89,15 @@ #define STE_TX_RECLAIM_THRESH 0x5D #define STE_PHYCTL 0x5E #define STE_MAR0 0x60 -#define STE_MAR1 0x64 +#define STE_MAR1 0x62 +#define STE_MAR2 0x64 +#define STE_MAR3 0x66 #define STE_STATS 0x68 +#define STE_LATE_COLLS 0x75 +#define STE_MULTI_COLLS 0x76 +#define STE_SINGLE_COLLS 0x77 + #define STE_DMACTL_RXDMA_STOPPED 0x00000001 #define STE_DMACTL_TXDMA_CMPREQ 0x00000002 #define STE_DMACTL_TXDMA_STOPPED 0x00000004 @@ -223,13 +229,13 @@ * The number of bytes that must in present in the TX FIFO before * transmission begins. Value should be in increments of 4 bytes. */ -#define STE_TXSTART_THRESH 0x1FFF +#define STE_TXSTART_THRESH 0x1FFC /* * Number of bytes that must be present in the RX FIFO before * an RX EARLY interrupt is generated. */ -#define STE_RXEARLY_THRESH 0x1FFF +#define STE_RXEARLY_THRESH 0x1FFC #define STE_WAKEEVENT_WAKEPKT_ENB 0x01 #define STE_WAKEEVENT_MAGICPKT_ENB 0x02 @@ -271,8 +277,9 @@ #define STE_IMR_RX_DMADONE 0x0400 #define STE_INTRS \ - (STE_IMR_RX_DMADONE|STE_IMR_TX_DMADONE|STE_IMR_STATS_OFLOW| \ - STE_IMR_TX_DONE|STE_IMR_HOSTERR|STE_IMR_RX_EARLY) + (STE_IMR_RX_DMADONE|STE_IMR_TX_DMADONE| \ + STE_IMR_TX_DONE|STE_IMR_HOSTERR| \ + STE_IMR_LINKEVENT) #define STE_ISR_INTLATCH 0x0001 #define STE_ISR_HOSTERR 0x0002 @@ -405,7 +412,7 @@ struct ste_frag { #define STE_FRAG_LAST 0x80000000 #define STE_FRAG_LEN 0x00001FFF -#define STE_MAXFRAGS 63 +#define STE_MAXFRAGS 8 struct ste_desc { u_int32_t ste_next; @@ -458,9 +465,10 @@ struct ste_desc_onefrag { #define STE_TIMEOUT 1000 #define STE_MIN_FRAMELEN 60 #define STE_PACKET_SIZE 1536 -#define STE_RX_LIST_CNT 128 -#define STE_TX_LIST_CNT 256 +#define STE_RX_LIST_CNT 64 +#define STE_TX_LIST_CNT 128 #define STE_INC(x, y) (x) = (x + 1) % y +#define STE_NEXT(x, y) (x + 1) % y struct ste_type { u_int16_t ste_vid; @@ -471,14 +479,12 @@ struct ste_type { struct ste_list_data { struct ste_desc_onefrag ste_rx_list[STE_RX_LIST_CNT]; struct ste_desc ste_tx_list[STE_TX_LIST_CNT]; - u_int8_t ste_pad[STE_MIN_FRAMELEN]; }; struct ste_chain { struct ste_desc *ste_ptr; struct mbuf *ste_mbuf; struct ste_chain *ste_next; - struct ste_chain *ste_prev; u_int32_t ste_phys; }; @@ -495,7 +501,6 @@ struct ste_chain_data { int ste_tx_prod; int ste_tx_cons; - int ste_tx_cnt; }; struct ste_softc { @@ -509,6 +514,7 @@ struct ste_softc { int ste_tx_thresh; u_int8_t ste_link; int ste_if_flags; + struct ste_chain *ste_tx_prev; struct ste_list_data *ste_ldata; caddr_t ste_ldata_ptr; struct ste_chain_data ste_cdata; -- cgit v1.2.3