Deleted Added
full compact
32c32
< __FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_tx.c 240592 2012-09-17 03:17:42Z adrian $");
---
> __FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_tx.c 240639 2012-09-18 10:14:17Z adrian $");
116a117,119
> static struct ath_buf *
> ath_tx_retry_clone(struct ath_softc *sc, struct ath_node *an,
> struct ath_tid *tid, struct ath_buf *bf);
147a151,166
> static void
> ath_tx_set_retry(struct ath_softc *sc, struct ath_buf *bf)
> {
> struct ieee80211_frame *wh;
>
> wh = mtod(bf->bf_m, struct ieee80211_frame *);
> /* Only update/resync if needed */
> if (bf->bf_state.bfs_isretried == 0) {
> wh->i_fc[1] |= IEEE80211_FC1_RETRY;
> bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
> BUS_DMASYNC_PREWRITE);
> }
> bf->bf_state.bfs_isretried = 1;
> bf->bf_state.bfs_retries ++;
> }
>
2713a2733,2736
>
> /* XXX now with this bzer(), is the field 0'ing needed? */
> bzero(atid, sizeof(*atid));
>
2714a2738
> TAILQ_INIT(&atid->filtq.axq_q);
2723a2748
> atid->clrdmask = 1; /* Always start by setting this bit */
2764a2790,2795
> /* XXX isfiltered shouldn't ever be 0 at this point */
> if (tid->isfiltered == 1) {
> device_printf(sc->sc_dev, "%s: filtered?!\n", __func__);
> return;
> }
>
2771a2803,2988
> * Add the given ath_buf to the TID filtered frame list.
> * This requires the TID be filtered.
> */
> static void
> ath_tx_tid_filt_addbuf(struct ath_softc *sc, struct ath_tid *tid,
> struct ath_buf *bf)
> {
>
> ATH_TID_LOCK_ASSERT(sc, tid);
> if (! tid->isfiltered)
> device_printf(sc->sc_dev, "%s: not filtered?!\n", __func__);
>
> DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: bf=%p\n", __func__, bf);
>
> /* Set the retry bit and bump the retry counter */
> ath_tx_set_retry(sc, bf);
> sc->sc_stats.ast_tx_swfiltered++;
>
> ATH_TXQ_INSERT_TAIL(&tid->filtq, bf, bf_list);
> }
>
> /*
> * Handle a completed filtered frame from the given TID.
> * This just enables/pauses the filtered frame state if required
> * and appends the filtered frame to the filtered queue.
> */
> static void
> ath_tx_tid_filt_comp_buf(struct ath_softc *sc, struct ath_tid *tid,
> struct ath_buf *bf)
> {
>
> ATH_TID_LOCK_ASSERT(sc, tid);
>
> if (! tid->isfiltered) {
> DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: filter transition\n",
> __func__);
> tid->isfiltered = 1;
> ath_tx_tid_pause(sc, tid);
> }
>
> /* Add the frame to the filter queue */
> ath_tx_tid_filt_addbuf(sc, tid, bf);
> }
>
> /*
> * Complete the filtered frame TX completion.
> *
> * If there are no more frames in the hardware queue, unpause/unfilter
> * the TID if applicable. Otherwise we will wait for a node PS transition
> * to unfilter.
> */
> static void
> ath_tx_tid_filt_comp_complete(struct ath_softc *sc, struct ath_tid *tid)
> {
> struct ath_buf *bf;
>
> ATH_TID_LOCK_ASSERT(sc, tid);
>
> if (tid->hwq_depth != 0)
> return;
>
> DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: hwq=0, transition back\n",
> __func__);
> tid->isfiltered = 0;
> tid->clrdmask = 1;
>
> /* XXX this is really quite inefficient */
> while ((bf = TAILQ_LAST(&tid->filtq.axq_q, ath_bufhead_s)) != NULL) {
> ATH_TXQ_REMOVE(&tid->filtq, bf, bf_list);
> ATH_TXQ_INSERT_HEAD(tid, bf, bf_list);
> }
>
> ath_tx_tid_resume(sc, tid);
> }
>
> /*
> * Called when a single (aggregate or otherwise) frame is completed.
> *
> * Returns 1 if the buffer could be added to the filtered list
> * (cloned or otherwise), 0 if the buffer couldn't be added to the
> * filtered list (failed clone; expired retry) and the caller should
> * free it and handle it like a failure (eg by sending a BAR.)
> */
> static int
> ath_tx_tid_filt_comp_single(struct ath_softc *sc, struct ath_tid *tid,
> struct ath_buf *bf)
> {
> struct ath_buf *nbf;
> int retval;
>
> ATH_TID_LOCK_ASSERT(sc, tid);
>
> /*
> * Don't allow a filtered frame to live forever.
> */
> if (bf->bf_state.bfs_retries > SWMAX_RETRIES) {
> DPRINTF(sc, ATH_DEBUG_SW_TX_FILT,
> "%s: bf=%p, seqno=%d, exceeded retries\n",
> __func__,
> bf,
> bf->bf_state.bfs_seqno);
> return (0);
> }
>
> /*
> * A busy buffer can't be added to the retry list.
> * It needs to be cloned.
> */
> if (bf->bf_flags & ATH_BUF_BUSY) {
> nbf = ath_tx_retry_clone(sc, tid->an, tid, bf);
> DPRINTF(sc, ATH_DEBUG_SW_TX_FILT,
> "%s: busy buffer clone: %p -> %p\n",
> __func__, bf, nbf);
> } else {
> nbf = bf;
> }
>
> if (nbf == NULL) {
> DPRINTF(sc, ATH_DEBUG_SW_TX_FILT,
> "%s: busy buffer couldn't be cloned (%p)!\n",
> __func__, bf);
> retval = 1;
> } else {
> ath_tx_tid_filt_comp_buf(sc, tid, nbf);
> retval = 0;
> }
> ath_tx_tid_filt_comp_complete(sc, tid);
>
> return (retval);
> }
>
> static void
> ath_tx_tid_filt_comp_aggr(struct ath_softc *sc, struct ath_tid *tid,
> struct ath_buf *bf_first, ath_bufhead *bf_q)
> {
> struct ath_buf *bf, *bf_next, *nbf;
>
> ATH_TID_LOCK_ASSERT(sc, tid);
>
> bf = bf_first;
> while (bf) {
> bf_next = bf->bf_next;
> bf->bf_next = NULL; /* Remove it from the aggr list */
>
> /*
> * Don't allow a filtered frame to live forever.
> */
> if (bf->bf_state.bfs_retries > SWMAX_RETRIES) {
> DPRINTF(sc, ATH_DEBUG_SW_TX_FILT,
> "%s: bf=%p, seqno=%d, exceeded retries\n",
> __func__,
> bf,
> bf->bf_state.bfs_seqno);
> TAILQ_INSERT_TAIL(bf_q, bf, bf_list);
> goto next;
> }
>
> if (bf->bf_flags & ATH_BUF_BUSY) {
> nbf = ath_tx_retry_clone(sc, tid->an, tid, bf);
> DPRINTF(sc, ATH_DEBUG_SW_TX_FILT,
> "%s: busy buffer cloned: %p -> %p",
> __func__, bf, nbf);
> } else {
> nbf = bf;
> }
>
> /*
> * If the buffer couldn't be cloned, add it to bf_q;
> * the caller will free the buffer(s) as required.
> */
> if (nbf == NULL) {
> DPRINTF(sc, ATH_DEBUG_SW_TX_FILT,
> "%s: buffer couldn't be cloned! (%p)\n",
> __func__, bf);
> TAILQ_INSERT_TAIL(bf_q, bf, bf_list);
> } else {
> ath_tx_tid_filt_comp_buf(sc, tid, nbf);
> }
> next:
> bf = bf_next;
> }
>
> ath_tx_tid_filt_comp_complete(sc, tid);
> }
>
> /*
2926a3144,3147
> static void
> ath_tx_tid_drain_pkt(struct ath_softc *sc, struct ath_node *an,
> struct ath_tid *tid, ath_bufhead *bf_cq, struct ath_buf *bf)
> {
2927a3149,3218
> ATH_TID_LOCK_ASSERT(sc, tid);
>
> /*
> * If the current TID is running AMPDU, update
> * the BAW.
> */
> if (ath_tx_ampdu_running(sc, an, tid->tid) &&
> bf->bf_state.bfs_dobaw) {
> /*
> * Only remove the frame from the BAW if it's
> * been transmitted at least once; this means
> * the frame was in the BAW to begin with.
> */
> if (bf->bf_state.bfs_retries > 0) {
> ath_tx_update_baw(sc, an, tid, bf);
> bf->bf_state.bfs_dobaw = 0;
> }
> /*
> * This has become a non-fatal error now
> */
> if (! bf->bf_state.bfs_addedbaw)
> device_printf(sc->sc_dev,
> "%s: wasn't added: seqno %d\n",
> __func__, SEQNO(bf->bf_state.bfs_seqno));
> }
> TAILQ_INSERT_TAIL(bf_cq, bf, bf_list);
> }
>
> static void
> ath_tx_tid_drain_print(struct ath_softc *sc, struct ath_node *an,
> struct ath_tid *tid, struct ath_buf *bf)
> {
> struct ieee80211_node *ni = &an->an_node;
> struct ath_txq *txq = sc->sc_ac2q[tid->ac];
> struct ieee80211_tx_ampdu *tap;
>
> tap = ath_tx_get_tx_tid(an, tid->tid);
>
> device_printf(sc->sc_dev,
> "%s: node %p: bf=%p: addbaw=%d, dobaw=%d, "
> "seqno=%d, retry=%d\n",
> __func__, ni, bf,
> bf->bf_state.bfs_addedbaw,
> bf->bf_state.bfs_dobaw,
> SEQNO(bf->bf_state.bfs_seqno),
> bf->bf_state.bfs_retries);
> device_printf(sc->sc_dev,
> "%s: node %p: bf=%p: tid txq_depth=%d hwq_depth=%d, bar_wait=%d, isfiltered=%d\n",
> __func__, ni, bf,
> tid->axq_depth,
> tid->hwq_depth,
> tid->bar_wait,
> tid->isfiltered);
> device_printf(sc->sc_dev,
> "%s: node %p: tid %d: txq_depth=%d, "
> "txq_aggr_depth=%d, sched=%d, paused=%d, "
> "hwq_depth=%d, incomp=%d, baw_head=%d, "
> "baw_tail=%d txa_start=%d, ni_txseqs=%d\n",
> __func__, ni, tid->tid, txq->axq_depth,
> txq->axq_aggr_depth, tid->sched, tid->paused,
> tid->hwq_depth, tid->incomp, tid->baw_head,
> tid->baw_tail, tap == NULL ? -1 : tap->txa_start,
> ni->ni_txseqs[tid->tid]);
>
> /* XXX Dump the frame, see what it is? */
> ieee80211_dump_pkt(ni->ni_ic,
> mtod(bf->bf_m, const uint8_t *),
> bf->bf_m->m_len, 0, -1);
> }
>
2950,2951c3241
< int t = 0;
< struct ath_txq *txq = sc->sc_ac2q[tid->ac];
---
> int t;
2955c3245
< ATH_TXQ_LOCK_ASSERT(sc->sc_ac2q[tid->ac]);
---
> ATH_TID_LOCK_ASSERT(sc, tid);
2957a3248
> t = 0;
2965,2994c3256
< device_printf(sc->sc_dev,
< "%s: node %p: bf=%p: addbaw=%d, dobaw=%d, "
< "seqno=%d, retry=%d\n",
< __func__, ni, bf,
< bf->bf_state.bfs_addedbaw,
< bf->bf_state.bfs_dobaw,
< SEQNO(bf->bf_state.bfs_seqno),
< bf->bf_state.bfs_retries);
< device_printf(sc->sc_dev,
< "%s: node %p: bf=%p: tid txq_depth=%d hwq_depth=%d, bar_wait=%d\n",
< __func__, ni, bf,
< tid->axq_depth,
< tid->hwq_depth,
< tid->bar_wait);
< device_printf(sc->sc_dev,
< "%s: node %p: tid %d: txq_depth=%d, "
< "txq_aggr_depth=%d, sched=%d, paused=%d, "
< "hwq_depth=%d, incomp=%d, baw_head=%d, "
< "baw_tail=%d txa_start=%d, ni_txseqs=%d\n",
< __func__, ni, tid->tid, txq->axq_depth,
< txq->axq_aggr_depth, tid->sched, tid->paused,
< tid->hwq_depth, tid->incomp, tid->baw_head,
< tid->baw_tail, tap == NULL ? -1 : tap->txa_start,
< ni->ni_txseqs[tid->tid]);
<
< /* XXX Dump the frame, see what it is? */
< ieee80211_dump_pkt(ni->ni_ic,
< mtod(bf->bf_m, const uint8_t *),
< bf->bf_m->m_len, 0, -1);
<
---
> ath_tx_tid_drain_print(sc, an, tid, bf);
2997a3260,3262
> ATH_TXQ_REMOVE(tid, bf, bf_list);
> ath_tx_tid_drain_pkt(sc, an, tid, bf_cq, bf);
> }
2999,3020c3264,3273
< /*
< * If the current TID is running AMPDU, update
< * the BAW.
< */
< if (ath_tx_ampdu_running(sc, an, tid->tid) &&
< bf->bf_state.bfs_dobaw) {
< /*
< * Only remove the frame from the BAW if it's
< * been transmitted at least once; this means
< * the frame was in the BAW to begin with.
< */
< if (bf->bf_state.bfs_retries > 0) {
< ath_tx_update_baw(sc, an, tid, bf);
< bf->bf_state.bfs_dobaw = 0;
< }
< /*
< * This has become a non-fatal error now
< */
< if (! bf->bf_state.bfs_addedbaw)
< device_printf(sc->sc_dev,
< "%s: wasn't added: seqno %d\n",
< __func__, SEQNO(bf->bf_state.bfs_seqno));
---
> /* And now, drain the filtered frame queue */
> t = 0;
> for (;;) {
> bf = TAILQ_FIRST(&tid->filtq.axq_q);
> if (bf == NULL)
> break;
>
> if (t == 0) {
> ath_tx_tid_drain_print(sc, an, tid, bf);
> t = 1;
3022,3023c3275,3277
< ATH_TXQ_REMOVE(tid, bf, bf_list);
< TAILQ_INSERT_TAIL(bf_cq, bf, bf_list);
---
>
> ATH_TXQ_REMOVE(&tid->filtq, bf, bf_list);
> ath_tx_tid_drain_pkt(sc, an, tid, bf_cq, bf);
3136a3391,3395
>
> if (atid->isfiltered)
> device_printf(sc->sc_dev, "%s: isfiltered=1, normal_comp?\n",
> __func__);
>
3139a3399,3413
>
> /*
> * If the queue is filtered, potentially mark it as complete
> * and reschedule it as needed.
> *
> * This is required as there may be a subsequent TX descriptor
> * for this end-node that has CLRDMASK set, so it's quite possible
> * that a filtered frame will be followed by a non-filtered
> * (complete or otherwise) frame.
> *
> * XXX should we do this before we complete the frame?
> */
> if (atid->isfiltered)
> ath_tx_tid_filt_comp_complete(sc, atid);
>
3211a3486,3495
> * Move the filtered frames to the TX queue, before
> * we run off and discard/process things.
> */
> /* XXX this is really quite inefficient */
> while ((bf = TAILQ_LAST(&atid->filtq.axq_q, ath_bufhead_s)) != NULL) {
> ATH_TXQ_REMOVE(&atid->filtq, bf, bf_list);
> ATH_TXQ_INSERT_HEAD(atid, bf, bf_list);
> }
>
> /*
3290,3306d3573
< static void
< ath_tx_set_retry(struct ath_softc *sc, struct ath_buf *bf)
< {
< struct ieee80211_frame *wh;
<
< wh = mtod(bf->bf_m, struct ieee80211_frame *);
< /* Only update/resync if needed */
< if (bf->bf_state.bfs_isretried == 0) {
< wh->i_fc[1] |= IEEE80211_FC1_RETRY;
< bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
< BUS_DMASYNC_PREWRITE);
< }
< sc->sc_stats.ast_tx_swretries++;
< bf->bf_state.bfs_isretried = 1;
< bf->bf_state.bfs_retries ++;
< }
<
3354a3622
>
3435a3704
> sc->sc_stats.ast_tx_swretries++;
3470a3740
>
3506a3777
> sc->sc_stats.ast_tx_swretries++;
3592a3864
>
3635a3908
> /* XXX why would we send a BAR when transitioning to non-aggregation? */
3637a3911
>
3683a3958,3960
> TAILQ_INIT(&bf_q);
> TAILQ_INIT(&bf_cq);
>
3692a3970,3977
> * If the TID is filtered, handle completing the filter
> * transition before potentially kicking it to the cleanup
> * function.
> */
> if (atid->isfiltered)
> ath_tx_tid_filt_comp_complete(sc, atid);
>
> /*
3695a3981,3984
> if (atid->isfiltered)
> device_printf(sc->sc_dev,
> "%s: isfiltered=1, normal_comp?\n",
> __func__);
3701a3991,4032
> * If the frame is filtered, transition to filtered frame
> * mode and add this to the filtered frame list.
> *
> * XXX TODO: figure out how this interoperates with
> * BAR, pause and cleanup states.
> */
> if ((ts.ts_status & HAL_TXERR_FILT) ||
> (ts.ts_status != 0 && atid->isfiltered)) {
> if (fail != 0)
> device_printf(sc->sc_dev,
> "%s: isfiltered=1, fail=%d\n", __func__, fail);
> ath_tx_tid_filt_comp_aggr(sc, atid, bf_first, &bf_cq);
>
> /* Remove from BAW */
> TAILQ_FOREACH_SAFE(bf, &bf_cq, bf_list, bf_next) {
> if (bf->bf_state.bfs_addedbaw)
> drops++;
> if (bf->bf_state.bfs_dobaw) {
> ath_tx_update_baw(sc, an, atid, bf);
> if (! bf->bf_state.bfs_addedbaw)
> device_printf(sc->sc_dev,
> "%s: wasn't added: seqno %d\n",
> __func__,
> SEQNO(bf->bf_state.bfs_seqno));
> }
> bf->bf_state.bfs_dobaw = 0;
> }
> /*
> * If any intermediate frames in the BAW were dropped when
> * handling filtering things, send a BAR.
> */
> if (drops)
> ath_tx_tid_bar_suspend(sc, atid);
>
> /*
> * Finish up by sending a BAR if required and freeing
> * the frames outside of the TX lock.
> */
> goto finish_send_bar;
> }
>
> /*
3728,3729d4058
< TAILQ_INIT(&bf_q);
< TAILQ_INIT(&bf_cq);
3885a4215,4229
> * If the queue is filtered, re-schedule as required.
> *
> * This is required as there may be a subsequent TX descriptor
> * for this end-node that has CLRDMASK set, so it's quite possible
> * that a filtered frame will be followed by a non-filtered
> * (complete or otherwise) frame.
> *
> * XXX should we do this before we complete the frame?
> */
> if (atid->isfiltered)
> ath_tx_tid_filt_comp_complete(sc, atid);
>
> finish_send_bar:
>
> /*
3914a4259
> int drops = 0;
3948a4294,4301
> * If the TID is filtered, handle completing the filter
> * transition before potentially kicking it to the cleanup
> * function.
> */
> if (atid->isfiltered)
> ath_tx_tid_filt_comp_complete(sc, atid);
>
> /*
3954a4308,4311
> if (atid->isfiltered)
> device_printf(sc->sc_dev,
> "%s: isfiltered=1, normal_comp?\n",
> __func__);
3962a4320,4379
> * XXX TODO: how does cleanup, BAR and filtered frame handling
> * overlap?
> *
> * If the frame is filtered OR if it's any failure but
> * the TID is filtered, the frame must be added to the
> * filtered frame list.
> *
> * However - a busy buffer can't be added to the filtered
> * list as it will end up being recycled without having
> * been made available for the hardware.
> */
> if ((ts->ts_status & HAL_TXERR_FILT) ||
> (ts->ts_status != 0 && atid->isfiltered)) {
> int freeframe;
>
> if (fail != 0)
> device_printf(sc->sc_dev,
> "%s: isfiltered=1, fail=%d\n",
> __func__,
> fail);
> freeframe = ath_tx_tid_filt_comp_single(sc, atid, bf);
> if (freeframe) {
> /* Remove from BAW */
> if (bf->bf_state.bfs_addedbaw)
> drops++;
> if (bf->bf_state.bfs_dobaw) {
> ath_tx_update_baw(sc, an, atid, bf);
> if (! bf->bf_state.bfs_addedbaw)
> device_printf(sc->sc_dev,
> "%s: wasn't added: seqno %d\n",
> __func__, SEQNO(bf->bf_state.bfs_seqno));
> }
> bf->bf_state.bfs_dobaw = 0;
> }
>
> /*
> * If the frame couldn't be filtered, treat it as a drop and
> * prepare to send a BAR.
> */
> if (freeframe && drops)
> ath_tx_tid_bar_suspend(sc, atid);
>
> /*
> * Send BAR if required
> */
> if (ath_tx_tid_bar_tx_ready(sc, atid))
> ath_tx_tid_bar_tx(sc, atid);
>
> ATH_TXQ_UNLOCK(sc->sc_ac2q[atid->ac]);
> /*
> * If freeframe is set, then the frame couldn't be
> * cloned and bf is still valid. Just complete/free it.
> */
> if (freeframe)
> ath_tx_default_comp(sc, bf, fail);
>
>
> return;
> }
> /*
3989a4407,4419
> * If the queue is filtered, re-schedule as required.
> *
> * This is required as there may be a subsequent TX descriptor
> * for this end-node that has CLRDMASK set, so it's quite possible
> * that a filtered frame will be followed by a non-filtered
> * (complete or otherwise) frame.
> *
> * XXX should we do this before we complete the frame?
> */
> if (atid->isfiltered)
> ath_tx_tid_filt_comp_complete(sc, atid);
>
> /*