- merge re_encap() into re_start() so that we can handle each error case

more flexible
- account a number of free txq and use it to see if packets are queued or sent
- assume free TX descriptors are not owned by the chip in re_start() and
  panic if there is any inconsistency on them #ifdef DIAGNOSTIC case.
- replace a magic number (reserved TX descs?) with macro
This commit is contained in:
tsutsui 2006-11-12 03:09:37 +00:00
parent 92bb57d7f6
commit 0d397b62fe
2 changed files with 197 additions and 225 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: rtl8169.c,v 1.58 2006/11/11 13:51:44 tsutsui Exp $ */
/* $NetBSD: rtl8169.c,v 1.59 2006/11/12 03:09:37 tsutsui Exp $ */
/*
* Copyright (c) 1997, 1998-2003
@ -153,8 +153,6 @@
#include <dev/ic/rtl8169var.h>
static int re_encap(struct rtk_softc *, struct mbuf *, int *);
static int re_newbuf(struct rtk_softc *, int, struct mbuf *);
static int re_rx_list_init(struct rtk_softc *);
static int re_tx_list_init(struct rtk_softc *);
@ -682,8 +680,8 @@ re_attach(struct rtk_softc *sc)
for (i = 0; i < RE_TX_QLEN; i++) {
error = bus_dmamap_create(sc->sc_dmat,
round_page(IP_MAXPACKET),
RE_TX_DESC_CNT(sc) - 4, RE_TDESC_CMD_FRAGLEN, 0, 0,
&sc->re_ldata.re_txq[i].txq_dmamap);
RE_TX_DESC_CNT(sc) - RE_NTXDESC_RSVD, RE_TDESC_CMD_FRAGLEN,
0, 0, &sc->re_ldata.re_txq[i].txq_dmamap);
if (error) {
aprint_error("%s: can't create DMA map for TX\n",
sc->sc_dev.dv_xname);
@ -1106,6 +1104,7 @@ re_tx_list_init(struct rtk_softc *sc)
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
sc->re_ldata.re_txq_prodidx = 0;
sc->re_ldata.re_txq_considx = 0;
sc->re_ldata.re_txq_free = RE_TX_QLEN;
sc->re_ldata.re_tx_free = RE_TX_DESC_CNT(sc);
sc->re_ldata.re_tx_nextfree = 0;
@ -1305,17 +1304,14 @@ re_txeof(struct rtk_softc *sc)
struct re_txq *txq;
uint32_t txstat;
int idx, descidx;
boolean_t done = FALSE;
ifp = &sc->ethercom.ec_if;
for (idx = sc->re_ldata.re_txq_considx;; idx = RE_NEXT_TXQ(sc, idx)) {
for (idx = sc->re_ldata.re_txq_considx;
sc->re_ldata.re_txq_free < RE_TX_QLEN;
idx = RE_NEXT_TXQ(sc, idx), sc->re_ldata.re_txq_free++) {
txq = &sc->re_ldata.re_txq[idx];
if (txq->txq_mbuf == NULL) {
KASSERT(idx == sc->re_ldata.re_txq_prodidx);
break;
}
KASSERT(txq->txq_mbuf != NULL);
descidx = txq->txq_descidx;
RE_TXDESCSYNC(sc, descidx,
@ -1342,14 +1338,12 @@ re_txeof(struct rtk_softc *sc)
ifp->if_oerrors++;
else
ifp->if_opackets++;
done = TRUE;
}
if (done) {
sc->re_ldata.re_txq_considx = idx;
sc->re_ldata.re_txq_considx = idx;
if (sc->re_ldata.re_txq_free > 0)
ifp->if_flags &= ~IFF_OACTIVE;
}
/*
* If not all descriptors have been released reaped yet,
@ -1357,7 +1351,7 @@ re_txeof(struct rtk_softc *sc)
* interrupt that will cause us to re-enter this routine.
* This is done in case the transmitter has gone idle.
*/
if (sc->re_ldata.re_tx_free < RE_TX_DESC_CNT(sc))
if (sc->re_ldata.re_txq_free < RE_TX_QLEN)
CSR_WRITE_4(sc, RTK_TIMERCNT, 1);
else
ifp->if_timer = 0;
@ -1502,171 +1496,7 @@ re_intr(void *arg)
return handled;
}
static int
re_encap(struct rtk_softc *sc, struct mbuf *m, int *idx)
{
bus_dmamap_t map;
int error, seg, uidx, startidx, curidx, lastidx;
#ifdef RE_VLAN
struct m_tag *mtag;
#endif
struct re_desc *d;
uint32_t cmdstat, re_flags;
struct re_txq *txq;
if (sc->re_ldata.re_tx_free <= 4) {
return EFBIG;
}
/*
* Set up checksum offload. Note: checksum offload bits must
* appear in all descriptors of a multi-descriptor transmit
* attempt. (This is according to testing done with an 8169
* chip. I'm not sure if this is a requirement or a bug.)
*/
if ((m->m_pkthdr.csum_flags & M_CSUM_TSOv4) != 0) {
uint32_t segsz = m->m_pkthdr.segsz;
re_flags = RE_TDESC_CMD_LGSEND |
(segsz << RE_TDESC_CMD_MSSVAL_SHIFT);
} else {
/*
* set RE_TDESC_CMD_IPCSUM if any checksum offloading
* is requested. otherwise, RE_TDESC_CMD_TCPCSUM/
* RE_TDESC_CMD_UDPCSUM doesn't make effects.
*/
re_flags = 0;
if ((m->m_pkthdr.csum_flags &
(M_CSUM_IPv4 | M_CSUM_TCPv4 | M_CSUM_UDPv4)) != 0) {
re_flags |= RE_TDESC_CMD_IPCSUM;
if (m->m_pkthdr.csum_flags & M_CSUM_TCPv4) {
re_flags |= RE_TDESC_CMD_TCPCSUM;
} else if (m->m_pkthdr.csum_flags & M_CSUM_UDPv4) {
re_flags |= RE_TDESC_CMD_UDPCSUM;
}
}
}
txq = &sc->re_ldata.re_txq[*idx];
map = txq->txq_dmamap;
error = bus_dmamap_load_mbuf(sc->sc_dmat, map, m,
BUS_DMA_WRITE|BUS_DMA_NOWAIT);
if (error) {
/* XXX try to defrag if EFBIG? */
aprint_error("%s: can't map mbuf (error %d)\n",
sc->sc_dev.dv_xname, error);
return error;
}
if (map->dm_nsegs > sc->re_ldata.re_tx_free - 4) {
error = EFBIG;
goto fail_unload;
}
/*
* Make sure that the caches are synchronized before we
* ask the chip to start DMA for the packet data.
*/
bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
BUS_DMASYNC_PREWRITE);
/*
* Map the segment array into descriptors. Note that we set the
* start-of-frame and end-of-frame markers for either TX or RX, but
* they really only have meaning in the TX case. (In the RX case,
* it's the chip that tells us where packets begin and end.)
* We also keep track of the end of the ring and set the
* end-of-ring bits as needed, and we set the ownership bits
* in all except the very first descriptor. (The caller will
* set this descriptor later when it start transmission or
* reception.)
*/
curidx = startidx = sc->re_ldata.re_tx_nextfree;
lastidx = -1;
for (seg = 0; seg < map->dm_nsegs;
seg++, curidx = RE_NEXT_TX_DESC(sc, curidx)) {
d = &sc->re_ldata.re_tx_list[curidx];
RE_TXDESCSYNC(sc, curidx,
BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
cmdstat = le32toh(d->re_cmdstat);
RE_TXDESCSYNC(sc, curidx, BUS_DMASYNC_PREREAD);
if (cmdstat & RE_TDESC_STAT_OWN) {
aprint_error("%s: tried to map busy TX descriptor\n",
sc->sc_dev.dv_xname);
for (; seg > 0; seg--) {
uidx = (curidx + RE_TX_DESC_CNT(sc) - seg) %
RE_TX_DESC_CNT(sc);
sc->re_ldata.re_tx_list[uidx].re_cmdstat = 0;
RE_TXDESCSYNC(sc, uidx,
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
}
error = ENOBUFS;
goto fail_unload;
}
cmdstat = map->dm_segs[seg].ds_len;
if (seg == 0)
cmdstat |= RE_TDESC_CMD_SOF;
else
cmdstat |= RE_TDESC_CMD_OWN;
if (seg == map->dm_nsegs - 1) {
cmdstat |= RE_TDESC_CMD_EOF;
lastidx = curidx;
}
if (curidx == (RE_TX_DESC_CNT(sc) - 1))
cmdstat |= RE_TDESC_CMD_EOR;
d->re_cmdstat = htole32(cmdstat | re_flags);
d->re_bufaddr_lo =
htole32(RE_ADDR_LO(map->dm_segs[seg].ds_addr));
d->re_bufaddr_hi =
htole32(RE_ADDR_HI(map->dm_segs[seg].ds_addr));
RE_TXDESCSYNC(sc, curidx,
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
}
KASSERT(lastidx != -1);
/*
* Set up hardware VLAN tagging. Note: vlan tag info must
* appear in the first descriptor of a multi-descriptor
* transmission attempt.
*/
#ifdef RE_VLAN
if ((mtag = VLAN_OUTPUT_TAG(&sc->ethercom, m)) != NULL) {
sc->re_ldata.re_tx_list[startidx].re_vlanctl =
htole32(htons(VLAN_TAG_VALUE(mtag)) |
RE_TDESC_VLANCTL_TAG);
}
#endif
/* Transfer ownership of packet to the chip. */
sc->re_ldata.re_tx_list[startidx].re_cmdstat |=
htole32(RE_TDESC_CMD_OWN);
RE_TXDESCSYNC(sc, startidx, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
/* update info of TX queue and descriptors */
txq->txq_mbuf = m;
txq->txq_descidx = lastidx;
sc->re_ldata.re_tx_free -= map->dm_nsegs;
sc->re_ldata.re_tx_nextfree = curidx;
*idx = RE_NEXT_TXQ(sc, *idx);
return 0;
fail_unload:
bus_dmamap_unload(sc->sc_dmat, map);
return error;
}
/*
* Main transmit routine for C+ and gigE NICs.
@ -1676,41 +1506,179 @@ static void
re_start(struct ifnet *ifp)
{
struct rtk_softc *sc;
int idx;
boolean_t done = FALSE;
struct mbuf *m;
bus_dmamap_t map;
struct re_txq *txq;
struct re_desc *d;
#ifdef RE_VLAN
struct m_tag *mtag;
#endif
uint32_t cmdstat, re_flags;
int ofree, idx, error, seg;
int startdesc, curdesc, lastdesc;
sc = ifp->if_softc;
ofree = sc->re_ldata.re_txq_free;
idx = sc->re_ldata.re_txq_prodidx;
for (;;) {
struct mbuf *m;
int error;
for (idx = sc->re_ldata.re_txq_prodidx;; idx = RE_NEXT_TXQ(sc, idx)) {
IFQ_POLL(&ifp->if_snd, m);
if (m == NULL)
break;
if (sc->re_ldata.re_txq[idx].txq_mbuf != NULL) {
KASSERT(idx == sc->re_ldata.re_txq_considx);
if (sc->re_ldata.re_txq_free == 0 ||
sc->re_ldata.re_tx_free <= RE_NTXDESC_RSVD) {
/* no more free slots left */
ifp->if_flags |= IFF_OACTIVE;
break;
}
error = re_encap(sc, m, &idx);
if (error == EFBIG &&
sc->re_ldata.re_tx_free == RE_TX_DESC_CNT(sc)) {
/*
* Set up checksum offload. Note: checksum offload bits must
* appear in all descriptors of a multi-descriptor transmit
* attempt. (This is according to testing done with an 8169
* chip. I'm not sure if this is a requirement or a bug.)
*/
if ((m->m_pkthdr.csum_flags & M_CSUM_TSOv4) != 0) {
uint32_t segsz = m->m_pkthdr.segsz;
re_flags = RE_TDESC_CMD_LGSEND |
(segsz << RE_TDESC_CMD_MSSVAL_SHIFT);
} else {
/*
* set RE_TDESC_CMD_IPCSUM if any checksum offloading
* is requested. otherwise, RE_TDESC_CMD_TCPCSUM/
* RE_TDESC_CMD_UDPCSUM doesn't make effects.
*/
re_flags = 0;
if ((m->m_pkthdr.csum_flags &
(M_CSUM_IPv4 | M_CSUM_TCPv4 | M_CSUM_UDPv4))
!= 0) {
re_flags |= RE_TDESC_CMD_IPCSUM;
if (m->m_pkthdr.csum_flags & M_CSUM_TCPv4) {
re_flags |= RE_TDESC_CMD_TCPCSUM;
} else if (m->m_pkthdr.csum_flags &
M_CSUM_UDPv4) {
re_flags |= RE_TDESC_CMD_UDPCSUM;
}
}
}
txq = &sc->re_ldata.re_txq[idx];
map = txq->txq_dmamap;
error = bus_dmamap_load_mbuf(sc->sc_dmat, map, m,
BUS_DMA_WRITE|BUS_DMA_NOWAIT);
if (error) {
/* XXX try to defrag if EFBIG? */
aprint_error("%s: can't map mbuf (error %d)\n",
sc->sc_dev.dv_xname, error);
IFQ_DEQUEUE(&ifp->if_snd, m);
m_freem(m);
ifp->if_oerrors++;
continue;
}
if (error) {
if (map->dm_nsegs > sc->re_ldata.re_tx_free - RE_NTXDESC_RSVD) {
/*
* Not enough free descriptors to transmit this packet.
*/
ifp->if_flags |= IFF_OACTIVE;
bus_dmamap_unload(sc->sc_dmat, map);
break;
}
IFQ_DEQUEUE(&ifp->if_snd, m);
/*
* Make sure that the caches are synchronized before we
* ask the chip to start DMA for the packet data.
*/
bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
BUS_DMASYNC_PREWRITE);
/*
* Map the segment array into descriptors.
* Note that we set the start-of-frame and
* end-of-frame markers for either TX or RX,
* but they really only have meaning in the TX case.
* (In the RX case, it's the chip that tells us
* where packets begin and end.)
* We also keep track of the end of the ring
* and set the end-of-ring bits as needed,
* and we set the ownership bits in all except
* the very first descriptor. (The caller will
* set this descriptor later when it start
* transmission or reception.)
*/
curdesc = startdesc = sc->re_ldata.re_tx_nextfree;
lastdesc = -1;
for (seg = 0; seg < map->dm_nsegs;
seg++, curdesc = RE_NEXT_TX_DESC(sc, curdesc)) {
d = &sc->re_ldata.re_tx_list[curdesc];
#ifdef DIAGNISTIC
RE_TXDESCSYNC(sc, curdesc,
BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
cmdstat = le32toh(d->re_cmdstat);
RE_TXDESCSYNC(sc, curdesc, BUS_DMASYNC_PREREAD);
if (cmdstat & RE_TDESC_STAT_OWN) {
panic("%s: tried to map busy TX descriptor",
sc->sc_dev.dv_xname);
}
#endif
d->re_bufaddr_lo =
htole32(RE_ADDR_LO(map->dm_segs[seg].ds_addr));
d->re_bufaddr_hi =
htole32(RE_ADDR_HI(map->dm_segs[seg].ds_addr));
cmdstat = re_flags | map->dm_segs[seg].ds_len;
if (seg == 0)
cmdstat |= RE_TDESC_CMD_SOF;
else
cmdstat |= RE_TDESC_CMD_OWN;
if (curdesc == (RE_TX_DESC_CNT(sc) - 1))
cmdstat |= RE_TDESC_CMD_EOR;
if (seg == map->dm_nsegs - 1) {
cmdstat |= RE_TDESC_CMD_EOF;
lastdesc = curdesc;
}
d->re_cmdstat = htole32(cmdstat);
RE_TXDESCSYNC(sc, curdesc,
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
}
KASSERT(lastdesc != -1);
/*
* Set up hardware VLAN tagging. Note: vlan tag info must
* appear in the first descriptor of a multi-descriptor
* transmission attempt.
*/
#ifdef RE_VLAN
if ((mtag = VLAN_OUTPUT_TAG(&sc->ethercom, m)) != NULL) {
sc->re_ldata.re_tx_list[startdesc].re_vlanctl =
htole32(htons(VLAN_TAG_VALUE(mtag)) |
RE_TDESC_VLANCTL_TAG);
}
#endif
/* Transfer ownership of packet to the chip. */
sc->re_ldata.re_tx_list[startdesc].re_cmdstat |=
htole32(RE_TDESC_CMD_OWN);
RE_TXDESCSYNC(sc, startdesc,
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
/* update info of TX queue and descriptors */
txq->txq_mbuf = m;
txq->txq_descidx = lastdesc;
sc->re_ldata.re_txq_free--;
sc->re_ldata.re_tx_free -= map->dm_nsegs;
sc->re_ldata.re_tx_nextfree = curdesc;
#if NBPFILTER > 0
/*
* If there's a BPF listener, bounce a copy of this frame
@ -1719,39 +1687,40 @@ re_start(struct ifnet *ifp)
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, m);
#endif
done = TRUE;
}
if (!done) {
return;
if (sc->re_ldata.re_txq_free < ofree) {
/*
* TX packets are enqueued.
*/
sc->re_ldata.re_txq_prodidx = idx;
/*
* Start the transmitter to poll.
*
* RealTek put the TX poll request register in a different
* location on the 8169 gigE chip. I don't know why.
*/
if (sc->rtk_type == RTK_8169)
CSR_WRITE_2(sc, RTK_GTXSTART, RTK_TXSTART_START);
else
CSR_WRITE_1(sc, RTK_TXSTART, RTK_TXSTART_START);
/*
* Use the countdown timer for interrupt moderation.
* 'TX done' interrupts are disabled. Instead, we reset the
* countdown timer, which will begin counting until it hits
* the value in the TIMERINT register, and then trigger an
* interrupt. Each time we write to the TIMERCNT register,
* the timer count is reset to 0.
*/
CSR_WRITE_4(sc, RTK_TIMERCNT, 1);
/*
* Set a timeout in case the chip goes out to lunch.
*/
ifp->if_timer = 5;
}
sc->re_ldata.re_txq_prodidx = idx;
/*
* RealTek put the TX poll request register in a different
* location on the 8169 gigE chip. I don't know why.
*/
if (sc->rtk_type == RTK_8169)
CSR_WRITE_2(sc, RTK_GTXSTART, RTK_TXSTART_START);
else
CSR_WRITE_1(sc, RTK_TXSTART, RTK_TXSTART_START);
/*
* Use the countdown timer for interrupt moderation.
* 'TX done' interrupts are disabled. Instead, we reset the
* countdown timer, which will begin counting until it hits
* the value in the TIMERINT register, and then trigger an
* interrupt. Each time we write to the TIMERCNT register,
* the timer count is reset to 0.
*/
CSR_WRITE_4(sc, RTK_TIMERCNT, 1);
/*
* Set a timeout in case the chip goes out to lunch.
*/
ifp->if_timer = 5;
}
static int

View File

@ -1,4 +1,4 @@
/* $NetBSD: rtl81x9var.h,v 1.33 2006/11/11 13:41:06 tsutsui Exp $ */
/* $NetBSD: rtl81x9var.h,v 1.34 2006/11/12 03:09:37 tsutsui Exp $ */
/*
* Copyright (c) 1997, 1998
@ -137,6 +137,8 @@ struct rtk_mii_frame {
#define RE_TX_DESC_CNT_8169 1024
#define RE_TX_QLEN 64
#define RE_NTXDESC_RSVD 4
struct re_rxsoft {
struct mbuf *rxs_mbuf;
bus_dmamap_t rxs_dmamap;
@ -152,6 +154,7 @@ struct re_list_data {
struct re_txq re_txq[RE_TX_QLEN];
int re_txq_considx;
int re_txq_prodidx;
int re_txq_free;
bus_dmamap_t re_tx_list_map;
struct re_desc *re_tx_list;