From 39abbd9bd210e7d7a8c16ab7e1e950378f6bc8c3 Mon Sep 17 00:00:00 2001 From: Adrian Chadd Date: Sat, 14 Jul 2012 02:07:51 +0000 Subject: [PATCH] Fix EDMA RX to actually work without panicing the machine. I was setting up the RX EDMA buffer to be 4096 bytes rather than the RX data buffer portion. The hardware was likely getting very confused and DMAing descriptor portions into places it shouldn't, leading to memory corruption and occasional panics. Whilst here, don't bother allocating descriptors for the RX EDMA case. We don't use those descriptors. Instead, just allocate ath_buf entries. --- sys/dev/ath/if_ath.c | 61 ++++++++++++++++++++++++++++++++++++ sys/dev/ath/if_ath_misc.h | 3 ++ sys/dev/ath/if_ath_rx_edma.c | 17 +++++----- 3 files changed, 73 insertions(+), 8 deletions(-) diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c index 965a1fd6c07..650efbe9a5b 100644 --- a/sys/dev/ath/if_ath.c +++ b/sys/dev/ath/if_ath.c @@ -2888,6 +2888,67 @@ fail0: #undef ATH_DESC_4KB_BOUND_CHECK } +/* + * Allocate ath_buf entries but no descriptor contents. + * + * This is for RX EDMA where the descriptors are the header part of + * the RX buffer. + */ +int +ath_descdma_setup_rx_edma(struct ath_softc *sc, + struct ath_descdma *dd, ath_bufhead *head, + const char *name, int nbuf, int rx_status_len) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ath_buf *bf; + int i, bsize, error; + + DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA: %u buffers\n", + __func__, name, nbuf); + + dd->dd_name = name; + /* + * This is (mostly) purely for show. We're not allocating any actual + * descriptors here as EDMA RX has the descriptor be part + * of the RX buffer. + * + * However, dd_desc_len is used by ath_descdma_free() to determine + * whether we have already freed this DMA mapping. + */ + dd->dd_desc_len = rx_status_len; + + /* allocate rx buffers */ + bsize = sizeof(struct ath_buf) * nbuf; + bf = malloc(bsize, M_ATHDEV, M_NOWAIT | M_ZERO); + if (bf == NULL) { + if_printf(ifp, "malloc of %s buffers failed, size %u\n", + dd->dd_name, bsize); + goto fail3; + } + dd->dd_bufptr = bf; + + TAILQ_INIT(head); + for (i = 0; i < nbuf; i++, bf++) { + bf->bf_desc = NULL; + bf->bf_daddr = 0; + bf->bf_lastds = NULL; /* Just an initial value */ + + error = bus_dmamap_create(sc->sc_dmat, BUS_DMA_NOWAIT, + &bf->bf_dmamap); + if (error != 0) { + if_printf(ifp, "unable to create dmamap for %s " + "buffer %u, error %u\n", dd->dd_name, i, error); + ath_descdma_cleanup(sc, dd, head); + return error; + } + TAILQ_INSERT_TAIL(head, bf, bf_list); + } + return 0; +fail3: + memset(dd, 0, sizeof(*dd)); + return error; +} + void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd, ath_bufhead *head) diff --git a/sys/dev/ath/if_ath_misc.h b/sys/dev/ath/if_ath_misc.h index 0a51feeec76..23c9f68fc64 100644 --- a/sys/dev/ath/if_ath_misc.h +++ b/sys/dev/ath/if_ath_misc.h @@ -86,6 +86,9 @@ extern void ath_setslottime(struct ath_softc *sc); extern int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, ath_bufhead *head, const char *name, int nbuf, int ndesc); +extern int ath_descdma_setup_rx_edma(struct ath_softc *sc, + struct ath_descdma *dd, ath_bufhead *head, const char *name, + int nbuf, int desclen); extern void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd, ath_bufhead *head); diff --git a/sys/dev/ath/if_ath_rx_edma.c b/sys/dev/ath/if_ath_rx_edma.c index 9e3580d966c..9ed3a91afa3 100644 --- a/sys/dev/ath/if_ath_rx_edma.c +++ b/sys/dev/ath/if_ath_rx_edma.c @@ -696,11 +696,11 @@ ath_edma_dma_rxsetup(struct ath_softc *sc) { int error; - /* Create RX DMA tag */ - /* Create RX ath_buf array */ - - error = ath_descdma_setup(sc, &sc->sc_rxdma, &sc->sc_rxbuf, - "rx", ath_rxbuf, 1); + /* + * Create RX DMA tag and buffers. + */ + error = ath_descdma_setup_rx_edma(sc, &sc->sc_rxdma, &sc->sc_rxbuf, + "rx", ath_rxbuf, sc->sc_rx_statuslen); if (error != 0) return error; @@ -739,15 +739,16 @@ ath_recv_setup_edma(struct ath_softc *sc) /* Set buffer size to 4k */ sc->sc_edma_bufsize = 4096; - /* Configure the hardware with this */ - (void) ath_hal_setrxbufsize(sc->sc_ah, sc->sc_edma_bufsize); - /* Fetch EDMA field and buffer sizes */ (void) ath_hal_getrxstatuslen(sc->sc_ah, &sc->sc_rx_statuslen); (void) ath_hal_gettxdesclen(sc->sc_ah, &sc->sc_tx_desclen); (void) ath_hal_gettxstatuslen(sc->sc_ah, &sc->sc_tx_statuslen); (void) ath_hal_getntxmaps(sc->sc_ah, &sc->sc_tx_nmaps); + /* Configure the hardware with the RX buffer size */ + (void) ath_hal_setrxbufsize(sc->sc_ah, sc->sc_edma_bufsize - + sc->sc_rx_statuslen); + device_printf(sc->sc_dev, "RX status length: %d\n", sc->sc_rx_statuslen); device_printf(sc->sc_dev, "TX descriptor length: %d\n",