diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c index 7601f2f12c2c..2fe8de3d3f2d 100644 --- a/sys/dev/ath/if_ath.c +++ b/sys/dev/ath/if_ath.c @@ -836,6 +836,11 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) "Enabling register serialisation\n"); } + /* + * Initialise the deferred completed RX buffer list. + */ + TAILQ_INIT(&sc->sc_rx_rxlist); + /* * Indicate we need the 802.11 header padded to a * 32-bit boundary for 4-address and QoS frames. @@ -1711,7 +1716,7 @@ ath_intr(void *arg) * traffic so any frames held on the staging * queue are aged and potentially flushed. */ - taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); + sc->sc_rx.recv_sched(sc, 1); #endif } } @@ -1751,13 +1756,13 @@ ath_intr(void *arg) if (! sc->sc_kickpcu) sc->sc_rxlink = NULL; sc->sc_kickpcu = 1; + ATH_PCU_UNLOCK(sc); /* * Enqueue an RX proc, to handled whatever * is in the RX queue. * This will then kick the PCU. */ - taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); - ATH_PCU_UNLOCK(sc); + sc->sc_rx.recv_sched(sc, 1); } if (status & HAL_INT_TXURN) { sc->sc_stats.ast_txurn++; @@ -1770,7 +1775,7 @@ ath_intr(void *arg) */ if (status & (HAL_INT_RX | HAL_INT_RXHP | HAL_INT_RXLP)) { sc->sc_stats.ast_rx_intr++; - taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); + sc->sc_rx.recv_sched(sc, 1); } if (status & HAL_INT_TX) { sc->sc_stats.ast_tx_intr++; diff --git a/sys/dev/ath/if_ath_rx.c b/sys/dev/ath/if_ath_rx.c index 3c40f17bc39d..fb0cd8db6e58 100644 --- a/sys/dev/ath/if_ath_rx.c +++ b/sys/dev/ath/if_ath_rx.c @@ -1030,7 +1030,7 @@ rx_proc_next: * will reduce latency. */ if (npkts >= ATH_RX_MAX) - taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); + sc->sc_rx.recv_sched(sc, resched); ATH_PCU_LOCK(sc); sc->sc_rxproc_cnt--; @@ -1181,6 +1181,21 @@ ath_legacy_dma_rxteardown(struct ath_softc *sc) return (0); } +static void +ath_legacy_recv_sched(struct ath_softc *sc, int dosched) +{ + + taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); +} + +static void +ath_legacy_recv_sched_queue(struct ath_softc *sc, HAL_RX_QUEUE q, + int dosched) +{ + + taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); +} + void ath_recv_setup_legacy(struct ath_softc *sc) { @@ -1200,4 +1215,6 @@ ath_recv_setup_legacy(struct ath_softc *sc) sc->sc_rx.recv_setup = ath_legacy_dma_rxsetup; sc->sc_rx.recv_teardown = ath_legacy_dma_rxteardown; + sc->sc_rx.recv_sched = ath_legacy_recv_sched; + sc->sc_rx.recv_sched_queue = ath_legacy_recv_sched_queue; } diff --git a/sys/dev/ath/if_ath_rx_edma.c b/sys/dev/ath/if_ath_rx_edma.c index 3df234184b79..ec7fa770b614 100644 --- a/sys/dev/ath/if_ath_rx_edma.c +++ b/sys/dev/ath/if_ath_rx_edma.c @@ -132,12 +132,8 @@ MALLOC_DECLARE(M_ATHDEV); /* * XXX TODO: * - * + Add an RX lock, just to ensure we don't have things clash; * + Make sure the FIFO is correctly flushed and reinitialised * through a reset; - * + Handle the "kickpcu" state where the FIFO overflows. - * + Implement a "flush" routine, which doesn't push any - * new frames into the FIFO. * + Verify multi-descriptor frames work! * + There's a "memory use after free" which needs to be tracked down * and fixed ASAP. I've seen this in the legacy path too, so it @@ -152,7 +148,9 @@ static int ath_edma_rxfifo_alloc(struct ath_softc *sc, HAL_RX_QUEUE qtype, int nbufs); static int ath_edma_rxfifo_flush(struct ath_softc *sc, HAL_RX_QUEUE qtype); static void ath_edma_rxbuf_free(struct ath_softc *sc, struct ath_buf *bf); -static int ath_edma_recv_proc_queue(struct ath_softc *sc, +static void ath_edma_recv_proc_queue(struct ath_softc *sc, + HAL_RX_QUEUE qtype, int dosched); +static int ath_edma_recv_proc_deferred_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, int dosched); static void @@ -282,6 +280,24 @@ ath_edma_startrecv(struct ath_softc *sc) return (0); } +static void +ath_edma_recv_sched_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, + int dosched) +{ + + ath_edma_recv_proc_queue(sc, qtype, dosched); + taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); +} + +static void +ath_edma_recv_sched(struct ath_softc *sc, int dosched) +{ + + ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_HP, dosched); + ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_LP, dosched); + taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); +} + static void ath_edma_recv_flush(struct ath_softc *sc) { @@ -292,27 +308,27 @@ ath_edma_recv_flush(struct ath_softc *sc) sc->sc_rxproc_cnt++; ATH_PCU_UNLOCK(sc); + /* + * Flush any active frames from FIFO -> deferred list + */ ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_HP, 0); ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_LP, 0); + /* + * Process what's in the deferred queue + */ + ath_edma_recv_proc_deferred_queue(sc, HAL_RX_QUEUE_HP, 0); + ath_edma_recv_proc_deferred_queue(sc, HAL_RX_QUEUE_LP, 0); + ATH_PCU_LOCK(sc); sc->sc_rxproc_cnt--; ATH_PCU_UNLOCK(sc); } /* - * Process frames from the current queue. - * - * TODO: - * - * + Add a "dosched" flag, so we don't reschedule any FIFO frames - * to the hardware or re-kick the PCU after 'kickpcu' is set. - * - * + Perhaps split "check FIFO contents" and "handle frames", so - * we can run the "check FIFO contents" in ath_intr(), but - * "handle frames" in the RX tasklet. + * Process frames from the current queue into the deferred queue. */ -static int +static void ath_edma_recv_proc_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, int dosched) { @@ -323,12 +339,8 @@ ath_edma_recv_proc_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, struct mbuf *m; struct ath_hal *ah = sc->sc_ah; uint64_t tsf; - int16_t nf; - int ngood = 0, npkts = 0; - ath_bufhead rxlist; - struct ath_buf *next; - - TAILQ_INIT(&rxlist); + uint16_t nf; + int npkts = 0; tsf = ath_hal_gettsf64(ah); nf = ath_hal_getchannoise(ah, sc->sc_curchan); @@ -386,7 +398,7 @@ ath_edma_recv_proc_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, * queue. */ re->m_fifo[re->m_fifo_head] = NULL; - TAILQ_INSERT_TAIL(&rxlist, bf, bf_list); + TAILQ_INSERT_TAIL(&sc->sc_rx_rxlist, bf, bf_list); /* Bump the descriptor FIFO stats */ INCR(re->m_fifo_head, re->m_fifolen); @@ -400,6 +412,78 @@ ath_edma_recv_proc_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, ATH_RX_UNLOCK(sc); + /* rx signal state monitoring */ + ath_hal_rxmonitor(ah, &sc->sc_halstats, sc->sc_curchan); + + ATH_KTR(sc, ATH_KTR_INTERRUPTS, 1, + "ath edma rx proc: npkts=%d\n", + npkts); + + /* Handle resched and kickpcu appropriately */ + ATH_PCU_LOCK(sc); + if (dosched && sc->sc_kickpcu) { + ATH_KTR(sc, ATH_KTR_ERROR, 0, + "ath_edma_recv_proc_queue(): kickpcu"); + device_printf(sc->sc_dev, + "%s: handled npkts %d\n", + __func__, npkts); + + /* + * XXX TODO: what should occur here? Just re-poke and + * re-enable the RX FIFO? + */ + sc->sc_kickpcu = 0; + } + ATH_PCU_UNLOCK(sc); + + return; +} + +/* + * Flush the deferred queue. + * + * This destructively flushes the deferred queue - it doesn't + * call the wireless stack on each mbuf. + */ +static void +ath_edma_flush_deferred_queue(struct ath_softc *sc) +{ + struct ath_buf *bf, *next; + + ATH_RX_LOCK_ASSERT(sc); + /* Free in one set, inside the lock */ + TAILQ_FOREACH_SAFE(bf, &sc->sc_rx_rxlist, bf_list, next) { + /* Free the buffer/mbuf */ + ath_edma_rxbuf_free(sc, bf); + } +} + +static int +ath_edma_recv_proc_deferred_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, + int dosched) +{ + int ngood = 0; + uint64_t tsf; + struct ath_buf *bf, *next; + struct ath_rx_status *rs; + int16_t nf; + ath_bufhead rxlist; + + TAILQ_INIT(&rxlist); + + nf = ath_hal_getchannoise(sc->sc_ah, sc->sc_curchan); + /* + * XXX TODO: the NF/TSF should be stamped on the bufs themselves, + * otherwise we may end up adding in the wrong values if this + * is delayed too far.. + */ + tsf = ath_hal_gettsf64(sc->sc_ah); + + /* Copy the list over */ + ATH_RX_LOCK(sc); + TAILQ_CONCAT(&rxlist, &sc->sc_rx_rxlist, bf_list); + ATH_RX_UNLOCK(sc); + /* Handle the completed descriptors */ TAILQ_FOREACH_SAFE(bf, &rxlist, bf_list, next) { /* @@ -417,6 +501,14 @@ ath_edma_recv_proc_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, ngood++; } + if (ngood) { + sc->sc_lastrx = tsf; + } + + ATH_KTR(sc, ATH_KTR_INTERRUPTS, 1, + "ath edma rx deferred proc: ngood=%d\n", + ngood); + /* Free in one set, inside the lock */ ATH_RX_LOCK(sc); TAILQ_FOREACH_SAFE(bf, &rxlist, bf_list, next) { @@ -425,32 +517,6 @@ ath_edma_recv_proc_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, } ATH_RX_UNLOCK(sc); - /* rx signal state monitoring */ - ath_hal_rxmonitor(ah, &sc->sc_halstats, sc->sc_curchan); - if (ngood) - sc->sc_lastrx = tsf; - - ATH_KTR(sc, ATH_KTR_INTERRUPTS, 2, - "ath edma rx proc: npkts=%d, ngood=%d", - npkts, ngood); - - /* Handle resched and kickpcu appropriately */ - ATH_PCU_LOCK(sc); - if (dosched && sc->sc_kickpcu) { - ATH_KTR(sc, ATH_KTR_ERROR, 0, - "ath_edma_recv_proc_queue(): kickpcu"); - device_printf(sc->sc_dev, - "%s: handled npkts %d ngood %d\n", - __func__, npkts, ngood); - - /* - * XXX TODO: what should occur here? Just re-poke and - * re-enable the RX FIFO? - */ - sc->sc_kickpcu = 0; - } - ATH_PCU_UNLOCK(sc); - return (ngood); } @@ -480,6 +546,9 @@ ath_edma_recv_tasklet(void *arg, int npending) ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_HP, 1); ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_LP, 1); + ath_edma_recv_proc_deferred_queue(sc, HAL_RX_QUEUE_HP, 1); + ath_edma_recv_proc_deferred_queue(sc, HAL_RX_QUEUE_LP, 1); + /* XXX inside IF_LOCK ? */ if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { #ifdef IEEE80211_SUPPORT_SUPERG @@ -812,6 +881,7 @@ ath_edma_dma_rxteardown(struct ath_softc *sc) { ATH_RX_LOCK(sc); + ath_edma_flush_deferred_queue(sc); ath_edma_rxfifo_flush(sc, HAL_RX_QUEUE_HP); ath_edma_rxfifo_free(sc, HAL_RX_QUEUE_HP); @@ -854,4 +924,7 @@ ath_recv_setup_edma(struct ath_softc *sc) sc->sc_rx.recv_setup = ath_edma_dma_rxsetup; sc->sc_rx.recv_teardown = ath_edma_dma_rxteardown; + + sc->sc_rx.recv_sched = ath_edma_recv_sched; + sc->sc_rx.recv_sched_queue = ath_edma_recv_sched_queue; } diff --git a/sys/dev/ath/if_athvar.h b/sys/dev/ath/if_athvar.h index d3e4aa30eee0..bf5a4c8ed10d 100644 --- a/sys/dev/ath/if_athvar.h +++ b/sys/dev/ath/if_athvar.h @@ -442,6 +442,9 @@ typedef enum { } ATH_RESET_TYPE; struct ath_rx_methods { + void (*recv_sched_queue)(struct ath_softc *sc, + HAL_RX_QUEUE q, int dosched); + void (*recv_sched)(struct ath_softc *sc, int dosched); void (*recv_stop)(struct ath_softc *sc, int dodelay); int (*recv_start)(struct ath_softc *sc); void (*recv_flush)(struct ath_softc *sc); @@ -656,6 +659,7 @@ struct ath_softc { struct ath_descdma sc_rxdma; /* RX descriptors */ ath_bufhead sc_rxbuf; /* receive buffer */ + ath_bufhead sc_rx_rxlist; /* deferred RX completion */ u_int32_t *sc_rxlink; /* link ptr in last RX desc */ struct task sc_rxtask; /* rx int processing */ u_int8_t sc_defant; /* current default antenna */