diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/xmit.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/xmit.c | 177 |
1 files changed, 129 insertions, 48 deletions
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 5fa98e2..1c5a782 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -124,21 +124,44 @@ void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq) ath_tx_status(hw, skb); } -static void ath_tx_queue_tid(struct ath_softc *sc, struct ath_txq *txq, - struct ath_atx_tid *tid) +void __ath_tx_queue_tid(struct ath_softc *sc, struct ath_atx_tid *tid) { - struct list_head *list; struct ath_vif *avp = (struct ath_vif *) tid->an->vif->drv_priv; struct ath_chanctx *ctx = avp->chanctx; + struct ath_acq *acq; + struct list_head *tid_list; + u8 acno = TID_TO_WME_AC(tid->tidno); - if (!ctx) + if (!ctx || !list_empty(&tid->list)) return; - list = &ctx->acq[TID_TO_WME_AC(tid->tidno)]; - if (list_empty(&tid->list)) - list_add_tail(&tid->list, list); + + acq = &ctx->acq[acno]; + if ((sc->airtime_flags & AIRTIME_USE_NEW_QUEUES) && + tid->an->airtime_deficit[acno] > 0) + tid_list = &acq->acq_new; + else + tid_list = &acq->acq_old; + + list_add_tail(&tid->list, tid_list); } +void ath_tx_queue_tid(struct ath_softc *sc, struct ath_atx_tid *tid) +{ + struct ath_vif *avp = (struct ath_vif *) tid->an->vif->drv_priv; + struct ath_chanctx *ctx = avp->chanctx; + struct ath_acq *acq; + + if (!ctx || !list_empty(&tid->list)) + return; + + acq = &ctx->acq[TID_TO_WME_AC(tid->tidno)]; + spin_lock_bh(&acq->lock); + __ath_tx_queue_tid(sc, tid); + spin_unlock_bh(&acq->lock); +} + + void ath9k_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *queue) { struct ath_softc *sc = hw->priv; @@ -153,7 +176,7 @@ void ath9k_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *queue) ath_txq_lock(sc, txq); tid->has_queued = true; - ath_tx_queue_tid(sc, txq, tid); + ath_tx_queue_tid(sc, tid); ath_txq_schedule(sc, txq); ath_txq_unlock(sc, txq); @@ -660,7 +683,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, skb_queue_splice_tail(&bf_pending, &tid->retry_q); if (!an->sleeping) { - ath_tx_queue_tid(sc, txq, tid); + ath_tx_queue_tid(sc, tid); if (ts->ts_status & (ATH9K_TXERR_FILT | ATH9K_TXERR_XRETRY)) tid->clear_ps_filter = true; @@ -688,6 +711,53 @@ static bool bf_is_ampdu_not_probing(struct ath_buf *bf) return bf_isampdu(bf) && !(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE); } +static void ath_tx_count_airtime(struct ath_softc *sc, struct ath_txq *txq, + struct ath_buf *bf, struct ath_tx_status *ts) +{ + struct ath_node *an; + struct ath_acq *acq = &sc->cur_chan->acq[txq->mac80211_qnum]; + struct sk_buff *skb; + struct ieee80211_hdr *hdr; + struct ieee80211_hw *hw = sc->hw; + struct ieee80211_tx_rate rates[4]; + struct ieee80211_sta *sta; + int i; + u32 airtime = 0; + + skb = bf->bf_mpdu; + if(!skb) + return; + + hdr = (struct ieee80211_hdr *)skb->data; + memcpy(rates, bf->rates, sizeof(rates)); + + rcu_read_lock(); + + sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr1, hdr->addr2); + if(!sta) + goto exit; + + + an = (struct ath_node *) sta->drv_priv; + + airtime += ts->duration * (ts->ts_longretry + 1); + + for(i=0; i < ts->ts_rateindex; i++) + airtime += ath9k_hw_get_duration(sc->sc_ah, bf->bf_desc, i) * rates[i].count; + + if (!!(sc->airtime_flags & AIRTIME_USE_TX)) { + spin_lock_bh(&acq->lock); + an->airtime_deficit[txq->mac80211_qnum] -= airtime; + if (an->airtime_deficit[txq->mac80211_qnum] <= 0) + __ath_tx_queue_tid(sc, ath_get_skb_tid(sc, an, skb)); + spin_unlock_bh(&acq->lock); + } + ath_debug_airtime(sc, an, 0, airtime); + +exit: + rcu_read_unlock(); +} + static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq, struct ath_tx_status *ts, struct ath_buf *bf, struct list_head *bf_head) @@ -709,6 +779,7 @@ static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq, ts->duration = ath9k_hw_get_duration(sc->sc_ah, bf->bf_desc, ts->ts_rateindex); + ath_tx_count_airtime(sc, txq, bf, ts); hdr = (struct ieee80211_hdr *) bf->bf_mpdu->data; sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr1, hdr->addr2); @@ -1068,8 +1139,8 @@ finish: * width - 0 for 20 MHz, 1 for 40 MHz * half_gi - to use 4us v/s 3.6 us for symbol time */ -static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, int pktlen, - int width, int half_gi, bool shortPreamble) +u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, int pktlen, + int width, int half_gi, bool shortPreamble) { u32 nbits, nsymbits, duration, nsymbols; int streams; @@ -1468,7 +1539,7 @@ ath_tx_form_burst(struct ath_softc *sc, struct ath_txq *txq, } static bool ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq, - struct ath_atx_tid *tid, bool *stop) + struct ath_atx_tid *tid) { struct ath_buf *bf; struct ieee80211_tx_info *tx_info; @@ -1490,7 +1561,6 @@ static bool ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq, if ((aggr && txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) || (!aggr && txq->axq_depth >= ATH_NON_AGGR_MIN_QDEPTH)) { __skb_queue_tail(&tid->retry_q, bf->bf_mpdu); - *stop = true; return false; } @@ -1614,7 +1684,7 @@ void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an) ath_txq_lock(sc, txq); tid->clear_ps_filter = true; if (ath_tid_has_buffered(tid)) { - ath_tx_queue_tid(sc, txq, tid); + ath_tx_queue_tid(sc, tid); ath_txq_schedule(sc, txq); } ath_txq_unlock_complete(sc, txq); @@ -1913,9 +1983,10 @@ void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq) void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_atx_tid *tid, *last_tid; + struct ath_atx_tid *tid; struct list_head *tid_list; - bool sent = false; + struct ath_acq *acq; + bool active = AIRTIME_ACTIVE(sc->airtime_flags); if (txq->mac80211_qnum < 0) return; @@ -1924,48 +1995,55 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) return; spin_lock_bh(&sc->chan_lock); - tid_list = &sc->cur_chan->acq[txq->mac80211_qnum]; - - if (list_empty(tid_list)) { - spin_unlock_bh(&sc->chan_lock); - return; - } - rcu_read_lock(); + acq = &sc->cur_chan->acq[txq->mac80211_qnum]; - last_tid = list_entry(tid_list->prev, struct ath_atx_tid, list); - while (!list_empty(tid_list)) { - bool stop = false; - - if (sc->cur_chan->stopped) - break; - - tid = list_first_entry(tid_list, struct ath_atx_tid, list); - list_del_init(&tid->list); + if (sc->cur_chan->stopped) + goto out; - if (ath_tx_sched_aggr(sc, txq, tid, &stop)) - sent = true; +begin: + tid_list = &acq->acq_new; + if (list_empty(tid_list)) { + tid_list = &acq->acq_old; + if (list_empty(tid_list)) + goto out; + } + tid = list_first_entry(tid_list, struct ath_atx_tid, list); - /* - * add tid to round-robin queue if more frames - * are pending for the tid - */ - if (ath_tid_has_buffered(tid)) - ath_tx_queue_tid(sc, txq, tid); + if (active && tid->an->airtime_deficit[txq->mac80211_qnum] <= 0) { + spin_lock_bh(&acq->lock); + tid->an->airtime_deficit[txq->mac80211_qnum] += ATH_AIRTIME_QUANTUM; + list_move_tail(&tid->list, &acq->acq_old); + spin_unlock_bh(&acq->lock); + goto begin; + } - if (stop) - break; + if (!ath_tid_has_buffered(tid)) { + spin_lock_bh(&acq->lock); + if ((tid_list == &acq->acq_new) && !list_empty(&acq->acq_old)) + list_move_tail(&tid->list, &acq->acq_old); + else { + list_del_init(&tid->list); + } + spin_unlock_bh(&acq->lock); + goto begin; + } - if (tid == last_tid) { - if (!sent) - break; - sent = false; - last_tid = list_entry(tid_list->prev, - struct ath_atx_tid, list); + /* + * If we succeed in scheduling something, immediately restart to make + * sure we keep the HW busy. + */ + if(ath_tx_sched_aggr(sc, txq, tid)) { + if (!active) { + spin_lock_bh(&acq->lock); + list_move_tail(&tid->list, &acq->acq_old); + spin_unlock_bh(&acq->lock); } + goto begin; } +out: rcu_read_unlock(); spin_unlock_bh(&sc->chan_lock); } @@ -2819,6 +2897,9 @@ void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an) struct ath_atx_tid *tid; int tidno, acno; + for (acno = 0; acno < IEEE80211_NUM_ACS; acno++) + an->airtime_deficit[acno] = ATH_AIRTIME_QUANTUM; + for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) { tid = ath_node_to_tid(an, tidno); tid->an = an; |