svn commit: r224930 - user/adrian/if_ath_tx/sys/dev/ath
Adrian Chadd
adrian at FreeBSD.org
Wed Aug 17 09:02:58 UTC 2011
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];
More information about the svn-src-user
mailing list