Call for hme(4) testers
Pyun YongHyeon
yongari at kt-is.co.kr
Mon Feb 21 10:25:15 GMT 2005
Hi,
Here is an optimized hme(4) patch. In my tests, now hme(4) outperforms
i82558 based fxp on Ultra AXe.
Details on changes:
. move statistics code to mii_tick to reduce overhead in interrupt
handler. It's not uncommon hme(4)'s interrupt handler is invoked
at the rate of 2000+/sec and it's waste of time to update these
statistics counters in the interrupt handler. It also simplifies
adding DEVICE_POLLING code and reduces polling overheads.(Obtained
from OpenBSD.)
. Switch to bus_dmamap_load_mbuf_sg() for loading TX buffers. This
saves one callback call and removes additional traverse of mbuf
chains.
. use imax() instead of max() since max() looks forward to seeing
unsigned integer.
. make sure to stop if_timer and disable interrupts in hme_stop.
. free mbuf only after invoking bus_dmamap_sync.
. simplify if_timer setup
. don't leak mbuf when loading TX buffers were failed.
. make sure to invoke hme_init_locked() to unfreeze DMA enine when
there are fatal errors.
The primary intent of the patch is for detecting DMA freeze under
high network loads I seen on AXe.(I never seen the freeze on Ultra2).
It seems that it is very difficult for developers to reproduce the
DMA freeze. I think it's more likely to happen on optimized driver
since the driver is more faster than ever before and usually I saw
the panic at 7.5 - 8 MB/s TX speeds.
If there is no objections I'll committ it next week.
Thanks.
--
Regards,
Pyun YongHyeon
http://www.kr.freebsd.org/~yongari | yongari at freebsd.org
-------------- next part --------------
--- sys/dev/hme/if_hme.c.orig Mon Jan 17 19:51:39 2005
+++ sys/dev/hme/if_hme.c Mon Feb 21 18:59:25 2005
@@ -126,8 +126,6 @@
static void hme_rxcksum(struct mbuf *, u_int32_t);
static void hme_cdma_callback(void *, bus_dma_segment_t *, int, int);
-static void hme_txdma_callback(void *, bus_dma_segment_t *, int,
- bus_size_t, int);
devclass_t hme_devclass;
@@ -223,14 +221,14 @@
if (error)
goto fail_ptag;
- error = bus_dma_tag_create(sc->sc_pdmatag, max(0x10, sc->sc_burst), 0,
+ error = bus_dma_tag_create(sc->sc_pdmatag, imax(0x10, sc->sc_burst), 0,
BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES,
HME_NRXDESC, BUS_SPACE_MAXSIZE_32BIT, BUS_DMA_ALLOCNOW,
NULL, NULL, &sc->sc_rdmatag);
if (error)
goto fail_ctag;
- error = bus_dma_tag_create(sc->sc_pdmatag, max(0x10, sc->sc_burst), 0,
+ error = bus_dma_tag_create(sc->sc_pdmatag, imax(0x10, sc->sc_burst), 0,
BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES,
HME_NTXDESC, BUS_SPACE_MAXSIZE_32BIT, BUS_DMA_ALLOCNOW,
NULL, NULL, &sc->sc_tdmatag);
@@ -439,8 +437,31 @@
hme_tick(void *arg)
{
struct hme_softc *sc = arg;
+ struct ifnet *ifp;
int s;
+
+ HME_LOCK(sc);
+ ifp = &sc->sc_arpcom.ac_if;
+
+ /*
+ * Unload collision counters
+ */
+ ifp->if_collisions +=
+ HME_MAC_READ_4(sc, HME_MACI_NCCNT) +
+ HME_MAC_READ_4(sc, HME_MACI_FCCNT) +
+ HME_MAC_READ_4(sc, HME_MACI_EXCNT) +
+ HME_MAC_READ_4(sc, HME_MACI_LTCNT);
+
+ /*
+ * then clear the hardware counters.
+ */
+ HME_MAC_WRITE_4(sc, HME_MACI_NCCNT, 0);
+ HME_MAC_WRITE_4(sc, HME_MACI_FCCNT, 0);
+ HME_MAC_WRITE_4(sc, HME_MACI_EXCNT, 0);
+ HME_MAC_WRITE_4(sc, HME_MACI_LTCNT, 0);
+ HME_UNLOCK(sc);
+
s = splnet();
mii_tick(sc->sc_mii);
splx(s);
@@ -463,11 +484,17 @@
static void
hme_stop(struct hme_softc *sc)
{
+ struct ifnet *ifp;
u_int32_t v;
int n;
+ ifp = &sc->sc_arpcom.ac_if;
+ ifp->if_timer = 0;
callout_stop(&sc->sc_tick_ch);
+ /* mask all interrupts */
+ HME_SEB_WRITE_4(sc, HME_SEBI_IMASK, 0xffffffff);
+
/* Reset transmitter and receiver */
HME_SEB_WRITE_4(sc, HME_SEBI_RESET, HME_SEB_RESET_ETX |
HME_SEB_RESET_ERX);
@@ -527,7 +554,7 @@
* the mapping must be done in a way that a burst can start on a
* natural boundary we might need to extend this.
*/
- a = max(HME_MINRXALIGN, sc->sc_burst);
+ a = imax(HME_MINRXALIGN, sc->sc_burst);
/*
* Make sure the buffer suitably aligned. The 2 byte offset is removed
* when the mbuf is handed up. XXX: this ensures at least 16 byte
@@ -607,10 +634,10 @@
for (i = 0; i < HME_NTXQ; i++) {
td = &sc->sc_rb.rb_txdesc[i];
if (td->htx_m != NULL) {
- m_freem(td->htx_m);
bus_dmamap_sync(sc->sc_tdmatag, td->htx_dmamap,
BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->sc_tdmatag, td->htx_dmamap);
+ m_freem(td->htx_m);
td->htx_m = NULL;
}
STAILQ_INSERT_TAIL(&sc->sc_rb.rb_txfreeq, td, htx_q);
@@ -879,69 +906,6 @@
hme_start_locked(ifp);
}
-struct hme_txdma_arg {
- struct hme_softc *hta_sc;
- struct hme_txdesc *hta_htx;
- int hta_ndescs;
-};
-
-/*
- * XXX: this relies on the fact that segments returned by bus_dmamap_load_mbuf()
- * are readable from the nearest burst boundary on (i.e. potentially before
- * ds_addr) to the first boundary beyond the end. This is usually a safe
- * assumption to make, but is not documented.
- */
-static void
-hme_txdma_callback(void *xsc, bus_dma_segment_t *segs, int nsegs,
- bus_size_t totsz, int error)
-{
- struct hme_txdma_arg *ta = xsc;
- struct hme_txdesc *htx;
- bus_size_t len = 0;
- caddr_t txd;
- u_int32_t flags = 0;
- int i, tdhead, pci;
-
- if (error != 0)
- return;
-
- tdhead = ta->hta_sc->sc_rb.rb_tdhead;
- pci = ta->hta_sc->sc_pci;
- txd = ta->hta_sc->sc_rb.rb_txd;
- htx = ta->hta_htx;
-
- if (ta->hta_sc->sc_rb.rb_td_nbusy + nsegs >= HME_NTXDESC) {
- ta->hta_ndescs = -1;
- return;
- }
- ta->hta_ndescs = nsegs;
-
- for (i = 0; i < nsegs; i++) {
- if (segs[i].ds_len == 0)
- continue;
-
- /* Fill the ring entry. */
- flags = HME_XD_ENCODE_TSIZE(segs[i].ds_len);
- if (len == 0)
- flags |= HME_XD_SOP;
- if (len + segs[i].ds_len == totsz)
- flags |= HME_XD_EOP;
- CTR5(KTR_HME, "hme_txdma_callback: seg %d/%d, ri %d, "
- "flags %#x, addr %#x", i + 1, nsegs, tdhead, (u_int)flags,
- (u_int)segs[i].ds_addr);
- HME_XD_SETFLAGS(pci, txd, tdhead, flags);
- HME_XD_SETADDR(pci, txd, tdhead, segs[i].ds_addr);
-
- ta->hta_sc->sc_rb.rb_td_nbusy++;
- htx->htx_lastdesc = tdhead;
- tdhead = (tdhead + 1) % HME_NTXDESC;
- len += segs[i].ds_len;
- }
- ta->hta_sc->sc_rb.rb_tdhead = tdhead;
- KASSERT((flags & HME_XD_EOP) != 0,
- ("hme_txdma_callback: missed end of packet!"));
-}
-
/* TX TCP/UDP checksum */
static void
hme_txcksum(struct mbuf *m, u_int32_t *cflags)
@@ -988,50 +952,65 @@
static int
hme_load_txmbuf(struct hme_softc *sc, struct mbuf *m0)
{
- struct hme_txdma_arg cba;
- struct hme_txdesc *td;
- int error, si, ri;
+ struct hme_txdesc *htx;
+ caddr_t txd;
+ int error;
+ int i, pci, si, ri, nseg;
u_int32_t flags, cflags = 0;
- si = sc->sc_rb.rb_tdhead;
- if ((td = STAILQ_FIRST(&sc->sc_rb.rb_txfreeq)) == NULL)
- return (-1);
+ if ((htx = STAILQ_FIRST(&sc->sc_rb.rb_txfreeq)) == NULL) {
+ error = -1;
+ goto fail;
+ }
if ((m0->m_pkthdr.csum_flags & sc->sc_csum_features) != 0)
hme_txcksum(m0, &cflags);
- td->htx_m = m0;
- cba.hta_sc = sc;
- cba.hta_htx = td;
- if ((error = bus_dmamap_load_mbuf(sc->sc_tdmatag, td->htx_dmamap,
- m0, hme_txdma_callback, &cba, 0)) != 0)
+ error = bus_dmamap_load_mbuf_sg(sc->sc_tdmatag, htx->htx_dmamap,
+ m0, sc->sc_rb.rb_txsegs, &nseg, BUS_DMA_NOWAIT);
+ if (error != 0)
goto fail;
- if (cba.hta_ndescs == -1) {
- error = -1;
+ if (sc->sc_rb.rb_td_nbusy + nseg >= HME_NTXDESC) {
+ bus_dmamap_unload(sc->sc_tdmatag, htx->htx_dmamap);
+ error = -2;
goto fail;
}
- bus_dmamap_sync(sc->sc_tdmatag, td->htx_dmamap,
- BUS_DMASYNC_PREWRITE);
+ bus_dmamap_sync(sc->sc_tdmatag, htx->htx_dmamap, BUS_DMASYNC_PREWRITE);
- STAILQ_REMOVE_HEAD(&sc->sc_rb.rb_txfreeq, htx_q);
- STAILQ_INSERT_TAIL(&sc->sc_rb.rb_txbusyq, td, htx_q);
-
- /* Turn descriptor ownership to the hme, back to forth. */
- ri = sc->sc_rb.rb_tdhead;
- CTR2(KTR_HME, "hme_load_mbuf: next desc is %d (%#x)",
- ri, HME_XD_GETFLAGS(sc->sc_pci, sc->sc_rb.rb_txd, ri));
- do {
- ri = (ri + HME_NTXDESC - 1) % HME_NTXDESC;
- flags = HME_XD_GETFLAGS(sc->sc_pci, sc->sc_rb.rb_txd, ri) |
- HME_XD_OWN | cflags;
- CTR3(KTR_HME, "hme_load_mbuf: activating ri %d, si %d (%#x)",
- ri, si, flags);
- HME_XD_SETFLAGS(sc->sc_pci, sc->sc_rb.rb_txd, ri, flags);
- } while (ri != si);
+ si = ri = sc->sc_rb.rb_tdhead;
+ txd = sc->sc_rb.rb_txd;
+ pci = sc->sc_pci;
+ for (i = 0; i < nseg; i++) {
+ /* Fill the ring entry. */
+ flags = HME_XD_ENCODE_TSIZE(sc->sc_rb.rb_txsegs[i].ds_len);
+ if (i == 0)
+ flags |= HME_XD_SOP | cflags;
+ else
+ flags |= HME_XD_OWN | cflags;
+ HME_XD_SETADDR(pci, txd, ri, sc->sc_rb.rb_txsegs[i].ds_addr);
+ HME_XD_SETFLAGS(pci, txd, ri, flags);
+ sc->sc_rb.rb_td_nbusy++;
+ htx->htx_lastdesc = ri;
+ ri = (ri + 1) % HME_NTXDESC;
+ }
+ sc->sc_rb.rb_tdhead = ri;
+
+ /* set EOP on the last descriptor */
+ ri = (ri + HME_NTXDESC - 1) % HME_NTXDESC;
+ flags = HME_XD_GETFLAGS(pci, txd, ri);
+ flags |= HME_XD_EOP;
+ HME_XD_SETFLAGS(pci, txd, ri, flags);
+
+ /* Turn the first descriptor ownership to the hme */
+ flags = HME_XD_GETFLAGS(sc->sc_pci, txd, si);
+ flags |= HME_XD_OWN;
+ HME_XD_SETFLAGS(pci, txd, si, flags);
+ STAILQ_REMOVE_HEAD(&sc->sc_rb.rb_txfreeq, htx_q);
+ STAILQ_INSERT_TAIL(&sc->sc_rb.rb_txbusyq, htx, htx_q);
+ htx->htx_m = m0;
/* start the transmission. */
HME_ETX_WRITE_4(sc, HME_ETXI_PENDING, HME_ETX_TP_DMAWAKEUP);
return (0);
fail:
- bus_dmamap_unload(sc->sc_tdmatag, td->htx_dmamap);
return (error);
}
@@ -1103,30 +1082,23 @@
if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
return;
- error = 0;
for (;;) {
IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
if (m == NULL)
break;
error = hme_load_txmbuf(sc, m);
- if (error == -1) {
+ if (error != 0) {
ifp->if_flags |= IFF_OACTIVE;
IFQ_DRV_PREPEND(&ifp->if_snd, m);
break;
- } else if (error > 0) {
- printf("hme_start: error %d while loading mbuf\n",
- error);
- } else {
- enq = 1;
- BPF_MTAP(ifp, m);
}
+ enq++;
+ BPF_MTAP(ifp, m);
}
- if (sc->sc_rb.rb_td_nbusy == HME_NTXDESC || error == -1)
- ifp->if_flags |= IFF_OACTIVE;
/* Set watchdog timer if a packet was queued */
- if (enq) {
+ if (enq > 0) {
bus_dmamap_sync(sc->sc_cdmatag, sc->sc_cdmamap,
BUS_DMASYNC_PREWRITE);
ifp->if_timer = 5;
@@ -1143,23 +1115,6 @@
struct hme_txdesc *htx;
unsigned int ri, txflags;
- /*
- * Unload collision counters
- */
- ifp->if_collisions +=
- HME_MAC_READ_4(sc, HME_MACI_NCCNT) +
- HME_MAC_READ_4(sc, HME_MACI_FCCNT) +
- HME_MAC_READ_4(sc, HME_MACI_EXCNT) +
- HME_MAC_READ_4(sc, HME_MACI_LTCNT);
-
- /*
- * then clear the hardware counters.
- */
- HME_MAC_WRITE_4(sc, HME_MACI_NCCNT, 0);
- HME_MAC_WRITE_4(sc, HME_MACI_FCCNT, 0);
- HME_MAC_WRITE_4(sc, HME_MACI_EXCNT, 0);
- HME_MAC_WRITE_4(sc, HME_MACI_LTCNT, 0);
-
htx = STAILQ_FIRST(&sc->sc_rb.rb_txbusyq);
bus_dmamap_sync(sc->sc_cdmatag, sc->sc_cdmamap, BUS_DMASYNC_POSTREAD);
/* Fetch current position in the transmit ring */
@@ -1197,17 +1152,13 @@
STAILQ_INSERT_TAIL(&sc->sc_rb.rb_txfreeq, htx, htx_q);
htx = STAILQ_FIRST(&sc->sc_rb.rb_txbusyq);
}
- /* Turn off watchdog */
- if (sc->sc_rb.rb_td_nbusy == 0)
- ifp->if_timer = 0;
+ /* Turn off watchdog if hme(4) transmitted queued packet */
+ ifp->if_timer = sc->sc_rb.rb_td_nbusy > 0 ? 5 : 0;
/* Update ring */
sc->sc_rb.rb_tdtail = ri;
hme_start_locked(ifp);
-
- if (sc->sc_rb.rb_td_nbusy == 0)
- ifp->if_timer = 0;
}
/*
@@ -1325,6 +1276,12 @@
}
HME_WHINE(sc->sc_dev, "error signaled, status=%#x\n", status);
+ /* check for fatal errors that needs reset to unfreeze DMA engine */
+ if ((status & (HME_SEB_STAT_SLVERR | HME_SEB_STAT_SLVERR |
+ HME_SEB_STAT_TXTERR | HME_SEB_STAT_TXPERR | HME_SEB_STAT_TXLERR |
+ HME_SEB_STAT_TXEACK | HME_SEB_STAT_RXTERR | HME_SEB_STAT_RXPERR |
+ HME_SEB_STAT_RXLATERR | HME_SEB_STAT_RXERR)) != 0)
+ hme_init_locked(sc);
}
void
@@ -1347,7 +1304,6 @@
hme_rint(sc);
HME_UNLOCK(sc);
}
-
static void
hme_watchdog(struct ifnet *ifp)
--- sys/dev/hme/if_hmevar.h.orig Thu Dec 16 18:57:37 2004
+++ sys/dev/hme/if_hmevar.h Mon Feb 21 18:05:30 2005
@@ -100,6 +100,7 @@
/* Descriptors */
struct hme_rxdesc rb_rxdesc[HME_NRXDESC];
struct hme_txdesc rb_txdesc[HME_NTXQ];
+ bus_dma_segment_t rb_txsegs[HME_NTXDESC];
struct hme_txdq rb_txfreeq;
struct hme_txdq rb_txbusyq;
More information about the freebsd-sparc64
mailing list