Modify the RX path to keep the previous RX descriptor around once it's

used.

It turns out that the RX DMA engine does the same last-descriptor-link-
pointer-re-reading trick that the TX DMA engine.  That is, the hardware
re-reads the link pointer before it moves onto the next descriptor.
Thus we can't free a descriptor before we move on; it's possible the
hardware will need to re-read the link pointer before we overwrite
it with a new one.

Tested:

* AR5416, STA mode

TODO:

* more thorough AP and STA mode testing!
* test on other pre-AR9380 NICs, just to be sure.
* Break out the RX descriptor grabbing bits from the RX completion
  bits, like what is done in the RX EDMA code, so ..
* .. the RX lock can be held during ath_rx_proc(), but not across
  packet input.
This commit is contained in:
Adrian Chadd 2014-05-06 01:15:42 +00:00
parent c669105d17
commit 67aaf73997
3 changed files with 122 additions and 17 deletions

View File

@ -245,6 +245,8 @@ ath_legacy_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf)
struct mbuf *m; struct mbuf *m;
struct ath_desc *ds; struct ath_desc *ds;
/* XXX TODO: ATH_RX_LOCK_ASSERT(sc); */
m = bf->bf_m; m = bf->bf_m;
if (m == NULL) { if (m == NULL) {
/* /*
@ -974,6 +976,14 @@ ath_rx_pkt(struct ath_softc *sc, struct ath_rx_status *rs, HAL_STATUS status,
#define ATH_RX_MAX 128 #define ATH_RX_MAX 128
/*
* XXX TODO: break out the "get buffers" from "call ath_rx_pkt()" like
* the EDMA code does.
*
* XXX TODO: then, do all of the RX list management stuff inside
* ATH_RX_LOCK() so we don't end up potentially racing. The EDMA
* code is doing it right.
*/
static void static void
ath_rx_proc(struct ath_softc *sc, int resched) ath_rx_proc(struct ath_softc *sc, int resched)
{ {
@ -995,6 +1005,7 @@ ath_rx_proc(struct ath_softc *sc, int resched)
u_int64_t tsf; u_int64_t tsf;
int npkts = 0; int npkts = 0;
int kickpcu = 0; int kickpcu = 0;
int ret;
/* XXX we must not hold the ATH_LOCK here */ /* XXX we must not hold the ATH_LOCK here */
ATH_UNLOCK_ASSERT(sc); ATH_UNLOCK_ASSERT(sc);
@ -1094,8 +1105,26 @@ ath_rx_proc(struct ath_softc *sc, int resched)
if (ath_rx_pkt(sc, rs, status, tsf, nf, HAL_RX_QUEUE_HP, bf, m)) if (ath_rx_pkt(sc, rs, status, tsf, nf, HAL_RX_QUEUE_HP, bf, m))
ngood++; ngood++;
rx_proc_next: rx_proc_next:
TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list); /*
} while (ath_rxbuf_init(sc, bf) == 0); * If there's a holding buffer, insert that onto
* the RX list; the hardware is now definitely not pointing
* to it now.
*/
ret = 0;
if (sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf != NULL) {
TAILQ_INSERT_TAIL(&sc->sc_rxbuf,
sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf,
bf_list);
ret = ath_rxbuf_init(sc,
sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf);
}
/*
* Next, throw our buffer into the holding entry. The hardware
* may use the descriptor to read the link pointer before
* DMAing the next descriptor in to write out a packet.
*/
sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf = bf;
} while (ret == 0);
/* rx signal state monitoring */ /* rx signal state monitoring */
ath_hal_rxmonitor(ah, &sc->sc_halstats, sc->sc_curchan); ath_hal_rxmonitor(ah, &sc->sc_halstats, sc->sc_curchan);
@ -1127,6 +1156,13 @@ ath_rx_proc(struct ath_softc *sc, int resched)
* constantly write over the same frame, leading * constantly write over the same frame, leading
* the RX driver code here to get heavily confused. * the RX driver code here to get heavily confused.
*/ */
/*
* XXX Has RX DMA stopped enough here to just call
* ath_startrecv()?
* XXX Do we need to use the holding buffer to restart
* RX DMA by appending entries to the final
* descriptor? Quite likely.
*/
#if 1 #if 1
ath_startrecv(sc); ath_startrecv(sc);
#else #else
@ -1217,6 +1253,58 @@ ath_legacy_flushrecv(struct ath_softc *sc)
ath_rx_proc(sc, 0); ath_rx_proc(sc, 0);
} }
static void
ath_legacy_flush_rxpending(struct ath_softc *sc)
{
/* XXX ATH_RX_LOCK_ASSERT(sc); */
if (sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending != NULL) {
m_freem(sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending);
sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending = NULL;
}
if (sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending != NULL) {
m_freem(sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending);
sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending = NULL;
}
}
static int
ath_legacy_flush_rxholdbf(struct ath_softc *sc)
{
struct ath_buf *bf;
/* XXX ATH_RX_LOCK_ASSERT(sc); */
/*
* If there are RX holding buffers, free them here and return
* them to the list.
*
* XXX should just verify that bf->bf_m is NULL, as it must
* be at this point!
*/
bf = sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf;
if (bf != NULL) {
if (bf->bf_m != NULL)
m_freem(bf->bf_m);
bf->bf_m = NULL;
TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
(void) ath_rxbuf_init(sc, bf);
}
sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf = NULL;
bf = sc->sc_rxedma[HAL_RX_QUEUE_LP].m_holdbf;
if (bf != NULL) {
if (bf->bf_m != NULL)
m_freem(bf->bf_m);
bf->bf_m = NULL;
TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
(void) ath_rxbuf_init(sc, bf);
}
sc->sc_rxedma[HAL_RX_QUEUE_LP].m_holdbf = NULL;
return (0);
}
/* /*
* Disable the receive h/w in preparation for a reset. * Disable the receive h/w in preparation for a reset.
*/ */
@ -1228,6 +1316,8 @@ ath_legacy_stoprecv(struct ath_softc *sc, int dodelay)
((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
struct ath_hal *ah = sc->sc_ah; struct ath_hal *ah = sc->sc_ah;
ATH_RX_LOCK(sc);
ath_hal_stoppcurecv(ah); /* disable PCU */ ath_hal_stoppcurecv(ah); /* disable PCU */
ath_hal_setrxfilter(ah, 0); /* clear recv filter */ ath_hal_setrxfilter(ah, 0); /* clear recv filter */
ath_hal_stopdmarecv(ah); /* disable DMA engine */ ath_hal_stopdmarecv(ah); /* disable DMA engine */
@ -1261,21 +1351,22 @@ ath_legacy_stoprecv(struct ath_softc *sc, int dodelay)
} }
} }
#endif #endif
/*
* Free both high/low RX pending, just in case. (void) ath_legacy_flush_rxpending(sc);
*/ (void) ath_legacy_flush_rxholdbf(sc);
if (sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending != NULL) {
m_freem(sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending);
sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending = NULL;
}
if (sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending != NULL) {
m_freem(sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending);
sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending = NULL;
}
sc->sc_rxlink = NULL; /* just in case */ sc->sc_rxlink = NULL; /* just in case */
ATH_RX_UNLOCK(sc);
#undef PA2DESC #undef PA2DESC
} }
/*
* XXX TODO: something was calling startrecv without calling
* stoprecv. Let's figure out what/why. It was showing up
* as a mbuf leak (rxpending) and ath_buf leak (holdbf.)
*/
/* /*
* Enable the receive h/w following a reset. * Enable the receive h/w following a reset.
*/ */
@ -1285,9 +1376,18 @@ ath_legacy_startrecv(struct ath_softc *sc)
struct ath_hal *ah = sc->sc_ah; struct ath_hal *ah = sc->sc_ah;
struct ath_buf *bf; struct ath_buf *bf;
ATH_RX_LOCK(sc);
/*
* XXX should verify these are already all NULL!
*/
sc->sc_rxlink = NULL; sc->sc_rxlink = NULL;
sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending = NULL; (void) ath_legacy_flush_rxpending(sc);
sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending = NULL; (void) ath_legacy_flush_rxholdbf(sc);
/*
* Re-chain all of the buffers in the RX buffer list.
*/
TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
int error = ath_rxbuf_init(sc, bf); int error = ath_rxbuf_init(sc, bf);
if (error != 0) { if (error != 0) {
@ -1303,6 +1403,8 @@ ath_legacy_startrecv(struct ath_softc *sc)
ath_hal_rxena(ah); /* enable recv descriptors */ ath_hal_rxena(ah); /* enable recv descriptors */
ath_mode_init(sc); /* set filters, etc. */ ath_mode_init(sc); /* set filters, etc. */
ath_hal_startpcurecv(ah); /* re-enable PCU/DMA engine */ ath_hal_startpcurecv(ah); /* re-enable PCU/DMA engine */
ATH_RX_UNLOCK(sc);
return 0; return 0;
} }

View File

@ -413,12 +413,14 @@ ath_sysctl_txagg(SYSCTL_HANDLER_ARGS)
ATH_RX_LOCK(sc); ATH_RX_LOCK(sc);
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
printf("%d: fifolen: %d/%d; head=%d; tail=%d\n", printf("%d: fifolen: %d/%d; head=%d; tail=%d; m_pending=%p, m_holdbf=%p\n",
i, i,
sc->sc_rxedma[i].m_fifo_depth, sc->sc_rxedma[i].m_fifo_depth,
sc->sc_rxedma[i].m_fifolen, sc->sc_rxedma[i].m_fifolen,
sc->sc_rxedma[i].m_fifo_head, sc->sc_rxedma[i].m_fifo_head,
sc->sc_rxedma[i].m_fifo_tail); sc->sc_rxedma[i].m_fifo_tail,
sc->sc_rxedma[i].m_rxpending,
sc->sc_rxedma[i].m_holdbf);
} }
i = 0; i = 0;
TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {

View File

@ -510,6 +510,7 @@ struct ath_rx_edma {
int m_fifo_tail; int m_fifo_tail;
int m_fifo_depth; int m_fifo_depth;
struct mbuf *m_rxpending; struct mbuf *m_rxpending;
struct ath_buf *m_holdbf;
}; };
struct ath_tx_edma_fifo { struct ath_tx_edma_fifo {