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:
parent
c669105d17
commit
67aaf73997
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user