From 519e6c0fc222192a1862c66b66a78a9b8a983e73 Mon Sep 17 00:00:00 2001 From: Andriy Voskoboinyk Date: Sun, 6 Nov 2016 18:11:19 +0000 Subject: [PATCH] rtwn: fix Tx ring cleanup. Do not try to clear stale Tx descriptor entries when there are some running vaps; just free node references - rtwn_pci_tx_done() will free mbufs without creating holes in the Tx descriptor space. Also, reset only 2 first entries in the beacon ring - other will not be used anyway. Tested with RTL8188CE, STA + STA mode. --- sys/dev/rtwn/pci/rtwn_pci_attach.c | 124 ++++++++++++++++++++++------- 1 file changed, 94 insertions(+), 30 deletions(-) diff --git a/sys/dev/rtwn/pci/rtwn_pci_attach.c b/sys/dev/rtwn/pci/rtwn_pci_attach.c index e48ae8141291..4db4427a5f89 100644 --- a/sys/dev/rtwn/pci/rtwn_pci_attach.c +++ b/sys/dev/rtwn/pci/rtwn_pci_attach.c @@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$"); #include +#include #include #include #include @@ -75,6 +76,8 @@ static int rtwn_pci_alloc_rx_list(struct rtwn_softc *); static void rtwn_pci_reset_rx_list(struct rtwn_softc *); static void rtwn_pci_free_rx_list(struct rtwn_softc *); static int rtwn_pci_alloc_tx_list(struct rtwn_softc *, int); +static void rtwn_pci_reset_tx_ring_stopped(struct rtwn_softc *, int); +static void rtwn_pci_reset_beacon_ring(struct rtwn_softc *, int); static void rtwn_pci_reset_tx_list(struct rtwn_softc *, struct ieee80211vap *, int); static void rtwn_pci_free_tx_list(struct rtwn_softc *, int); @@ -312,48 +315,109 @@ rtwn_pci_alloc_tx_list(struct rtwn_softc *sc, int qid) } static void -rtwn_pci_reset_tx_list(struct rtwn_softc *sc, struct ieee80211vap *vap, - int qid) +rtwn_pci_reset_tx_ring_stopped(struct rtwn_softc *sc, int qid) { - struct rtwn_vap *uvp = RTWN_VAP(vap); struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); - struct rtwn_tx_ring *tx_ring = &pc->tx_ring[qid]; - int i, id; - - id = (uvp != NULL ? uvp->id : RTWN_VAP_ID_INVALID); + struct rtwn_tx_ring *ring = &pc->tx_ring[qid]; + int i; for (i = 0; i < RTWN_PCI_TX_LIST_COUNT; i++) { - struct rtwn_tx_data *tx_data = &tx_ring->tx_data[i]; + struct rtwn_tx_data *data = &ring->tx_data[i]; + void *desc = (uint8_t *)ring->desc + sc->txdesc_len * i; - if (vap == NULL || (tx_data->ni == NULL && - (tx_data->id == id || id == RTWN_VAP_ID_INVALID)) || - (tx_data->ni != NULL && tx_data->ni->ni_vap == vap)) { - void *tx_desc = - (uint8_t *)tx_ring->desc + sc->txdesc_len * i; + rtwn_pci_copy_tx_desc(pc, desc, NULL); - rtwn_pci_copy_tx_desc(pc, tx_desc, NULL); - - if (tx_data->m != NULL) { - bus_dmamap_sync(tx_ring->data_dmat, - tx_data->map, BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(tx_ring->data_dmat, - tx_data->map); - m_freem(tx_data->m); - tx_data->m = NULL; - } - if (tx_data->ni != NULL) { - ieee80211_free_node(tx_data->ni); - tx_data->ni = NULL; - } + if (data->m != NULL) { + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->data_dmat, data->map); + m_freem(data->m); + data->m = NULL; + } + if (data->ni != NULL) { + ieee80211_free_node(data->ni); + data->ni = NULL; } } - bus_dmamap_sync(tx_ring->desc_dmat, tx_ring->desc_map, + bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_POSTWRITE); sc->qfullmsk &= ~(1 << qid); - tx_ring->queued = 0; - tx_ring->last = tx_ring->cur = 0; + ring->queued = 0; + ring->last = ring->cur = 0; +} + +/* + * Clear entry 0 (or 1) in the beacon queue (other are not used). + */ +static void +rtwn_pci_reset_beacon_ring(struct rtwn_softc *sc, int id) +{ + struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); + struct rtwn_tx_ring *ring = &pc->tx_ring[RTWN_PCI_BEACON_QUEUE]; + struct rtwn_tx_data *data = &ring->tx_data[id]; + struct rtwn_tx_desc_common *txd = (struct rtwn_tx_desc_common *) + ((uint8_t *)ring->desc + id * sc->txdesc_len); + + bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_POSTREAD); + if (txd->flags0 & RTWN_FLAGS0_OWN) { + /* Clear OWN bit. */ + txd->flags0 &= ~RTWN_FLAGS0_OWN; + bus_dmamap_sync(ring->desc_dmat, ring->desc_map, + BUS_DMASYNC_PREWRITE); + + /* Unload mbuf. */ + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->data_dmat, data->map); + } +} + +/* + * Drop stale entries from Tx ring before the vap will be deleted. + * In case if vap is NULL just free everything and reset cur / last pointers. + */ +static void +rtwn_pci_reset_tx_list(struct rtwn_softc *sc, struct ieee80211vap *vap, + int qid) +{ + int i; + + if (vap == NULL) { + if (qid != RTWN_PCI_BEACON_QUEUE) { + /* + * Device was stopped; just clear all entries. + */ + rtwn_pci_reset_tx_ring_stopped(sc, qid); + } else { + for (i = 0; i < RTWN_PORT_COUNT; i++) + rtwn_pci_reset_beacon_ring(sc, i); + } + } else if (qid == RTWN_PCI_BEACON_QUEUE && + (vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_IBSS)) { + struct rtwn_vap *uvp = RTWN_VAP(vap); + + rtwn_pci_reset_beacon_ring(sc, uvp->id); + } else { + struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); + struct rtwn_tx_ring *ring = &pc->tx_ring[qid]; + + for (i = 0; i < RTWN_PCI_TX_LIST_COUNT; i++) { + struct rtwn_tx_data *data = &ring->tx_data[i]; + if (data->ni != NULL && data->ni->ni_vap == vap) { + /* + * NB: if some vap is still running + * rtwn_pci_tx_done() will free the mbuf; + * otherwise, rtwn_stop() will reset all rings + * after device shutdown. + */ + ieee80211_free_node(data->ni); + data->ni = NULL; + } + } + } } static void