Rework the transmit logic. Use a "free" and "dirty" list of

transmit jobs.  The previous logic occasionally, under heavy
load, would attempt to sync and unload DMA maps that weren't
currently in use, and also occasionally leaked mbufs under
heavy load.

Yay, NFS now works pretty well on my Dreamcast.
This commit is contained in:
thorpej 2001-02-02 04:34:19 +00:00
parent 295df3b3d7
commit 311479928a
2 changed files with 82 additions and 75 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: rtl81x9.c,v 1.30 2001/02/02 03:51:51 thorpej Exp $ */
/* $NetBSD: rtl81x9.c,v 1.31 2001/02/02 04:34:19 thorpej Exp $ */
/*
* Copyright (c) 1997, 1998
@ -631,6 +631,7 @@ rtk_attach(sc)
struct rtk_softc *sc;
{
struct ifnet *ifp;
struct rtk_tx_desc *txd;
u_int16_t val;
u_int8_t eaddr[ETHER_ADDR_LEN];
int error;
@ -691,14 +692,21 @@ rtk_attach(sc)
goto fail_3;
}
for (i = 0; i < RTK_TX_LIST_CNT; i++)
for (i = 0; i < RTK_TX_LIST_CNT; i++) {
txd = &sc->rtk_tx_descs[i];
if ((error = bus_dmamap_create(sc->sc_dmat,
MCLBYTES, 1, MCLBYTES, 0, BUS_DMA_NOWAIT,
&sc->snd_dmamap[i])) != 0) {
&txd->txd_dmamap)) != 0) {
printf("%s: can't create snd buffer DMA map,"
" error = %d\n", sc->sc_dev.dv_xname, error);
goto fail_4;
}
txd->txd_txaddr = RTK_TXADDR0 + (i * 4);
txd->txd_txstat = RTK_TXSTAT0 + (i * 4);
}
SIMPLEQ_INIT(&sc->rtk_tx_free);
SIMPLEQ_INIT(&sc->rtk_tx_dirty);
/*
* From this point forward, the attachment cannot fail. A failure
* before this releases all resources thar may have been
@ -766,9 +774,11 @@ rtk_attach(sc)
return;
fail_4:
for (i = 0; i < RTK_TX_LIST_CNT; i++)
if (sc->snd_dmamap[i] != NULL)
bus_dmamap_destroy(sc->sc_dmat, sc->snd_dmamap[i]);
for (i = 0; i < RTK_TX_LIST_CNT; i++) {
txd = &sc->rtk_tx_descs[i];
if (txd->txd_dmamap != NULL)
bus_dmamap_destroy(sc->sc_dmat, txd->txd_dmamap);
}
fail_3:
bus_dmamap_destroy(sc->sc_dmat, sc->recv_dmamap);
fail_2:
@ -786,18 +796,19 @@ rtk_attach(sc)
STATIC int rtk_list_tx_init(sc)
struct rtk_softc *sc;
{
struct rtk_chain_data *cd;
struct rtk_tx_desc *txd;
int i;
cd = &sc->rtk_cdata;
for (i = 0; i < RTK_TX_LIST_CNT; i++) {
cd->rtk_tx_chain[i] = NULL;
CSR_WRITE_4(sc,
RTK_TXADDR0 + (i * sizeof(u_int32_t)), 0x0000000);
}
while ((txd = SIMPLEQ_FIRST(&sc->rtk_tx_dirty)) != NULL)
SIMPLEQ_REMOVE_HEAD(&sc->rtk_tx_dirty, txd, txd_q);
while ((txd = SIMPLEQ_FIRST(&sc->rtk_tx_free)) != NULL)
SIMPLEQ_REMOVE_HEAD(&sc->rtk_tx_free, txd, txd_q);
sc->rtk_cdata.cur_tx = 0;
sc->rtk_cdata.last_tx = 0;
for (i = 0; i < RTK_TX_LIST_CNT; i++) {
txd = &sc->rtk_tx_descs[i];
CSR_WRITE_4(sc, txd->txd_txaddr, 0);
SIMPLEQ_INSERT_TAIL(&sc->rtk_tx_free, txd, txd_q);
}
return (0);
}
@ -838,6 +849,7 @@ rtk_detach(sc)
struct rtk_softc *sc;
{
struct ifnet *ifp = &sc->ethercom.ec_if;
struct rtk_tx_desc *txd;
int i;
/*
@ -858,9 +870,11 @@ rtk_detach(sc)
ether_ifdetach(ifp);
if_detach(ifp);
for (i = 0; i < RTK_TX_LIST_CNT; i++)
if (sc->snd_dmamap[i] != NULL)
bus_dmamap_destroy(sc->sc_dmat, sc->snd_dmamap[i]);
for (i = 0; i < RTK_TX_LIST_CNT; i++) {
txd = &sc->rtk_tx_descs[i];
if (txd->txd_dmamap != NULL)
bus_dmamap_destroy(sc->sc_dmat, txd->txd_dmamap);
}
bus_dmamap_destroy(sc->sc_dmat, sc->recv_dmamap);
bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->rtk_rx_buf,
RTK_RXBUFLEN + 16);
@ -1154,6 +1168,7 @@ STATIC void rtk_txeof(sc)
struct rtk_softc *sc;
{
struct ifnet *ifp;
struct rtk_tx_desc *txd;
u_int32_t txstat;
ifp = &sc->ethercom.ec_if;
@ -1165,20 +1180,19 @@ STATIC void rtk_txeof(sc)
* Go through our tx list and free mbufs for those
* frames that have been uploaded.
*/
do {
txstat = CSR_READ_4(sc, RTK_LAST_TXSTAT(sc));
while ((txd = SIMPLEQ_FIRST(&sc->rtk_tx_dirty)) != NULL) {
txstat = CSR_READ_4(sc, txd->txd_txstat);
if ((txstat & (RTK_TXSTAT_TX_OK|
RTK_TXSTAT_TX_UNDERRUN|RTK_TXSTAT_TXABRT)) == 0)
break;
bus_dmamap_sync(sc->sc_dmat,
sc->snd_dmamap[sc->rtk_cdata.last_tx], 0,
sc->snd_dmamap[sc->rtk_cdata.last_tx]->dm_mapsize,
BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->sc_dmat,
sc->snd_dmamap[sc->rtk_cdata.last_tx]);
m_freem(RTK_LAST_TXMBUF(sc));
RTK_LAST_TXMBUF(sc) = NULL;
SIMPLEQ_REMOVE_HEAD(&sc->rtk_tx_dirty, txd, txd_q);
bus_dmamap_sync(sc->sc_dmat, txd->txd_dmamap, 0,
txd->txd_dmamap->dm_mapsize, BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->sc_dmat, txd->txd_dmamap);
m_freem(txd->txd_mbuf);
txd->txd_mbuf = NULL;
ifp->if_collisions += (txstat & RTK_TXSTAT_COLLCNT) >> 24;
@ -1189,9 +1203,9 @@ STATIC void rtk_txeof(sc)
if (txstat & (RTK_TXSTAT_TXABRT|RTK_TXSTAT_OUTOFWIN))
CSR_WRITE_4(sc, RTK_TXCFG, RTK_TXCFG_CONFIG);
}
RTK_INC(sc->rtk_cdata.last_tx);
SIMPLEQ_INSERT_TAIL(&sc->rtk_tx_free, txd, txd_q);
ifp->if_flags &= ~IFF_OACTIVE;
} while (sc->rtk_cdata.last_tx != sc->rtk_cdata.cur_tx);
}
}
int rtk_intr(arg)
@ -1251,26 +1265,25 @@ STATIC void rtk_start(ifp)
struct ifnet *ifp;
{
struct rtk_softc *sc;
struct rtk_tx_desc *txd;
struct mbuf *m_head = NULL, *m_new;
int error, idx, len;
int error, len;
sc = ifp->if_softc;
while(RTK_CUR_TXMBUF(sc) == NULL) {
while ((txd = SIMPLEQ_FIRST(&sc->rtk_tx_free)) != NULL) {
IFQ_POLL(&ifp->if_snd, m_head);
if (m_head == NULL)
break;
m_new = NULL;
idx = sc->rtk_cdata.cur_tx;
/*
* Load the DMA map. If this fails, the packet didn't
* fit in one DMA segment, and we need to copy. Note,
* the packet must also be aligned.
*/
if ((mtod(m_head, bus_addr_t) & 3) != 0 ||
bus_dmamap_load_mbuf(sc->sc_dmat, sc->snd_dmamap[idx],
bus_dmamap_load_mbuf(sc->sc_dmat, txd->txd_dmamap,
m_head, BUS_DMA_NOWAIT) != 0) {
MGETHDR(m_new, M_DONTWAIT, MT_DATA);
if (m_new == NULL) {
@ -1292,7 +1305,7 @@ STATIC void rtk_start(ifp)
m_new->m_pkthdr.len = m_new->m_len =
m_head->m_pkthdr.len;
error = bus_dmamap_load_mbuf(sc->sc_dmat,
sc->snd_dmamap[idx], m_new, BUS_DMA_NOWAIT);
txd->txd_dmamap, m_new, BUS_DMA_NOWAIT);
if (error) {
printf("%s: unable to load Tx buffer, "
"error = %d\n", sc->sc_dev.dv_xname, error);
@ -1304,8 +1317,10 @@ STATIC void rtk_start(ifp)
m_freem(m_head);
m_head = m_new;
}
txd->txd_mbuf = m_head;
RTK_CUR_TXMBUF(sc) = m_head;
SIMPLEQ_REMOVE_HEAD(&sc->rtk_tx_free, txd, txd_q);
SIMPLEQ_INSERT_TAIL(&sc->rtk_tx_dirty, txd, txd_q);
#if NBPFILTER > 0
/*
@ -1313,24 +1328,22 @@ STATIC void rtk_start(ifp)
* to him.
*/
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, RTK_CUR_TXMBUF(sc));
bpf_mtap(ifp->if_bpf, m_head);
#endif
/*
* Transmit the frame.
*/
bus_dmamap_sync(sc->sc_dmat,
sc->snd_dmamap[idx], 0, sc->snd_dmamap[idx]->dm_mapsize,
txd->txd_dmamap, 0, txd->txd_dmamap->dm_mapsize,
BUS_DMASYNC_PREWRITE);
len = sc->snd_dmamap[idx]->dm_segs[0].ds_len;
len = txd->txd_dmamap->dm_segs[0].ds_len;
if (len < (ETHER_MIN_LEN - ETHER_CRC_LEN))
len = (ETHER_MIN_LEN - ETHER_CRC_LEN);
CSR_WRITE_4(sc, RTK_CUR_TXADDR(sc),
sc->snd_dmamap[idx]->dm_segs[0].ds_addr);
CSR_WRITE_4(sc, RTK_CUR_TXSTAT(sc), RTK_TX_EARLYTHRESH | len);
RTK_INC(sc->rtk_cdata.cur_tx);
CSR_WRITE_4(sc, txd->txd_txaddr,
txd->txd_dmamap->dm_segs[0].ds_addr);
CSR_WRITE_4(sc, txd->txd_txstat, RTK_TX_EARLYTHRESH | len);
}
/*
@ -1338,7 +1351,7 @@ STATIC void rtk_start(ifp)
* full. Mark the NIC as busy until it drains some of the
* packets from the queue.
*/
if (RTK_CUR_TXMBUF(sc) != NULL)
if (SIMPLEQ_FIRST(&sc->rtk_tx_free) == NULL)
ifp->if_flags |= IFF_OACTIVE;
/*
@ -1536,7 +1549,7 @@ STATIC void rtk_stop(ifp, disable)
int disable;
{
struct rtk_softc *sc = ifp->if_softc;
int i;
struct rtk_tx_desc *txd;
callout_stop(&sc->rtk_tick_ch);
@ -1548,13 +1561,12 @@ STATIC void rtk_stop(ifp, disable)
/*
* Free the TX list buffers.
*/
for (i = 0; i < RTK_TX_LIST_CNT; i++) {
if (sc->rtk_cdata.rtk_tx_chain[i] != NULL) {
bus_dmamap_unload(sc->sc_dmat, sc->snd_dmamap[i]);
m_freem(sc->rtk_cdata.rtk_tx_chain[i]);
sc->rtk_cdata.rtk_tx_chain[i] = NULL;
CSR_WRITE_4(sc, RTK_TXADDR0 + i, 0x0000000);
}
while ((txd = SIMPLEQ_FIRST(&sc->rtk_tx_dirty)) != NULL) {
SIMPLEQ_REMOVE_HEAD(&sc->rtk_tx_dirty, txd, txd_q);
bus_dmamap_unload(sc->sc_dmat, txd->txd_dmamap);
m_freem(txd->txd_mbuf);
txd->txd_mbuf = NULL;
CSR_WRITE_4(sc, txd->txd_txaddr, 0);
}
if (disable)

View File

@ -1,4 +1,4 @@
/* $NetBSD: rtl81x9var.h,v 1.8 2001/02/02 03:51:51 thorpej Exp $ */
/* $NetBSD: rtl81x9var.h,v 1.9 2001/02/02 04:34:19 thorpej Exp $ */
/*
* Copyright (c) 1997, 1998
@ -37,20 +37,6 @@
#define RTK_ETHER_ALIGN 2
#define RTK_RXSTAT_LEN 4
struct rtk_chain_data {
struct mbuf *rtk_tx_chain[RTK_TX_LIST_CNT];
int last_tx;
int cur_tx;
};
#define RTK_INC(x) (x = (x + 1) % RTK_TX_LIST_CNT)
#define RTK_CUR_TXADDR(x) ((x->rtk_cdata.cur_tx * 4) + RTK_TXADDR0)
#define RTK_CUR_TXSTAT(x) ((x->rtk_cdata.cur_tx * 4) + RTK_TXSTAT0)
#define RTK_CUR_TXMBUF(x) (x->rtk_cdata.rtk_tx_chain[x->rtk_cdata.cur_tx])
#define RTK_LAST_TXADDR(x) ((x->rtk_cdata.last_tx * 4) + RTK_TXADDR0)
#define RTK_LAST_TXSTAT(x) ((x->rtk_cdata.last_tx * 4) + RTK_TXSTAT0)
#define RTK_LAST_TXMBUF(x) (x->rtk_cdata.rtk_tx_chain[x->rtk_cdata.last_tx])
struct rtk_type {
u_int16_t rtk_vid;
u_int16_t rtk_did;
@ -78,6 +64,14 @@ struct rtk_mii_frame {
#define RTK_8129 1
#define RTK_8139 2
struct rtk_tx_desc {
SIMPLEQ_ENTRY(rtk_tx_desc) txd_q;
struct mbuf *txd_mbuf;
bus_dmamap_t txd_dmamap;
bus_addr_t txd_txaddr;
bus_addr_t txd_txstat;
};
struct rtk_softc {
struct device sc_dev; /* generic device structures */
struct ethercom ethercom; /* interface info */
@ -93,8 +87,9 @@ struct rtk_softc {
bus_dmamap_t recv_dmamap;
caddr_t rtk_rx_buf;
struct rtk_chain_data rtk_cdata;
bus_dmamap_t snd_dmamap[RTK_TX_LIST_CNT];
struct rtk_tx_desc rtk_tx_descs[RTK_TX_LIST_CNT];
SIMPLEQ_HEAD(, rtk_tx_desc) rtk_tx_free;
SIMPLEQ_HEAD(, rtk_tx_desc) rtk_tx_dirty;
int sc_flags; /* misc flags */
void *sc_sdhook; /* shutdown hook */