Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 17 Aug 2011 09:02:58 +0000 (UTC)
From:      Adrian Chadd <adrian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-user@freebsd.org
Subject:   svn commit: r224930 - user/adrian/if_ath_tx/sys/dev/ath
Message-ID:  <201108170902.p7H92w3T095282@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: adrian
Date: Wed Aug 17 09:02:58 2011
New Revision: 224930
URL: http://svn.freebsd.org/changeset/base/224930

Log:
  This is another "too much going on in one commit for my own good" commits.
  
  The stuff in progress, which shouldn't affect anything:
  
  * Begin fleshing out the aggregate list creation function, untested
  * Rename some of the aggregate response functions to indicate they're
    handling individual, non-aggregate frames when in aggregation mode.
  * make ath_tx_aggr_comp() now call either the aggr_comp_aggr() for an
    aggregate frame list, or aggr_comp_unaggr() for a single frame.
  
  The important change which could break things, but is needed:
  
  * add ath_buf->bf_lastds, which points to the descriptor in the last
    frame in the list.
  
  Now, the reasoning.
  
  The TX descriptor status is in the _last_ descriptor in a list.
  
  For a single frame w/ a single TX descriptor, it's also the first
  descriptor.
  
  For a single frame w/ multiple TX descriptors, it's the last descriptor
  in the frame.
  
  For an aggregate list w/ multiple TX descriptors, it's the first
  descriptor in the -last- subframe. Ie, NOT the very last descriptor.
  
  The (upcoming) aggregate scheduling function will thus have to set
  ds->lastds to the first descriptor in the last buffer.
  
  The point is, bf->bf_lastds needs to be correct for setting the
  rate control stuff (which uses bf_lastds to hide a copy of the
  rate information in the last descriptor in the chain) and for
  completion status checking.
  
  TODO: although linux and the reference code seem to use the above,
  I really should double-check that this is correct before trying
  to TX aggregate frames.
  
  TODO: see whether that use of lastds in ath_buf_set_rate() is needed
  for the hardware, or for hiding a copy of the rate settings used,
  to be used by the rate control code.

Modified:
  user/adrian/if_ath_tx/sys/dev/ath/if_ath.c
  user/adrian/if_ath_tx/sys/dev/ath/if_ath_tx.c
  user/adrian/if_ath_tx/sys/dev/ath/if_ath_tx_ht.c
  user/adrian/if_ath_tx/sys/dev/ath/if_ath_tx_ht.h
  user/adrian/if_ath_tx/sys/dev/ath/if_athvar.h

Modified: user/adrian/if_ath_tx/sys/dev/ath/if_ath.c
==============================================================================
--- user/adrian/if_ath_tx/sys/dev/ath/if_ath.c	Wed Aug 17 08:27:11 2011	(r224929)
+++ user/adrian/if_ath_tx/sys/dev/ath/if_ath.c	Wed Aug 17 09:02:58 2011	(r224930)
@@ -3086,6 +3086,7 @@ ath_descdma_setup(struct ath_softc *sc,
 			ath_descdma_cleanup(sc, dd, head);
 			return error;
 		}
+		bf->bf_lastds = bf->bf_desc;	/* Just an initial value */
 		STAILQ_INSERT_TAIL(head, bf, bf_list);
 	}
 	return 0;
@@ -4171,7 +4172,7 @@ ath_tx_processq(struct ath_softc *sc, st
 			break;
 		}
 		ds0 = &bf->bf_desc[0];
-		ds = &bf->bf_desc[bf->bf_nseg - 1];
+		ds = bf->bf_lastds;	/* XXX must be setup correctly! */
 		ts = &bf->bf_status.ds_txstat;
 		status = ath_hal_txprocdesc(ah, ds, ts);
 #ifdef ATH_DEBUG

Modified: user/adrian/if_ath_tx/sys/dev/ath/if_ath_tx.c
==============================================================================
--- user/adrian/if_ath_tx/sys/dev/ath/if_ath_tx.c	Wed Aug 17 08:27:11 2011	(r224929)
+++ user/adrian/if_ath_tx/sys/dev/ath/if_ath_tx.c	Wed Aug 17 09:02:58 2011	(r224930)
@@ -317,8 +317,8 @@ ath_tx_chaindesclist(struct ath_softc *s
 			"%s: %d: %08x %08x %08x %08x %08x %08x\n",
 			__func__, i, ds->ds_link, ds->ds_data,
 			ds->ds_ctl0, ds->ds_ctl1, ds->ds_hw[0], ds->ds_hw[1]);
+		bf->bf_lastds = ds;
 	}
-
 }
 
 static void
@@ -606,6 +606,7 @@ ath_tx_setds(struct ath_softc *sc, struc
 		, bf->bf_state.bfs_ctsrate	/* rts/cts rate */
 		, bf->bf_state.bfs_ctsduration	/* rts/cts duration */
 	);
+	bf->bf_lastds = ds;
 
 	/* XXX TODO: Setup descriptor chain */
 }
@@ -1780,6 +1781,8 @@ ath_tx_swq(struct ath_softc *sc, struct 
 	bf->bf_state.bfs_txq = txq;
 	bf->bf_state.bfs_pri = pri;
 	bf->bf_state.bfs_dobaw = 0;
+	bf->bf_state.bfs_aggr = 0;
+	bf->bf_state.bfs_aggrburst = 0;
 
 	/* Queue frame to the tail of the software queue */
 	ATH_TXQ_LOCK(atid);
@@ -2010,7 +2013,7 @@ ath_tx_normal_comp(struct ath_softc *sc,
  * an A-MPDU.
  */
 static void
-ath_tx_comp_cleanup(struct ath_softc *sc, struct ath_buf *bf)
+ath_tx_comp_cleanup_unaggr(struct ath_softc *sc, struct ath_buf *bf)
 {
 	struct ieee80211_node *ni = bf->bf_node;
 	struct ath_node *an = ATH_NODE(ni);
@@ -2228,14 +2231,160 @@ ath_tx_aggr_retry_unaggr(struct ath_soft
 }
 
 /*
+ * Common code for aggregate excessive retry/subframe retry.
+ * If retrying, queues buffers to bf_q. If not, frees the
+ * buffers.
+ *
+ * XXX should unify this with ath_tx_aggr_retry_unaggr()
+ */
+static int
+ath_tx_retry_subframe(struct ath_softc *sc, struct ath_buf *bf,
+    ath_bufhead *bf_q)
+{
+	struct ieee80211_node *ni = bf->bf_node;
+	struct ath_node *an = ATH_NODE(ni);
+	int tid = bf->bf_state.bfs_tid;
+	struct ath_tid *atid = &an->an_tid[tid];
+
+	ath_hal_clr11n_aggr(sc->sc_ah, bf->bf_desc);
+	ath_hal_set11nburstduration(sc->sc_ah, bf->bf_desc, 0);
+	/* ath_hal_set11n_virtualmorefrag(sc->sc_ah, bf->bf_desc, 0); */
+
+	if (bf->bf_state.bfs_retries >= SWMAX_RETRIES) {
+		ath_tx_update_baw(sc, an, atid, SEQNO(bf->bf_state.bfs_seqno));
+		/* XXX subframe completion status? is that valid here? */
+		ath_tx_default_comp(sc, bf, 0);
+		return 1;
+	}
+
+	if (bf->bf_flags & ATH_BUF_BUSY) {
+		bf->bf_flags &= ~ ATH_BUF_BUSY;
+		DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
+		    "%s: bf %p: ATH_BUF_BUSY\n", __func__, bf);
+	}
+
+	ath_tx_set_retry(sc, bf);
+
+	STAILQ_INSERT_TAIL(bf_q, bf, bf_list);
+	return 0;
+}
+
+/*
+ * error pkt completion for an aggregate destination
+ */
+static void
+ath_tx_comp_aggr_error(struct ath_softc *sc, struct ath_buf *bf_first,
+    struct ath_tid *tid)
+{
+	struct ieee80211_node *ni = bf_first->bf_node;
+	struct ath_node *an = ATH_NODE(ni);
+	struct ath_buf *bf_next, *bf;
+	ath_bufhead bf_q;
+	int drops = 0;
+	struct ieee80211_tx_ampdu *tap;
+
+	tap = ath_tx_get_tx_tid(an, tid->tid);
+
+	STAILQ_INIT(&bf_q);
+
+	/* Retry all subframes */
+	bf = bf_first;
+	while (bf) {
+		bf_next = bf->bf_next;
+		drops += ath_tx_retry_subframe(sc, bf, &bf_q);
+		bf = bf_next;
+	}
+
+	/* Update rate control module about aggregation */
+	/* XXX todo */
+
+	/*
+	 * send bar if we dropped any frames
+	 */
+	if (drops) {
+		if (ieee80211_send_bar(ni, tap, ni->ni_txseqs[tid->tid]) == 0) {
+			/*
+			 * Pause the TID if this was successful.
+			 * An un-successful BAR TX would never call
+			 * the BAR complete / timeout methods.
+			 */
+			ath_tx_tid_pause(sc, tid);
+		} else {
+			/* BAR TX failed */
+			device_printf(sc->sc_dev,
+			    "%s: TID %d: BAR TX failed\n",
+			    __func__, tid->tid);
+		}
+	}
+
+	/* Prepend all frames to the beginning of the queue */
+	ATH_TXQ_LOCK(tid);
+	while ((bf = STAILQ_FIRST(&bf_q)) != NULL) {
+		ATH_TXQ_INSERT_HEAD(tid, bf, bf_list);
+		STAILQ_REMOVE_HEAD(&bf_q, bf_list);
+	}
+	ATH_TXQ_LOCK(tid);
+}
+
+/*
+ * Handle clean-up of packets from an aggregate list.
+ */
+static void
+ath_tx_comp_cleanup_aggr(struct ath_softc *sc, struct ath_buf *bf)
+{
+	/* XXX TODO */
+}
+
+/*
+ * Handle completion of an set of aggregate frames.
+ *
+ * XXX for now, simply complete each sub-frame.
+ *
+ * Note: the completion handler is the last descriptor in the aggregate,
+ * not the last descriptor in the first frame.
+ */
+static void
+ath_tx_aggr_comp_aggr(struct ath_softc *sc, struct ath_buf *bf, int fail)
+{
+	//struct ath_desc *ds = bf->bf_lastds;
+	struct ieee80211_node *ni = bf->bf_node;
+	struct ath_node *an = ATH_NODE(ni);
+	int tid = bf->bf_state.bfs_tid;
+	struct ath_tid *atid = &an->an_tid[tid];
+	struct ath_tx_status *ts = &bf->bf_status.ds_txstat;
+
+	/*
+	 * Punt cleanup to the relevant function, not our problem now
+	 */
+	if (atid->cleanup_inprogress) {
+		ath_tx_comp_cleanup_aggr(sc, bf);
+		return;
+	}
+
+	/*
+	 * handle errors first
+	 */
+	if (ts->ts_status & HAL_TXERR_XRETRY) {
+		ath_tx_comp_aggr_error(sc, bf, atid);
+		return;
+	}
+
+	/*
+	 * extract starting sequence and block-ack bitmap
+	 */
+
+	/* AR5416 BA bug; this requires re-TX of all frames */
+}
+
+/*
  * Handle completion of unaggregated frames in an ADDBA
  * session.
  *
  * Fail is set to 1 if the entry is being freed via a call to
  * ath_tx_draintxq().
  */
-void
-ath_tx_aggr_comp(struct ath_softc *sc, struct ath_buf *bf, int fail)
+static void
+ath_tx_aggr_comp_unaggr(struct ath_softc *sc, struct ath_buf *bf, int fail)
 {
 	struct ieee80211_node *ni = bf->bf_node;
 	struct ath_node *an = ATH_NODE(ni);
@@ -2248,7 +2397,6 @@ ath_tx_aggr_comp(struct ath_softc *sc, s
 	DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: bf=%p: tid=%d\n",
 	    __func__, bf, bf->bf_state.bfs_tid);
 
-
 	/*
 	 * If a cleanup is in progress, punt to comp_cleanup;
 	 * rather than handling it here. It's thus their
@@ -2256,7 +2404,7 @@ ath_tx_aggr_comp(struct ath_softc *sc, s
 	 * function in net80211, update rate control, etc.
 	 */
 	if (atid->cleanup_inprogress) {
-		ath_tx_comp_cleanup(sc, bf);
+		ath_tx_comp_cleanup_unaggr(sc, bf);
 		return;
 	}
 
@@ -2279,6 +2427,15 @@ ath_tx_aggr_comp(struct ath_softc *sc, s
 	/* bf is freed at this point */
 }
 
+void
+ath_tx_aggr_comp(struct ath_softc *sc, struct ath_buf *bf, int fail)
+{
+	if (bf->bf_state.bfs_aggr)
+		ath_tx_aggr_comp_aggr(sc, bf, fail);
+	else
+		ath_tx_aggr_comp_unaggr(sc, bf, fail);
+}
+
 /*
  * Schedule some packets from the given node/TID to the hardware.
  *

Modified: user/adrian/if_ath_tx/sys/dev/ath/if_ath_tx_ht.c
==============================================================================
--- user/adrian/if_ath_tx/sys/dev/ath/if_ath_tx_ht.c	Wed Aug 17 08:27:11 2011	(r224929)
+++ user/adrian/if_ath_tx/sys/dev/ath/if_ath_tx_ht.c	Wed Aug 17 09:02:58 2011	(r224930)
@@ -86,8 +86,27 @@ __FBSDID("$FreeBSD$");
 #include <dev/ath/ath_tx99/ath_tx99.h>
 #endif
 
+#include <dev/ath/if_ath_tx.h>		/* XXX for some support functions */
 #include <dev/ath/if_ath_tx_ht.h>
 
+/*
+ * XXX net80211?
+ */
+#define	IEEE80211_AMPDU_SUBFRAME_DEFAULT		32
+
+#define	ATH_AGGR_DELIM_SZ	4	/* delimiter size   */
+#define	ATH_AGGR_MINPLEN	256	/* in bytes, minimum packet length */
+#define	ATH_AGGR_ENCRYPTDELIM	10	/* number of delimiters for encryption padding */
+
+/*
+ * returns delimiter padding required given the packet length
+ */
+#define	ATH_AGGR_GET_NDELIM(_len)					\
+	    (((((_len) + ATH_AGGR_DELIM_SZ) < ATH_AGGR_MINPLEN) ?	\
+	    (ATH_AGGR_MINPLEN - (_len) - ATH_AGGR_DELIM_SZ) : 0) >> 2)
+
+#define	PADBYTES(_len)		((4 - ((_len) % 4)) % 4)
+
 int ath_max_4ms_framelen[4][32] = {
 	[MCS_HT20] = {
 		3212,  6432,  9648,  12864,  19300,  25736,  28952,  32172,
@@ -251,7 +270,7 @@ ath_buf_set_rate(struct ath_softc *sc, s
 	/* Enforce RTS and CTS are mutually exclusive */
 
 	/* Get a pointer to the last tx descriptor in the list */
-	lastds = &bf->bf_desc[bf->bf_nseg - 1];
+	lastds = bf->bf_lastds;
 
 #if 0
 	printf("pktlen: %d; flags 0x%x\n", pktlen, flags);
@@ -274,3 +293,197 @@ ath_buf_set_rate(struct ath_softc *sc, s
 	/* This should only be done if aggregate protection is enabled */
 	//ath_hal_set11nburstduration(ah, ds, 8192);
 }
+
+/*
+ * Form an aggregate packet list.
+ *
+ * This function enforces the aggregate restrictions/requirements.
+ *
+ * These are:
+ *
+ * + The aggregate size maximum (64k for AR9160 and later, 8K for
+ *   AR5416 when doing RTS frame protection.)
+ * + Maximum number of sub-frames for an aggregate
+ * + The aggregate delimiter size, giving MACs time to do whatever is
+ *   needed before each frame
+ * + Enforce the BAW limit
+ *
+ * Each descriptor queued should already have the TX fields setup,
+ * the DMA map setup and the rate control series setup. Since
+ * aggregate sub-frame retransmission is done entirely in software,
+ * it should only have a single rate series configured.
+ *
+ * The first descriptor has the rate control and aggregate setup
+ * fields; the middle frames have "aggregate" and "more" flags set
+ * along with the delimiter count; the last frame has "aggregate"
+ * set.
+ *
+ * Note that the TID lock is only grabbed when dequeuing packets from
+ * the TID queue. If some code in another thread adds to the head of this
+ * list, very strange behaviour will occur. Since retransmission is the
+ * only reason this will occur, and this routine is designed to be called
+ * from within the scheduler task, it won't ever clash with the completion
+ * task.
+ *
+ * So if you want to call this from an upper layer context (eg, to direct-
+ * dispatch aggregate frames to the hardware), please keep this in mind.
+ */
+ATH_AGGR_STATUS
+ath_tx_form_aggr(struct ath_softc *sc, struct ath_node *an, struct ath_tid *tid,
+    ath_bufhead *bf_q)
+{
+	//struct ieee80211_node *ni = &an->an_node;
+	struct ath_buf *bf, *bf_first = NULL, *bf_prev = NULL;
+	int nframes = 0;
+	uint16_t aggr_limit = 0, al = 0, bpad = 0, al_delta, h_baw;
+	struct ieee80211_tx_ampdu *tap;
+	int status = ATH_AGGR_DONE;
+	int prev_frames = 0;	/* XXX for AR5416 burst, not done here */
+	int prev_al = 0;	/* XXX also for AR5416 burst */
+
+	tap = ath_tx_get_tx_tid(an, tid->tid);
+	if (tap == NULL) {
+		status = ATH_AGGR_ERROR;
+		goto finish;
+	}
+
+	h_baw = tap->txa_wnd / 2;
+
+	/* Calculate aggregation limit */
+	aggr_limit = 8192;		/* XXX just for now, for testing */
+
+	for (;;) {
+		ATH_TXQ_LOCK(tid);
+		bf = STAILQ_FIRST(&tid->axq_q);
+		if (bf_first == NULL)
+			bf_first = bf;
+		if (bf == NULL) {
+			ATH_TXQ_UNLOCK(tid);
+			status = ATH_AGGR_DONE;
+			break;
+		}
+
+		/*
+		 * Don't unlock the tid lock until we're sure we are going
+		 * to queue this frame.
+		 */
+
+		/*
+		 * If the frame doesn't have a sequence number, we can't
+		 * aggregate it.
+		 */
+		if (! bf->bf_state.bfs_dobaw) {
+			ATH_TXQ_UNLOCK(tid);
+			status = ATH_AGGR_NONAGGR;
+			break;
+		}
+
+		/*
+		 * If the packet has a sequence number, do not
+		 * step outside of the block-ack window.
+		 */
+		if (! BAW_WITHIN(tap->txa_start, tap->txa_wnd,
+		    SEQNO(bf->bf_state.bfs_seqno))) {
+		    ATH_TXQ_UNLOCK(tid);
+		    status = ATH_AGGR_BAW_CLOSED;
+		    break;
+		}
+
+		/*
+		 * do not exceed aggregation limit
+		 */
+		al_delta = ATH_AGGR_DELIM_SZ + bf->bf_state.bfs_pktlen;
+
+		/*
+		 * XXX TODO: AR5416 has an 8K aggregation size limit
+		 * when RTS is enabled, and RTS is required for dual-stream
+		 * rates.
+		 *
+		 * For now, limit all aggregates for the AR5416 to be 8K.
+		 */
+
+		if (nframes &&
+		    (aggr_limit < (al + bpad + al_delta + prev_al))) {
+			ATH_TXQ_UNLOCK(tid);
+			status = ATH_AGGR_LIMITED;
+			break;
+		}
+
+		/*
+		 * Do not exceed subframe limit.
+		 */
+		if ((nframes + prev_frames) >= MIN((h_baw),
+		    IEEE80211_AMPDU_SUBFRAME_DEFAULT)) {
+			ATH_TXQ_UNLOCK(tid);
+			status = ATH_AGGR_LIMITED;
+			break;
+		}
+
+		/*
+		 * XXX TODO: do not exceed 4ms packet length
+		 */
+
+		/*
+		 * this packet is part of an aggregate.
+		 */
+		ATH_TXQ_REMOVE_HEAD(tid, bf_list);
+		ATH_TXQ_UNLOCK(tid);
+
+		ath_tx_addto_baw(sc, an, tid, bf);
+		STAILQ_INSERT_TAIL(bf_q, bf, bf_list);
+		nframes ++;
+
+		/* Completion handler */
+		bf->bf_comp = ath_tx_aggr_comp;
+
+		/*
+		 * add padding for previous frame to aggregation length
+		 */
+		al += bpad + al_delta;
+		bf->bf_state.bfs_ndelim =
+		    ATH_AGGR_GET_NDELIM(bf->bf_state.bfs_pktlen);
+
+		/*
+		 * Add further padding if encryption is required
+		 * XXX for now, always add it.
+		 */
+		bf->bf_state.bfs_ndelim += ATH_AGGR_ENCRYPTDELIM;
+
+		bpad = PADBYTES(al_delta) + (bf->bf_state.bfs_ndelim << 2);
+
+		/*
+		 * link current buffer to the aggregate
+		 */
+		if (bf_prev) {
+			bf_prev->bf_next = bf;
+			bf_prev->bf_desc->ds_link = bf->bf_daddr;
+		}
+		bf_prev = bf;
+
+		/* Set aggregate flags */
+		ath_hal_set11naggrmiddle(sc->sc_ah, bf->bf_desc,
+		    bf->bf_state.bfs_ndelim);
+
+#if 0
+		/*
+		 * terminate aggregation on a small packet boundary
+		 */
+		if (bf->bf_state.bfs_pktlen < ATH_AGGR_MINPLEN) {
+			status = ATH_AGGR_SHORTPKT;
+			break;
+		}
+#endif
+
+	}
+
+finish:
+	/*
+	 * Just in case the list was empty when we tried to
+	 * dequeue a packet ..
+	 */
+	if (bf_first) {
+		bf_first->bf_state.bfs_al = al;
+		bf_first->bf_state.bfs_nframes = nframes;
+	}
+	return status;
+}

Modified: user/adrian/if_ath_tx/sys/dev/ath/if_ath_tx_ht.h
==============================================================================
--- user/adrian/if_ath_tx/sys/dev/ath/if_ath_tx_ht.h	Wed Aug 17 08:27:11 2011	(r224929)
+++ user/adrian/if_ath_tx/sys/dev/ath/if_ath_tx_ht.h	Wed Aug 17 09:02:58 2011	(r224930)
@@ -38,9 +38,23 @@ enum {
 	MCS_HT40_SGI,
 };
 
+typedef enum {
+	ATH_AGGR_DONE,
+	ATH_AGGR_BAW_CLOSED,
+	ATH_AGGR_LIMITED,
+	ATH_AGGR_SHORTPKT,
+	ATH_AGGR_8K_LIMITED,
+	ATH_AGGR_ERROR,
+	ATH_AGGR_NONAGGR,
+} ATH_AGGR_STATUS;
+
 extern int	ath_max_4ms_framelen[4][32];
 
 extern void	ath_buf_set_rate(struct ath_softc *sc,
 		struct ieee80211_node *ni, struct ath_buf *bf);
 
+extern ATH_AGGR_STATUS
+	    ath_tx_form_aggr(struct ath_softc *sc, struct ath_node *an,
+	    struct ath_tid *tid, ath_bufhead *bf_q);
+
 #endif

Modified: user/adrian/if_ath_tx/sys/dev/ath/if_athvar.h
==============================================================================
--- user/adrian/if_ath_tx/sys/dev/ath/if_athvar.h	Wed Aug 17 08:27:11 2011	(r224929)
+++ user/adrian/if_ath_tx/sys/dev/ath/if_athvar.h	Wed Aug 17 09:02:58 2011	(r224930)
@@ -181,6 +181,7 @@ struct ath_buf {
 	bus_dmamap_t		bf_dmamap;	/* DMA map for mbuf chain */
 	struct mbuf		*bf_m;		/* mbuf for buf */
 	struct ieee80211_node	*bf_node;	/* pointer to the node */
+	struct ath_desc		*bf_lastds;	/* last descriptor for comp status */
 	bus_size_t		bf_mapsize;
 #define	ATH_MAX_SCATTER		ATH_TXDESC	/* max(tx,rx,beacon) desc's */
 	bus_dma_segment_t	bf_segs[ATH_MAX_SCATTER];



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201108170902.p7H92w3T095282>