svn commit: r344197 - stable/12/sys/dev/iwn

Andriy Voskoboinyk avos at FreeBSD.org
Sat Feb 16 01:19:16 UTC 2019


Author: avos
Date: Sat Feb 16 01:19:14 2019
New Revision: 344197
URL: https://svnweb.freebsd.org/changeset/base/344197

Log:
  MFC r343094:
  iwn(4): (partially) rewrite A-MPDU Tx path
  
  The change fixes ifnet counters and improves Tx stability when A-MPDU
  is used.
  
  MFC r343292:
  iwn(4): drop i_seq field initialization for A-MPDU frames
  
  It is done by net80211 since r319460.
  
  PR:		192641, 210211
  Reviewed by:	adrian, dhw
  Differential Revision:	https://reviews.freebsd.org/D10728

Modified:
  stable/12/sys/dev/iwn/if_iwn.c
  stable/12/sys/dev/iwn/if_iwn_debug.h
  stable/12/sys/dev/iwn/if_iwnreg.h
  stable/12/sys/dev/iwn/if_iwnvar.h
Directory Properties:
  stable/12/   (props changed)

Modified: stable/12/sys/dev/iwn/if_iwn.c
==============================================================================
--- stable/12/sys/dev/iwn/if_iwn.c	Sat Feb 16 01:05:22 2019	(r344196)
+++ stable/12/sys/dev/iwn/if_iwn.c	Sat Feb 16 01:19:14 2019	(r344197)
@@ -168,6 +168,7 @@ static int	iwn_alloc_tx_ring(struct iwn_softc *, struc
 		    int);
 static void	iwn_reset_tx_ring(struct iwn_softc *, struct iwn_tx_ring *);
 static void	iwn_free_tx_ring(struct iwn_softc *, struct iwn_tx_ring *);
+static void	iwn_check_tx_ring(struct iwn_softc *, int);
 static void	iwn5000_ict_reset(struct iwn_softc *);
 static int	iwn_read_eeprom(struct iwn_softc *,
 		    uint8_t macaddr[IEEE80211_ADDR_LEN]);
@@ -199,6 +200,8 @@ static void	iwn_calib_timeout(void *);
 static void	iwn_rx_phy(struct iwn_softc *, struct iwn_rx_desc *);
 static void	iwn_rx_done(struct iwn_softc *, struct iwn_rx_desc *,
 		    struct iwn_rx_data *);
+static void	iwn_agg_tx_complete(struct iwn_softc *, struct iwn_tx_ring *,
+		    int, int, int);
 static void	iwn_rx_compressed_ba(struct iwn_softc *, struct iwn_rx_desc *);
 static void	iwn5000_rx_calib_results(struct iwn_softc *,
 		    struct iwn_rx_desc *);
@@ -207,10 +210,13 @@ static void	iwn4965_tx_done(struct iwn_softc *, struct
 		    struct iwn_rx_data *);
 static void	iwn5000_tx_done(struct iwn_softc *, struct iwn_rx_desc *,
 		    struct iwn_rx_data *);
+static void	iwn_adj_ampdu_ptr(struct iwn_softc *, struct iwn_tx_ring *);
 static void	iwn_tx_done(struct iwn_softc *, struct iwn_rx_desc *, int, int,
 		    uint8_t);
-static void	iwn_ampdu_tx_done(struct iwn_softc *, int, int, int, int, int,
-		    void *);
+static int	iwn_ampdu_check_bitmap(uint64_t, int, int);
+static int	iwn_ampdu_index_check(struct iwn_softc *, struct iwn_tx_ring *,
+		    uint64_t, int, int);
+static void	iwn_ampdu_tx_done(struct iwn_softc *, int, int, int, void *);
 static void	iwn_cmd_done(struct iwn_softc *, struct iwn_rx_desc *);
 static void	iwn_notif_intr(struct iwn_softc *);
 static void	iwn_wakeup_intr(struct iwn_softc *);
@@ -2069,6 +2075,8 @@ iwn_reset_tx_ring(struct iwn_softc *sc, struct iwn_tx_
 			ieee80211_free_node(data->ni);
 			data->ni = NULL;
 		}
+		data->remapped = 0;
+		data->long_retries = 0;
 	}
 	/* Clear TX descriptors. */
 	memset(ring->desc, 0, ring->desc_dma.size);
@@ -2108,6 +2116,42 @@ iwn_free_tx_ring(struct iwn_softc *sc, struct iwn_tx_r
 }
 
 static void
+iwn_check_tx_ring(struct iwn_softc *sc, int qid)
+{
+	struct iwn_tx_ring *ring = &sc->txq[qid];
+
+	KASSERT(ring->queued >= 0, ("%s: ring->queued (%d) for queue %d < 0!",
+	    __func__, ring->queued, qid));
+
+	if (qid >= sc->firstaggqueue) {
+		struct iwn_ops *ops = &sc->ops;
+		struct ieee80211_tx_ampdu *tap = sc->qid2tap[qid];
+
+		if (ring->queued == 0 && !IEEE80211_AMPDU_RUNNING(tap)) {
+			uint16_t ssn = tap->txa_start & 0xfff;
+			uint8_t tid = tap->txa_tid;
+			int *res = tap->txa_private;
+
+			iwn_nic_lock(sc);
+			ops->ampdu_tx_stop(sc, qid, tid, ssn);
+			iwn_nic_unlock(sc);
+
+			sc->qid2tap[qid] = NULL;
+			free(res, M_DEVBUF);
+		}
+	}
+
+	if (ring->queued < IWN_TX_RING_LOMARK) {
+		sc->qfullmsk &= ~(1 << qid);
+
+		if (ring->queued == 0)
+			sc->sc_tx_timer = 0;
+		else
+			sc->sc_tx_timer = 5;
+	}
+}
+
+static void
 iwn5000_ict_reset(struct iwn_softc *sc)
 {
 	/* Disable interrupts. */
@@ -3163,104 +3207,129 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *
 
 }
 
-/* Process an incoming Compressed BlockAck. */
 static void
-iwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc)
+iwn_agg_tx_complete(struct iwn_softc *sc, struct iwn_tx_ring *ring, int tid,
+    int idx, int success)
 {
 	struct ieee80211_ratectl_tx_status *txs = &sc->sc_txs;
-	struct iwn_ops *ops = &sc->ops;
+	struct iwn_tx_data *data = &ring->data[idx];
 	struct iwn_node *wn;
+	struct mbuf *m;
 	struct ieee80211_node *ni;
+
+	KASSERT(data->ni != NULL, ("idx %d: no node", idx));
+	KASSERT(data->m != NULL, ("idx %d: no mbuf", idx));
+
+	/* Unmap and free mbuf. */
+	bus_dmamap_sync(ring->data_dmat, data->map,
+	    BUS_DMASYNC_POSTWRITE);
+	bus_dmamap_unload(ring->data_dmat, data->map);
+	m = data->m, data->m = NULL;
+	ni = data->ni, data->ni = NULL;
+	wn = (void *)ni;
+
+#if 0
+	/* XXX causes significant performance degradation. */
+	txs->flags = IEEE80211_RATECTL_STATUS_SHORT_RETRY |
+		     IEEE80211_RATECTL_STATUS_LONG_RETRY;
+	txs->long_retries = data->long_retries - 1;
+#else
+	txs->flags = IEEE80211_RATECTL_STATUS_SHORT_RETRY;
+#endif
+	txs->short_retries = wn->agg[tid].short_retries;
+	if (success)
+		txs->status = IEEE80211_RATECTL_TX_SUCCESS;
+	else
+		txs->status = IEEE80211_RATECTL_TX_FAIL_UNSPECIFIED;
+
+	wn->agg[tid].short_retries = 0;
+	data->long_retries = 0;
+
+	DPRINTF(sc, IWN_DEBUG_AMPDU, "%s: freeing m %p ni %p idx %d qid %d\n",
+	    __func__, m, ni, idx, ring->qid);
+	ieee80211_ratectl_tx_complete(ni, txs);
+	ieee80211_tx_complete(ni, m, !success);
+}
+
+/* Process an incoming Compressed BlockAck. */
+static void
+iwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc)
+{
+	struct iwn_tx_ring *ring;
+	struct iwn_tx_data *data;
+	struct iwn_node *wn;
 	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;
-	uint16_t ssn;
 	uint8_t tid;
-	int i, lastidx, qid, *res, shift;
-	int tx_ok = 0, tx_err = 0;
+	int i, qid, shift;
+	int tx_ok = 0;
 
-	DPRINTF(sc, IWN_DEBUG_TRACE | IWN_DEBUG_XMIT, "->%s begin\n", __func__);
+	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
 
 	qid = le16toh(ba->qid);
-	txq = &sc->txq[ba->qid];
-	tap = sc->qid2tap[ba->qid];
+	tap = sc->qid2tap[qid];
+	ring = &sc->txq[qid];
 	tid = tap->txa_tid;
 	wn = (void *)tap->txa_ni;
 
-	res = NULL;
-	ssn = 0;
-	if (!IEEE80211_AMPDU_RUNNING(tap)) {
-		res = tap->txa_private;
-		ssn = tap->txa_start & 0xfff;
-	}
+	DPRINTF(sc, IWN_DEBUG_AMPDU, "%s: qid %d tid %d seq %04X ssn %04X\n"
+	    "bitmap: ba %016jX wn %016jX, start %d\n",
+	    __func__, qid, tid, le16toh(ba->seq), le16toh(ba->ssn),
+	    (uintmax_t)le64toh(ba->bitmap), (uintmax_t)wn->agg[tid].bitmap,
+	    wn->agg[tid].startidx);
 
-	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"));
-
-		DPRINTF(sc, IWN_DEBUG_XMIT, "%s: freeing m=%p\n", __func__, m);
-		ieee80211_tx_complete(ni, m, 1);
-
-		txq->queued--;
-		txq->read = (txq->read + 1) % IWN_TX_RING_COUNT;
-	}
-
-	if (txq->queued == 0 && res != NULL) {
-		iwn_nic_lock(sc);
-		ops->ampdu_tx_stop(sc, qid, tid, ssn);
-		iwn_nic_unlock(sc);
-		sc->qid2tap[qid] = NULL;
-		free(res, M_DEVBUF);
-		return;
-	}
-
 	if (wn->agg[tid].bitmap == 0)
 		return;
 
 	shift = wn->agg[tid].startidx - ((le16toh(ba->seq) >> 4) & 0xff);
-	if (shift < 0)
+	if (shift <= -64)
 		shift += 0x100;
 
-	if (wn->agg[tid].nframes > (64 - shift))
-		return;
-
 	/*
-	 * Walk the bitmap and calculate how many successful and failed
-	 * attempts are made.
+	 * Walk the bitmap and calculate how many successful attempts
+	 * are made.
 	 *
 	 * Yes, the rate control code doesn't know these are A-MPDU
-	 * subframes and that it's okay to fail some of these.
+	 * subframes; due to that long_retries stats are not used here.
 	 */
-	ni = tap->txa_ni;
-	bitmap = (le64toh(ba->bitmap) >> shift) & wn->agg[tid].bitmap;
-	for (i = 0; bitmap; i++) {
-		txs->flags = 0;		/* XXX TODO */
-		if ((bitmap & 1) == 0) {
-			tx_err ++;
-			txs->status = IEEE80211_RATECTL_TX_FAIL_UNSPECIFIED;
-		} else {
-			tx_ok ++;
-			txs->status = IEEE80211_RATECTL_TX_SUCCESS;
+	bitmap = le64toh(ba->bitmap);
+	if (shift >= 0)
+		bitmap >>= shift;
+	else
+		bitmap <<= -shift;
+	bitmap &= wn->agg[tid].bitmap;
+	wn->agg[tid].bitmap = 0;
+
+	for (i = wn->agg[tid].startidx;
+	     bitmap;
+	     bitmap >>= 1, i = (i + 1) % IWN_TX_RING_COUNT) {
+		if ((bitmap & 1) == 0)
+			continue;
+
+		data = &ring->data[i];
+		if (__predict_false(data->m == NULL)) {
+			/*
+			 * There is no frame; skip this entry.
+			 *
+			 * NB: it is "ok" to have both
+			 * 'tx done' + 'compressed BA' replies for frame
+			 * with STATE_SCD_QUERY status.
+			 */
+			DPRINTF(sc, IWN_DEBUG_AMPDU,
+			    "%s: ring %d: no entry %d\n", __func__, qid, i);
+			continue;
 		}
-		ieee80211_ratectl_tx_complete(ni, txs);
-		bitmap >>= 1;
+
+		tx_ok++;
+		iwn_agg_tx_complete(sc, ring, tid, i, 1);
 	}
 
-	DPRINTF(sc, IWN_DEBUG_TRACE | IWN_DEBUG_XMIT,
-	    "->%s: end; %d ok; %d err\n",__func__, tx_ok, tx_err);
+	ring->queued -= tx_ok;
+	iwn_check_tx_ring(sc, qid);
 
+	DPRINTF(sc, IWN_DEBUG_TRACE | IWN_DEBUG_AMPDU,
+	    "->%s: end; %d ok\n",__func__, tx_ok);
 }
 
 /*
@@ -3508,9 +3577,9 @@ iwn4965_tx_done(struct iwn_softc *sc, struct iwn_rx_de
 	    stat->rate, le16toh(stat->duration),
 	    le32toh(stat->status));
 
-	if (qid >= sc->firstaggqueue) {
-		iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes,
-		    stat->rtsfailcnt, stat->ackfailcnt, &stat->status);
+	if (qid >= sc->firstaggqueue && stat->nframes != 1) {
+		iwn_ampdu_tx_done(sc, qid, stat->nframes, stat->rtsfailcnt,
+		    &stat->status);
 	} else {
 		iwn_tx_done(sc, desc, stat->rtsfailcnt, stat->ackfailcnt,
 		    le32toh(stat->status) & 0xff);
@@ -3538,15 +3607,32 @@ iwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_de
 	iwn5000_reset_sched(sc, qid, desc->idx);
 #endif
 
-	if (qid >= sc->firstaggqueue) {
-		iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes,
-		    stat->rtsfailcnt, stat->ackfailcnt, &stat->status);
+	if (qid >= sc->firstaggqueue && stat->nframes != 1) {
+		iwn_ampdu_tx_done(sc, qid, stat->nframes, stat->rtsfailcnt,
+		    &stat->status);
 	} else {
 		iwn_tx_done(sc, desc, stat->rtsfailcnt, stat->ackfailcnt,
 		    le16toh(stat->status) & 0xff);
 	}
 }
 
+static void
+iwn_adj_ampdu_ptr(struct iwn_softc *sc, struct iwn_tx_ring *ring)
+{
+	int i;
+
+	for (i = ring->read; i != ring->cur; i = (i + 1) % IWN_TX_RING_COUNT) {
+		struct iwn_tx_data *data = &ring->data[i];
+
+		if (data->m != NULL)
+			break;
+
+		data->remapped = 0;
+	}
+
+	ring->read = i;
+}
+
 /*
  * Adapter-independent backend for TX_DONE firmware notifications.
  */
@@ -3560,7 +3646,18 @@ iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *
 	struct mbuf *m;
 	struct ieee80211_node *ni;
 
+	if (__predict_false(data->m == NULL &&
+	    ring->qid >= sc->firstaggqueue)) {
+		/*
+		 * There is no frame; skip this entry.
+		 */
+		DPRINTF(sc, IWN_DEBUG_AMPDU, "%s: ring %d: no entry %d\n",
+		    __func__, ring->qid, desc->idx);
+		return;
+	}
+
 	KASSERT(data->ni != NULL, ("no node"));
+	KASSERT(data->m != NULL, ("no mbuf"));
 
 	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
 
@@ -3570,7 +3667,20 @@ iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *
 	m = data->m, data->m = NULL;
 	ni = data->ni, data->ni = NULL;
 
+	data->long_retries = 0;
+
+	if (ring->qid >= sc->firstaggqueue)
+		iwn_adj_ampdu_ptr(sc, ring);
+
 	/*
+	 * XXX f/w may hang (device timeout) when desc->idx - ring->read == 64
+	 * (aggregation queues only).
+	 */
+
+	ring->queued--;
+	iwn_check_tx_ring(sc, ring->qid);
+
+	/*
 	 * Update rate control statistics for the node.
 	 */
 	txs->flags = IEEE80211_RATECTL_STATUS_SHORT_RETRY |
@@ -3618,10 +3728,6 @@ iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *
 		ieee80211_tx_complete(ni, m,
 		    (status & IWN_TX_FAIL) != 0);
 
-	sc->sc_tx_timer = 0;
-	if (--ring->queued < IWN_TX_RING_LOMARK)
-		sc->qfullmsk &= ~(1 << ring->qid);
-
 	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
 }
 
@@ -3658,148 +3764,218 @@ iwn_cmd_done(struct iwn_softc *sc, struct iwn_rx_desc 
 	wakeup(&ring->desc[desc->idx]);
 }
 
+static int
+iwn_ampdu_check_bitmap(uint64_t bitmap, int start, int idx)
+{
+	int bit, shift;
+
+	bit = idx - start;
+	shift = 0;
+	if (bit >= 64) {
+		shift = 0x100 - bit;
+		bit = 0;
+	} else if (bit <= -64)
+		bit = 0x100 + bit;
+	else if (bit < 0) {
+		shift = -bit;
+		bit = 0;
+	}
+
+	if (bit - shift >= 64)
+		return (0);
+
+	return ((bitmap & (1ULL << (bit - shift))) != 0);
+}
+
+/*
+ * Firmware bug workaround: in case if 'retries' counter
+ * overflows 'seqno' field will be incremented:
+ *    status|sequence|status|sequence|status|sequence
+ *     0000    0A48    0001    0A49    0000    0A6A
+ *     1000    0A48    1000    0A49    1000    0A6A
+ *     2000    0A48    2000    0A49    2000    0A6A
+ * ...
+ *     E000    0A48    E000    0A49    E000    0A6A
+ *     F000    0A48    F000    0A49    F000    0A6A
+ *     0000    0A49    0000    0A49    0000    0A6B
+ *     1000    0A49    1000    0A49    1000    0A6B
+ * ...
+ *     D000    0A49    D000    0A49    D000    0A6B
+ *     E000    0A49    E001    0A49    E000    0A6B
+ *     F000    0A49    F001    0A49    F000    0A6B
+ *     0000    0A4A    0000    0A4B    0000    0A6A
+ *     1000    0A4A    1000    0A4B    1000    0A6A
+ * ...
+ *
+ * Odd 'seqno' numbers are incremened by 2 every 2 overflows.
+ * For even 'seqno' % 4 != 0 overflow is cyclic (0 -> +1 -> 0).
+ * Not checked with nretries >= 64.
+ *
+ */
+static int
+iwn_ampdu_index_check(struct iwn_softc *sc, struct iwn_tx_ring *ring,
+    uint64_t bitmap, int start, int idx)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct iwn_tx_data *data;
+	int diff, min_retries, max_retries, new_idx, loop_end;
+
+	new_idx = idx - IWN_LONG_RETRY_LIMIT_LOG;
+	if (new_idx < 0)
+		new_idx += IWN_TX_RING_COUNT;
+
+	/*
+	 * Corner case: check if retry count is not too big;
+	 * reset device otherwise.
+	 */
+	if (!iwn_ampdu_check_bitmap(bitmap, start, new_idx)) {
+		data = &ring->data[new_idx];
+		if (data->long_retries > IWN_LONG_RETRY_LIMIT) {
+			device_printf(sc->sc_dev,
+			    "%s: retry count (%d) for idx %d/%d overflow, "
+			    "resetting...\n", __func__, data->long_retries,
+			    ring->qid, new_idx);
+			ieee80211_restart_all(ic);
+			return (-1);
+		}
+	}
+
+	/* Correct index if needed. */
+	loop_end = idx;
+	do {
+		data = &ring->data[new_idx];
+		diff = idx - new_idx;
+		if (diff < 0)
+			diff += IWN_TX_RING_COUNT;
+
+		min_retries = IWN_LONG_RETRY_FW_OVERFLOW * diff;
+		if ((new_idx % 2) == 0)
+			max_retries = IWN_LONG_RETRY_FW_OVERFLOW * (diff + 1);
+		else
+			max_retries = IWN_LONG_RETRY_FW_OVERFLOW * (diff + 2);
+
+		if (!iwn_ampdu_check_bitmap(bitmap, start, new_idx) &&
+		    ((data->long_retries >= min_retries &&
+		      data->long_retries < max_retries) ||
+		     (diff == 1 &&
+		      (new_idx & 0x03) == 0x02 &&
+		      data->long_retries >= IWN_LONG_RETRY_FW_OVERFLOW))) {
+			DPRINTF(sc, IWN_DEBUG_AMPDU,
+			    "%s: correcting index %d -> %d in queue %d"
+			    " (retries %d)\n", __func__, idx, new_idx,
+			    ring->qid, data->long_retries);
+			return (new_idx);
+		}
+
+		new_idx = (new_idx + 1) % IWN_TX_RING_COUNT;
+	} while (new_idx != loop_end);
+
+	return (idx);
+}
+
 static void
-iwn_ampdu_tx_done(struct iwn_softc *sc, int qid, int idx, int nframes,
-    int rtsfailcnt, int ackfailcnt, void *stat)
+iwn_ampdu_tx_done(struct iwn_softc *sc, int qid, int nframes, int rtsfailcnt,
+    void *stat)
 {
-	struct iwn_ops *ops = &sc->ops;
 	struct iwn_tx_ring *ring = &sc->txq[qid];
-	struct ieee80211_ratectl_tx_status *txs = &sc->sc_txs;
+	struct ieee80211_tx_ampdu *tap = sc->qid2tap[qid];
+	struct iwn_node *wn = (void *)tap->txa_ni;
 	struct iwn_tx_data *data;
-	struct mbuf *m;
-	struct iwn_node *wn;
-	struct ieee80211_node *ni;
-	struct ieee80211_tx_ampdu *tap;
-	uint64_t bitmap;
-	uint32_t *status = stat;
+	uint64_t bitmap = 0;
 	uint16_t *aggstatus = stat;
-	uint16_t ssn;
-	uint8_t tid;
-	int bit, i, lastidx, *res, seqno, shift, start;
+	uint8_t tid = tap->txa_tid;
+	int bit, i, idx, shift, start, tx_err;
 
-	/* XXX TODO: status is le16 field! Grr */
-
 	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
-	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: nframes=%d, status=0x%08x\n",
-	    __func__,
-	    nframes,
-	    *status);
 
-	tap = sc->qid2tap[qid];
-	tid = tap->txa_tid;
-	wn = (void *)tap->txa_ni;
-	ni = tap->txa_ni;
+	start = le16toh(*(aggstatus + nframes * 2)) & 0xff;
 
-	/*
-	 * XXX TODO: ACK and RTS failures would be nice here!
-	 */
+	for (i = 0; i < nframes; i++) {
+		uint16_t status = le16toh(aggstatus[i * 2]);
 
-	/*
-	 * A-MPDU single frame status - if we failed to transmit it
-	 * in A-MPDU, then it may be a permanent failure.
-	 *
-	 * XXX TODO: check what the Linux iwlwifi driver does here;
-	 * there's some permanent and temporary failures that may be
-	 * handled differently.
-	 */
-	if (nframes == 1) {
-		txs->flags = IEEE80211_RATECTL_STATUS_SHORT_RETRY |
-			     IEEE80211_RATECTL_STATUS_LONG_RETRY;
-		txs->short_retries = rtsfailcnt;
-		txs->long_retries = ackfailcnt;
-		if ((*status & 0xff) != 1 && (*status & 0xff) != 2) {
-#ifdef	NOT_YET
-			printf("ieee80211_send_bar()\n");
-#endif
+		if (status & IWN_AGG_TX_STATE_IGNORE_MASK)
+			continue;
+
+		idx = le16toh(aggstatus[i * 2 + 1]) & 0xff;
+		data = &ring->data[idx];
+		if (data->remapped) {
+			idx = iwn_ampdu_index_check(sc, ring, bitmap, start, idx);
+			if (idx == -1) {
+				/* skip error (device will be restarted anyway). */
+				continue;
+			}
+
+			/* Index may have changed. */
+			data = &ring->data[idx];
+		}
+
+		/*
+		 * XXX Sometimes (rarely) some frames are excluded from events.
+		 * XXX Due to that long_retries counter may be wrong.
+		 */
+		data->long_retries &= ~0x0f;
+		data->long_retries += IWN_AGG_TX_TRY_COUNT(status) + 1;
+
+		if (data->long_retries >= IWN_LONG_RETRY_FW_OVERFLOW) {
+			int diff, wrong_idx;
+
+			diff = data->long_retries / IWN_LONG_RETRY_FW_OVERFLOW;
+			wrong_idx = (idx + diff) % IWN_TX_RING_COUNT;
+
 			/*
-			 * If we completely fail a transmit, make sure a
-			 * notification is pushed up to the rate control
-			 * layer.
+			 * Mark the entry so the above code will check it
+			 * next time.
 			 */
-			/* XXX */
-			txs->status = IEEE80211_RATECTL_TX_FAIL_UNSPECIFIED;
-		} else {
+			ring->data[wrong_idx].remapped = 1;
+		}
+
+		if (status & IWN_AGG_TX_STATE_UNDERRUN_MSK) {
 			/*
-			 * If nframes=1, then we won't be getting a BA for
-			 * this frame.  Ensure that we correctly update the
-			 * rate control code with how many retries were
-			 * needed to send it.
+			 * NB: count retries but postpone - it was not
+			 * transmitted.
 			 */
-			txs->status = IEEE80211_RATECTL_TX_SUCCESS;
+			continue;
 		}
-		ieee80211_ratectl_tx_complete(ni, txs);
-	}
 
-	bitmap = 0;
-	start = idx;
-	for (i = 0; i < nframes; i++) {
-		if (le16toh(aggstatus[i * 2]) & 0xc)
-			continue;
-
-		idx = le16toh(aggstatus[2*i + 1]) & 0xff;
 		bit = idx - start;
 		shift = 0;
 		if (bit >= 64) {
-			shift = 0x100 - idx + start;
+			shift = 0x100 - bit;
 			bit = 0;
-			start = idx;
 		} else if (bit <= -64)
-			bit = 0x100 - start + idx;
+			bit = 0x100 + bit;
 		else if (bit < 0) {
-			shift = start - idx;
-			start = idx;
+			shift = -bit;
 			bit = 0;
 		}
 		bitmap = bitmap << shift;
 		bitmap |= 1ULL << bit;
 	}
-	tap = sc->qid2tap[qid];
-	tid = tap->txa_tid;
-	wn = (void *)tap->txa_ni;
-	wn->agg[tid].bitmap = bitmap;
 	wn->agg[tid].startidx = start;
-	wn->agg[tid].nframes = nframes;
+	wn->agg[tid].bitmap = bitmap;
+	wn->agg[tid].short_retries = rtsfailcnt;
 
-	res = NULL;
-	ssn = 0;
-	if (!IEEE80211_AMPDU_RUNNING(tap)) {
-		res = tap->txa_private;
-		ssn = tap->txa_start & 0xfff;
-	}
+	DPRINTF(sc, IWN_DEBUG_AMPDU, "%s: nframes %d start %d bitmap %016jX\n",
+	    __func__, nframes, start, (uintmax_t)bitmap);
 
-	/* This is going nframes DWORDS into the descriptor? */
-	seqno = le32toh(*(status + nframes)) & 0xfff;
-	for (lastidx = (seqno & 0xff); ring->read != lastidx;) {
-		data = &ring->data[ring->read];
+	i = ring->read;
 
-		/* Unmap and free mbuf. */
-		bus_dmamap_sync(ring->data_dmat, data->map,
-		    BUS_DMASYNC_POSTWRITE);
-		bus_dmamap_unload(ring->data_dmat, data->map);
-		m = data->m, data->m = NULL;
-		ni = data->ni, data->ni = NULL;
+	for (tx_err = 0;
+	     i != wn->agg[tid].startidx;
+	     i = (i + 1) % IWN_TX_RING_COUNT) {
+		data = &ring->data[i];
+		data->remapped = 0;
+		if (data->m == NULL)
+			continue;
 
-		KASSERT(ni != NULL, ("no node"));
-		KASSERT(m != NULL, ("no mbuf"));
-		DPRINTF(sc, IWN_DEBUG_XMIT, "%s: freeing m=%p\n", __func__, m);
-		ieee80211_tx_complete(ni, m, 1);
-
-		ring->queued--;
-		ring->read = (ring->read + 1) % IWN_TX_RING_COUNT;
+		tx_err++;
+		iwn_agg_tx_complete(sc, ring, tid, i, 0);
 	}
 
-	if (ring->queued == 0 && res != NULL) {
-		iwn_nic_lock(sc);
-		ops->ampdu_tx_stop(sc, qid, tid, ssn);
-		iwn_nic_unlock(sc);
-		sc->qid2tap[qid] = NULL;
-		free(res, M_DEVBUF);
-		return;
-	}
+	ring->read = wn->agg[tid].startidx;
+	ring->queued -= tx_err;
 
-	sc->sc_tx_timer = 0;
-	if (ring->queued < IWN_TX_RING_LOMARK)
-		sc->qfullmsk &= ~(1 << ring->qid);
+	iwn_check_tx_ring(sc, qid);
 
 	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
 }
@@ -4379,7 +4555,7 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, stru
 	struct ieee80211_frame *wh;
 	struct ieee80211_key *k = NULL;
 	uint32_t flags;
-	uint16_t seqno, qos;
+	uint16_t qos;
 	uint8_t tid, type;
 	int ac, totlen, rate;
 
@@ -4421,25 +4597,13 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, stru
 	 */
 
 	ac = M_WME_GETAC(m);
-	seqno = ni->ni_txseqs[tid];
 	if (m->m_flags & M_AMPDU_MPDU) {
 		struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac];
 
-		if (!IEEE80211_AMPDU_RUNNING(tap)) {
+		if (!IEEE80211_AMPDU_RUNNING(tap))
 			return (EINVAL);
-		}
 
-		/*
-		 * Queue this frame to the hardware ring that we've
-		 * negotiated AMPDU TX on.
-		 *
-		 * Note that the sequence number must match the TX slot
-		 * being used!
-		 */
 		ac = *(int *)tap->txa_private;
-		*(uint16_t *)wh->i_seq =
-		    htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT);
-		ni->ni_txseqs[tid]++;
 	}
 
 	/* Encrypt the frame if need be. */
@@ -4508,15 +4672,42 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, stru
 	}
 
 	ring = &sc->txq[ac];
-	if ((m->m_flags & M_AMPDU_MPDU) != 0 &&
-	    (seqno % 256) != ring->cur) {
-		device_printf(sc->sc_dev,
-		    "%s: m=%p: seqno (%d) (%d) != ring index (%d) !\n",
-		    __func__,
-		    m,
-		    seqno,
-		    seqno % 256,
-		    ring->cur);
+	if (m->m_flags & M_AMPDU_MPDU) {
+		uint16_t seqno = ni->ni_txseqs[tid];
+
+		if (ring->queued > IWN_TX_RING_COUNT / 2 &&
+		    (ring->cur + 1) % IWN_TX_RING_COUNT == ring->read) {
+			DPRINTF(sc, IWN_DEBUG_AMPDU, "%s: no more space "
+			    "(queued %d) left in %d queue!\n",
+			    __func__, ring->queued, ac);
+			return (ENOBUFS);
+		}
+
+		/*
+		 * Queue this frame to the hardware ring that we've
+		 * negotiated AMPDU TX on.
+		 *
+		 * Note that the sequence number must match the TX slot
+		 * being used!
+		 */
+		if ((seqno % 256) != ring->cur) {
+			device_printf(sc->sc_dev,
+			    "%s: m=%p: seqno (%d) (%d) != ring index (%d) !\n",
+			    __func__,
+			    m,
+			    seqno,
+			    seqno % 256,
+			    ring->cur);
+
+			/* XXX until D9195 will not be committed */
+			ni->ni_txseqs[tid] &= ~0xff;
+			ni->ni_txseqs[tid] += ring->cur;
+			seqno = ni->ni_txseqs[tid];
+		}
+
+		*(uint16_t *)wh->i_seq =
+		    htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT);
+		ni->ni_txseqs[tid]++;
 	}
 
 	/* Prepare TX firmware command. */
@@ -4677,6 +4868,13 @@ iwn_tx_cmd(struct iwn_softc *sc, struct mbuf *m, struc
 
 	desc = &ring->desc[ring->cur];
 	data = &ring->data[ring->cur];
+
+	if (__predict_false(data->m != NULL || data->ni != NULL)) {
+		device_printf(sc->sc_dev, "%s: ni (%p) or m (%p) for idx %d "
+		    "in queue %d is not NULL!\n", __func__, data->ni, data->m,
+		    ring->cur, ring->qid);
+		return EIO;
+	}
 
 	/* Prepare TX firmware command. */
 	cmd = &ring->cmd[ring->cur];

Modified: stable/12/sys/dev/iwn/if_iwn_debug.h
==============================================================================
--- stable/12/sys/dev/iwn/if_iwn_debug.h	Sat Feb 16 01:05:22 2019	(r344196)
+++ stable/12/sys/dev/iwn/if_iwn_debug.h	Sat Feb 16 01:19:14 2019	(r344197)
@@ -44,6 +44,7 @@ enum {
 	IWN_DEBUG_PWRSAVE	= 0x00004000,	/* Power save operations */
 	IWN_DEBUG_SCAN		= 0x00008000,	/* Scan related operations */
 	IWN_DEBUG_STATS		= 0x00010000,	/* Statistics updates */
+	IWN_DEBUG_AMPDU		= 0x00020000,	/* A-MPDU specific Tx */
 	IWN_DEBUG_REGISTER	= 0x20000000,	/* print chipset register */
 	IWN_DEBUG_TRACE		= 0x40000000,	/* Print begin and start driver function */
 	IWN_DEBUG_FATAL		= 0x80000000,	/* fatal errors */

Modified: stable/12/sys/dev/iwn/if_iwnreg.h
==============================================================================
--- stable/12/sys/dev/iwn/if_iwnreg.h	Sat Feb 16 01:05:22 2019	(r344196)
+++ stable/12/sys/dev/iwn/if_iwnreg.h	Sat Feb 16 01:19:14 2019	(r344197)
@@ -1378,10 +1378,17 @@ struct iwn_ucode_info {
 
 #define	IWN_AGG_TX_STATUS_MSK		0x00000fff
 #define	IWN_AGG_TX_TRY_MSK		0x0000f000
+#define	IWN_AGG_TX_TRY_POS		12
+#define	IWN_AGG_TX_TRY_COUNT(status)	\
+	(((status) & IWN_AGG_TX_TRY_MSK) >> IWN_AGG_TX_TRY_POS)
 
 #define	IWN_AGG_TX_STATE_LAST_SENT_MSK		\
 	    (IWN_AGG_TX_STATE_LAST_SENT_TTL_MSK | \
 	     IWN_AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK)
+
+#define IWN_AGG_TX_STATE_IGNORE_MASK		\
+	    (IWN_AGG_TX_STATE_FEW_BYTES_MSK | \
+	     IWN_AGG_TX_STATE_ABORT_MSK)
 
 /* # tx attempts for first frame in aggregation */
 #define	IWN_AGG_TX_STATE_TRY_CNT_POS	12

Modified: stable/12/sys/dev/iwn/if_iwnvar.h
==============================================================================
--- stable/12/sys/dev/iwn/if_iwnvar.h	Sat Feb 16 01:05:22 2019	(r344196)
+++ stable/12/sys/dev/iwn/if_iwnvar.h	Sat Feb 16 01:19:14 2019	(r344197)
@@ -100,6 +100,11 @@ struct iwn_tx_data {
 	bus_addr_t		scratch_paddr;
 	struct mbuf		*m;
 	struct ieee80211_node	*ni;
+	unsigned int		remapped:1;
+	unsigned int		long_retries:7;
+#define IWN_LONG_RETRY_FW_OVERFLOW	0x10
+#define IWN_LONG_RETRY_LIMIT_LOG	7
+#define IWN_LONG_RETRY_LIMIT		((1 << IWN_LONG_RETRY_LIMIT_LOG) - 3)
 };
 
 struct iwn_tx_ring {
@@ -138,8 +143,8 @@ struct iwn_node {
 	uint8_t				id;
 	struct {
 		uint64_t		bitmap;
+		int			short_retries;
 		int			startidx;
-		int			nframes;
 	} agg[IEEE80211_TID_SIZE];
 };
 


More information about the svn-src-all mailing list