From 05ce870289a3cbf798f9210d767787e12c38547b Mon Sep 17 00:00:00 2001 From: adrian Date: Sun, 10 May 2020 03:36:11 +0000 Subject: [PATCH] [atheros] [if_arge] Various fixes to avoid TX stalls and bad sized packets This is stuff I've been running for a couple years. It's inspired by changes I found in the linux ag71xx ethernet driver. * Delay between stopping DMA and checking to see if it's stopped; this gives the hardware time to do its thing. * Non-final frames in the chain need to be a multiple of 4 bytes in size. Ensure this is the case when assembling a TX DMA list. * Add counters for tx/rx underflow and too-short packets. * Log if TX/RX DMA couldn't be stopped when resetting the MAC. * Add some more debugging / logging around TX/RX ring bits. Tested: * AR7240, AR7241 * AR9344 (TL-WDR3600/TL-WDR4300 APs) * AR9331 (Carambola 2) --- sys/mips/atheros/if_arge.c | 68 +++++++++++++++++++++++++++++++++-- sys/mips/atheros/if_argevar.h | 1 + 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/sys/mips/atheros/if_arge.c b/sys/mips/atheros/if_arge.c index 842ced2fb66..771dc3f7d7c 100644 --- a/sys/mips/atheros/if_arge.c +++ b/sys/mips/atheros/if_arge.c @@ -332,6 +332,11 @@ arge_attach_sysctl(device_t dev) "tx_pkts_unaligned_len", CTLFLAG_RW, &sc->stats.tx_pkts_unaligned_len, 0, "number of TX unaligned packets (len)"); + SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "tx_pkts_unaligned_tooshort", CTLFLAG_RW, + &sc->stats.tx_pkts_unaligned_tooshort, + 0, "number of TX unaligned packets (mbuf length < 4 bytes)"); + SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_pkts_nosegs", CTLFLAG_RW, &sc->stats.tx_pkts_nosegs, 0, "number of TX packets fail with no ring slots avail"); @@ -347,6 +352,13 @@ arge_attach_sysctl(device_t dev) SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "intr_ok", CTLFLAG_RW, &sc->stats.intr_ok, 0, "number of OK interrupts"); + + SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "tx_underflow", CTLFLAG_RW, &sc->stats.tx_underflow, + 0, "Number of TX underflows"); + SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "rx_overflow", CTLFLAG_RW, &sc->stats.rx_overflow, + 0, "Number of RX overflows"); #ifdef ARGE_DEBUG SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_prod", CTLFLAG_RW, &sc->arge_cdata.arge_tx_prod, 0, ""); @@ -1365,15 +1377,24 @@ arge_set_pll(struct arge_softc *sc, int media, int duplex) static void arge_reset_dma(struct arge_softc *sc) { + uint32_t val; ARGEDEBUG(sc, ARGE_DBG_RESET, "%s: called\n", __func__); ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, 0); ARGE_WRITE(sc, AR71XX_DMA_TX_CONTROL, 0); + /* Give hardware a chance to finish */ + DELAY(1000); + ARGE_WRITE(sc, AR71XX_DMA_RX_DESC, 0); ARGE_WRITE(sc, AR71XX_DMA_TX_DESC, 0); + ARGEDEBUG(sc, ARGE_DBG_RESET, "%s: RX_STATUS=%08x, TX_STATUS=%08x\n", + __func__, + ARGE_READ(sc, AR71XX_DMA_RX_STATUS), + ARGE_READ(sc, AR71XX_DMA_TX_STATUS)); + /* Clear all possible RX interrupts */ while(ARGE_READ(sc, AR71XX_DMA_RX_STATUS) & DMA_RX_STATUS_PKT_RECVD) ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_PKT_RECVD); @@ -1397,6 +1418,24 @@ arge_reset_dma(struct arge_softc *sc) * flushed to RAM before underlying buffers are freed. */ arge_flush_ddr(sc); + + /* Check if we cleared RX status */ + val = ARGE_READ(sc, AR71XX_DMA_RX_STATUS); + if (val != 0) { + device_printf(sc->arge_dev, + "%s: unable to clear DMA_RX_STATUS: %08x\n", + __func__, val); + } + + /* Check if we cleared TX status */ + val = ARGE_READ(sc, AR71XX_DMA_TX_STATUS); + /* Mask out reserved bits */ + val = val & 0x00ffffff; + if (val != 0) { + device_printf(sc->arge_dev, + "%s: unable to clear DMA_TX_STATUS: %08x\n", + __func__, val); + } } static void @@ -1417,9 +1456,13 @@ arge_init_locked(struct arge_softc *sc) ARGE_LOCK_ASSERT(sc); + ARGEDEBUG(sc, ARGE_DBG_RESET, "%s: called\n", __func__); + if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) return; + ARGEDEBUG(sc, ARGE_DBG_RESET, "%s: init'ing\n", __func__); + /* Init circular RX list. */ if (arge_rx_ring_init(sc) != 0) { device_printf(sc->arge_dev, @@ -1431,6 +1474,7 @@ arge_init_locked(struct arge_softc *sc) /* Init tx descriptors. */ arge_tx_ring_init(sc); + /* Restart DMA */ arge_reset_dma(sc); if (sc->arge_miibus) { @@ -1452,6 +1496,11 @@ arge_init_locked(struct arge_softc *sc) arge_update_link_locked(sc); } + ARGEDEBUG(sc, ARGE_DBG_RESET, "%s: desc ring; TX=0x%x, RX=0x%x\n", + __func__, + ARGE_TX_RING_ADDR(sc, 0), + ARGE_RX_RING_ADDR(sc, 0)); + ARGE_WRITE(sc, AR71XX_DMA_TX_DESC, ARGE_TX_RING_ADDR(sc, 0)); ARGE_WRITE(sc, AR71XX_DMA_RX_DESC, ARGE_RX_RING_ADDR(sc, 0)); @@ -1495,6 +1544,15 @@ arge_mbuf_chain_is_tx_aligned(struct arge_softc *sc, struct mbuf *m0) sc->stats.tx_pkts_unaligned_len++; return 0; } + + /* + * All chips have this requirement for length being greater + * than 4. + */ + if ((m->m_next != NULL) && ((m->m_len < 4))) { + sc->stats.tx_pkts_unaligned_tooshort++; + return 0; + } } return 1; } @@ -1582,6 +1640,11 @@ arge_encap(struct arge_softc *sc, struct mbuf **m_head) tmp |= ARGE_DESC_EMPTY; desc->packet_ctrl = tmp; + ARGEDEBUG(sc, ARGE_DBG_TX, " [%d / %d] addr=0x%x, len=%d\n", + i, + prod, + (uint32_t) txsegs[i].ds_addr, (int) txsegs[i].ds_len); + /* XXX Note: only relevant for older MACs; but check length! */ if ((sc->arge_hw_flags & ARGE_HW_FLG_TX_DESC_ALIGN_4BYTE) && (txsegs[i].ds_addr & 3)) @@ -2606,7 +2669,7 @@ arge_intr(void *arg) } /* - * If we've finished TXing and there's space for more packets + * If we've finished RX /or/ TX and there's space for more packets * to be queued for TX, do so. Otherwise we may end up in a * situation where the interface send queue was filled * whilst the hardware queue was full, then the hardware @@ -2620,8 +2683,7 @@ arge_intr(void *arg) * after a TX underrun, then having the hardware queue added * to below. */ - if (status & (DMA_INTR_TX_PKT_SENT | DMA_INTR_TX_UNDERRUN) && - (ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { + if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { if (!IFQ_IS_EMPTY(&ifp->if_snd)) arge_start_locked(ifp); } diff --git a/sys/mips/atheros/if_argevar.h b/sys/mips/atheros/if_argevar.h index 5e284cc2b81..576fe501a8d 100644 --- a/sys/mips/atheros/if_argevar.h +++ b/sys/mips/atheros/if_argevar.h @@ -206,6 +206,7 @@ struct arge_softc { uint32_t tx_pkts_unaligned; uint32_t tx_pkts_unaligned_start; uint32_t tx_pkts_unaligned_len; + uint32_t tx_pkts_unaligned_tooshort; uint32_t tx_pkts_nosegs; uint32_t tx_pkts_aligned; uint32_t rx_overflow; -- 2.45.0