From 8f11253cd8dc76da7fac7f9e18ee4917ba4d98a3 Mon Sep 17 00:00:00 2001 From: adrian Date: Mon, 13 May 2013 19:02:22 +0000 Subject: Oops, commit the other half of r250606. --- sys/dev/ath/if_ath_tx.c | 161 ++++++++++++++++++++++++++++++++++++++++-------- sys/dev/ath/if_ath_tx.h | 1 + 2 files changed, 135 insertions(+), 27 deletions(-) (limited to 'sys/dev') diff --git a/sys/dev/ath/if_ath_tx.c b/sys/dev/ath/if_ath_tx.c index 5753e59..3664160 100644 --- a/sys/dev/ath/if_ath_tx.c +++ b/sys/dev/ath/if_ath_tx.c @@ -3556,6 +3556,72 @@ ath_tx_tid_drain(struct ath_softc *sc, struct ath_node *an, } /* + * Reset the TID state. This must be only called once the node has + * had its frames flushed from this TID, to ensure that no other + * pause / unpause logic can kick in. + */ +static void +ath_tx_tid_reset(struct ath_softc *sc, struct ath_tid *tid) +{ + +#if 0 + tid->bar_wait = tid->bar_tx = tid->isfiltered = 0; + tid->paused = tid->sched = tid->addba_tx_pending = 0; + tid->incomp = tid->cleanup_inprogress = 0; +#endif + + /* + * If we have a bar_wait set, we need to unpause the TID + * here. Otherwise once cleanup has finished, the TID won't + * have the right paused counter. + * + * XXX I'm not going through resume here - I don't want the + * node to be rescheuled just yet. This however should be + * methodized! + */ + if (tid->bar_wait) { + if (tid->paused > 0) { + tid->paused --; + } + } + + /* + * XXX same with a currently filtered TID. + * + * Since this is being called during a flush, we assume that + * the filtered frame list is actually empty. + * + * XXX TODO: add in a check to ensure that the filtered queue + * depth is actually 0! + */ + if (tid->isfiltered) { + if (tid->paused > 0) { + tid->paused --; + } + } + + /* + * Clear BAR, filtered frames, scheduled and ADDBA pending. + * The TID may be going through cleanup from the last association + * where things in the BAW are still in the hardware queue. + */ + tid->bar_wait = 0; + tid->bar_tx = 0; + tid->isfiltered = 0; + tid->sched = 0; + tid->addba_tx_pending = 0; + + /* + * XXX TODO: it may just be enough to walk the HWQs and mark + * frames for that node as non-aggregate; or mark the ath_node + * with something that indicates that aggregation is no longer + * occuring. Then we can just toss the BAW complaints and + * do a complete hard reset of state here - no pause, no + * complete counter, etc. + */ +} + +/* * Flush all software queued packets for the given node. * * This occurs when a completion handler frees the last buffer @@ -3582,6 +3648,8 @@ ath_tx_node_flush(struct ath_softc *sc, struct ath_node *an) ath_tx_tid_drain(sc, an, atid, &bf_cq); /* Remove this tid from the list of active tids */ ath_tx_tid_unsched(sc, atid); + /* Reset the per-TID pause, BAR, etc state */ + ath_tx_tid_reset(sc, atid); } ATH_TX_UNLOCK(sc); @@ -3748,19 +3816,18 @@ ath_tx_comp_cleanup_unaggr(struct ath_softc *sc, struct ath_buf *bf) * The caller is responsible for pausing the TID. */ static void -ath_tx_tid_cleanup(struct ath_softc *sc, struct ath_node *an, int tid) +ath_tx_tid_cleanup(struct ath_softc *sc, struct ath_node *an, int tid, + ath_bufhead *bf_cq) { struct ath_tid *atid = &an->an_tid[tid]; struct ieee80211_tx_ampdu *tap; struct ath_buf *bf, *bf_next; - ath_bufhead bf_cq; + + ATH_TX_LOCK_ASSERT(sc); DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, "%s: TID %d: called\n", __func__, tid); - TAILQ_INIT(&bf_cq); - ATH_TX_LOCK(sc); - /* * Move the filtered frames to the TX queue, before * we run off and discard/process things. @@ -3795,7 +3862,7 @@ ath_tx_tid_cleanup(struct ath_softc *sc, struct ath_node *an, int tid) * Call the default completion handler with "fail" just * so upper levels are suitably notified about this. */ - TAILQ_INSERT_TAIL(&bf_cq, bf, bf_list); + TAILQ_INSERT_TAIL(bf_cq, bf, bf_list); bf = bf_next; continue; } @@ -3840,13 +3907,8 @@ ath_tx_tid_cleanup(struct ath_softc *sc, struct ath_node *an, int tid) DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: TID %d: cleanup needed: %d packets\n", __func__, tid, atid->incomp); - ATH_TX_UNLOCK(sc); - /* Handle completing frames and fail them */ - while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) { - TAILQ_REMOVE(&bf_cq, bf, bf_list); - ath_tx_default_comp(sc, bf, 1); - } + /* Owner now must free completed frames */ } static struct ath_buf * @@ -5284,6 +5346,8 @@ ath_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) int tid = tap->txa_tid; struct ath_node *an = ATH_NODE(ni); struct ath_tid *atid = &an->an_tid[tid]; + ath_bufhead bf_cq; + struct ath_buf *bf; DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: called\n", __func__); @@ -5312,7 +5376,57 @@ ath_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) * it'll set the cleanup flag, and it'll be unpaused once * things have been cleaned up. */ - ath_tx_tid_cleanup(sc, an, tid); + TAILQ_INIT(&bf_cq); + ATH_TX_LOCK(sc); + ath_tx_tid_cleanup(sc, an, tid, &bf_cq); + ATH_TX_UNLOCK(sc); + + /* Handle completing frames and fail them */ + while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) { + TAILQ_REMOVE(&bf_cq, bf, bf_list); + ath_tx_default_comp(sc, bf, 1); + } +} + +/* + * Handle a node reassociation. + * + * We may have a bunch of frames queued to the hardware; those need + * to be marked as cleanup. + */ +void +ath_tx_node_reassoc(struct ath_softc *sc, struct ath_node *an) +{ + struct ath_tid *tid; + int i; + ath_bufhead bf_cq; + struct ath_buf *bf; + + TAILQ_INIT(&bf_cq); + + ATH_TX_UNLOCK_ASSERT(sc); + + ATH_TX_LOCK(sc); + for (i = 0; i < IEEE80211_TID_SIZE; i++) { + tid = &an->an_tid[i]; + if (tid->hwq_depth == 0) + continue; + ath_tx_tid_pause(sc, tid); + DPRINTF(sc, ATH_DEBUG_NODE, + "%s: %6D: TID %d: cleaning up TID\n", + __func__, + an->an_node.ni_macaddr, + ":", + i); + ath_tx_tid_cleanup(sc, an, i, &bf_cq); + } + ATH_TX_UNLOCK(sc); + + /* Handle completing frames and fail them */ + while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) { + TAILQ_REMOVE(&bf_cq, bf, bf_list); + ath_tx_default_comp(sc, bf, 1); + } } /* @@ -5406,7 +5520,7 @@ int ath_tx_node_is_asleep(struct ath_softc *sc, struct ath_node *an) { - ATH_NODE_LOCK_ASSERT(an); + ATH_TX_LOCK_ASSERT(sc); return (an->an_is_powersave); } @@ -5436,7 +5550,7 @@ ath_tx_node_sleep(struct ath_softc *sc, struct ath_node *an) struct ath_txq *txq; int tid; - ATH_NODE_UNLOCK_ASSERT(an); + ATH_TX_UNLOCK_ASSERT(sc); /* * It's possible that a parallel call to ath_tx_node_wakeup() @@ -5471,17 +5585,12 @@ ath_tx_node_sleep(struct ath_softc *sc, struct ath_node *an) ath_tx_tid_pause(sc, atid); } - ATH_TX_UNLOCK(sc); - - ATH_NODE_LOCK(an); /* In case of concurrency races from net80211.. */ if (an->an_is_powersave == 1) { - ATH_NODE_UNLOCK(an); device_printf(sc->sc_dev, "%s: an=%p: node was already asleep\n", __func__, an); - ATH_TX_LOCK(sc); for (tid = 0; tid < IEEE80211_TID_SIZE; tid++) { atid = &an->an_tid[tid]; txq = sc->sc_ac2q[atid->ac]; @@ -5495,7 +5604,7 @@ ath_tx_node_sleep(struct ath_softc *sc, struct ath_node *an) /* Mark node as in powersaving */ an->an_is_powersave = 1; - ATH_NODE_UNLOCK(an); + ATH_TX_UNLOCK(sc); } /* @@ -5509,12 +5618,13 @@ ath_tx_node_wakeup(struct ath_softc *sc, struct ath_node *an) struct ath_txq *txq; int tid; - ATH_NODE_UNLOCK_ASSERT(an); - ATH_NODE_LOCK(an); + ATH_TX_UNLOCK_ASSERT(sc); + + ATH_TX_LOCK(sc); /* In case of concurrency races from net80211.. */ if (an->an_is_powersave == 0) { - ATH_NODE_UNLOCK(an); + ATH_TX_UNLOCK(sc); device_printf(sc->sc_dev, "%s: an=%p: node was already awake\n", __func__, an); @@ -5524,9 +5634,6 @@ ath_tx_node_wakeup(struct ath_softc *sc, struct ath_node *an) /* Mark node as awake */ an->an_is_powersave = 0; - ATH_NODE_UNLOCK(an); - - ATH_TX_LOCK(sc); for (tid = 0; tid < IEEE80211_TID_SIZE; tid++) { atid = &an->an_tid[tid]; txq = sc->sc_ac2q[atid->ac]; diff --git a/sys/dev/ath/if_ath_tx.h b/sys/dev/ath/if_ath_tx.h index 1437d7f..d4775d0 100644 --- a/sys/dev/ath/if_ath_tx.h +++ b/sys/dev/ath/if_ath_tx.h @@ -134,6 +134,7 @@ extern void ath_addba_response_timeout(struct ieee80211_node *ni, extern void ath_tx_node_sleep(struct ath_softc *sc, struct ath_node *an); extern void ath_tx_node_wakeup(struct ath_softc *sc, struct ath_node *an); extern int ath_tx_node_is_asleep(struct ath_softc *sc, struct ath_node *an); +extern void ath_tx_node_reassoc(struct ath_softc *sc, struct ath_node *an); /* * Misc debugging stuff -- cgit v1.1