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>