summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorbschmidt <bschmidt@FreeBSD.org>2012-06-27 15:55:34 +0000
committerbschmidt <bschmidt@FreeBSD.org>2012-06-27 15:55:34 +0000
commit94f926381b2fb51be70183f9b9ef9c5130c65561 (patch)
treecd928c245517a86afaac8b2159cba17f97d67b58 /sys
parent06bb0a92994f0591683085b0d88c024087023325 (diff)
downloadFreeBSD-src-94f926381b2fb51be70183f9b9ef9c5130c65561.zip
FreeBSD-src-94f926381b2fb51be70183f9b9ef9c5130c65561.tar.gz
Fix a TX aggregation issue, if after the last compressed BA notification
the TX queue is empty, there won't be a TX done notification, effectly resulting in an mbuf leak. The correct way to handle this is to free up mbufs on both BA and TX done notifications up to the last sent seqno. Tested by: osa@ MFC after: 3 days
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/iwn/if_iwn.c41
1 files changed, 34 insertions, 7 deletions
diff --git a/sys/dev/iwn/if_iwn.c b/sys/dev/iwn/if_iwn.c
index 7a59796..844fb77 100644
--- a/sys/dev/iwn/if_iwn.c
+++ b/sys/dev/iwn/if_iwn.c
@@ -2437,18 +2437,43 @@ iwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc,
struct ieee80211_node *ni;
struct iwn_compressed_ba *ba = (struct iwn_compressed_ba *)(desc + 1);
struct iwn_tx_ring *txq;
+ struct iwn_tx_data *txdata;
struct ieee80211_tx_ampdu *tap;
+ struct mbuf *m;
uint64_t bitmap;
uint8_t tid;
- int ackfailcnt = 0, i, shift;
+ int ackfailcnt = 0, i, lastidx, qid, shift;
bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
- txq = &sc->txq[le16toh(ba->qid)];
- tap = sc->qid2tap[le16toh(ba->qid)];
+ qid = le16toh(ba->qid);
+ txq = &sc->txq[ba->qid];
+ tap = sc->qid2tap[ba->qid];
tid = tap->txa_tid;
- ni = tap->txa_ni;
- wn = (void *)ni;
+ wn = (void *)tap->txa_ni;
+
+ for (lastidx = le16toh(ba->ssn) & 0xff; txq->read != lastidx;) {
+ txdata = &txq->data[txq->read];
+
+ /* Unmap and free mbuf. */
+ bus_dmamap_sync(txq->data_dmat, txdata->map,
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(txq->data_dmat, txdata->map);
+ m = txdata->m, txdata->m = NULL;
+ ni = txdata->ni, txdata->ni = NULL;
+
+ KASSERT(ni != NULL, ("no node"));
+ KASSERT(m != NULL, ("no mbuf"));
+
+ if (m->m_flags & M_TXCB)
+ ieee80211_process_callback(ni, m, 1);
+
+ m_freem(m);
+ ieee80211_free_node(ni);
+
+ txq->queued--;
+ txq->read = (txq->read + 1) % IWN_TX_RING_COUNT;
+ }
if (wn->agg[tid].bitmap == 0)
return;
@@ -2460,6 +2485,7 @@ iwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc,
if (wn->agg[tid].nframes > (64 - shift))
return;
+ ni = tap->txa_ni;
bitmap = (le64toh(ba->bitmap) >> shift) & wn->agg[tid].bitmap;
for (i = 0; bitmap; i++) {
if ((bitmap & 1) == 0) {
@@ -2815,8 +2841,6 @@ iwn_ampdu_tx_done(struct iwn_softc *sc, int qid, int idx, int nframes,
for (lastidx = (seqno & 0xff); ring->read != lastidx;) {
data = &ring->data[ring->read];
- KASSERT(data->ni != NULL, ("no node"));
-
/* Unmap and free mbuf. */
bus_dmamap_sync(ring->data_dmat, data->map,
BUS_DMASYNC_POSTWRITE);
@@ -2824,6 +2848,9 @@ iwn_ampdu_tx_done(struct iwn_softc *sc, int qid, int idx, int nframes,
m = data->m, data->m = NULL;
ni = data->ni, data->ni = NULL;
+ KASSERT(ni != NULL, ("no node"));
+ KASSERT(m != NULL, ("no mbuf"));
+
if (m->m_flags & M_TXCB)
ieee80211_process_callback(ni, m, 1);
OpenPOWER on IntegriCloud