diff options
Diffstat (limited to 'package/kernel/mac80211/patches/344-ath9k-Introduce-airtime-fairness-scheduling-between-.patch')
-rw-r--r-- | package/kernel/mac80211/patches/344-ath9k-Introduce-airtime-fairness-scheduling-between-.patch | 651 |
1 files changed, 0 insertions, 651 deletions
diff --git a/package/kernel/mac80211/patches/344-ath9k-Introduce-airtime-fairness-scheduling-between-.patch b/package/kernel/mac80211/patches/344-ath9k-Introduce-airtime-fairness-scheduling-between-.patch deleted file mode 100644 index 10c6573b8c..0000000000 --- a/package/kernel/mac80211/patches/344-ath9k-Introduce-airtime-fairness-scheduling-between-.patch +++ /dev/null @@ -1,651 +0,0 @@ -From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= <toke@toke.dk> -Date: Mon, 5 Dec 2016 13:27:37 +0200 -Subject: [PATCH] ath9k: Introduce airtime fairness scheduling between stations -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -This reworks the ath9k driver to schedule transmissions to connected -stations in a way that enforces airtime fairness between them. It -accomplishes this by measuring the time spent transmitting to or -receiving from a station at TX and RX completion, and accounting this to -a per-station, per-QoS level airtime deficit. Then, an FQ-CoDel based -deficit scheduler is employed at packet dequeue time, to control which -station gets the next transmission opportunity. - -Airtime fairness can significantly improve the efficiency of the network -when station rates vary. The following throughput values are from a -simple three-station test scenario, where two stations operate at the -highest HT20 rate, and one station at the lowest, and the scheduler is -employed at the access point: - - Before / After -Fast station 1: 19.17 / 25.09 Mbps -Fast station 2: 19.83 / 25.21 Mbps -Slow station: 2.58 / 1.77 Mbps -Total: 41.58 / 52.07 Mbps - -The benefit of airtime fairness goes up the more stations are present. -In a 30-station test with one station artificially limited to 1 Mbps, -we have seen aggregate throughput go from 2.14 to 17.76 Mbps. - -Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk> -Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com> ---- - ---- a/drivers/net/wireless/ath/ath9k/ath9k.h -+++ b/drivers/net/wireless/ath/ath9k/ath9k.h -@@ -112,6 +112,8 @@ int ath_descdma_setup(struct ath_softc * - #define ATH_TXFIFO_DEPTH 8 - #define ATH_TX_ERROR 0x01 - -+#define ATH_AIRTIME_QUANTUM 300 /* usec */ -+ - /* Stop tx traffic 1ms before the GO goes away */ - #define ATH_P2P_PS_STOP_TIME 1000 - -@@ -247,6 +249,9 @@ struct ath_atx_tid { - bool has_queued; - }; - -+void __ath_tx_queue_tid(struct ath_softc *sc, struct ath_atx_tid *tid); -+void ath_tx_queue_tid(struct ath_softc *sc, struct ath_atx_tid *tid); -+ - struct ath_node { - struct ath_softc *sc; - struct ieee80211_sta *sta; /* station struct we're part of */ -@@ -258,9 +263,12 @@ struct ath_node { - - bool sleeping; - bool no_ps_filter; -+ s64 airtime_deficit[IEEE80211_NUM_ACS]; -+ u32 airtime_rx_start; - - #ifdef CPTCFG_ATH9K_STATION_STATISTICS - struct ath_rx_rate_stats rx_rate_stats; -+ struct ath_airtime_stats airtime_stats; - #endif - u8 key_idx[4]; - -@@ -317,10 +325,16 @@ struct ath_rx { - /* Channel Context */ - /*******************/ - -+struct ath_acq { -+ struct list_head acq_new; -+ struct list_head acq_old; -+ spinlock_t lock; -+}; -+ - struct ath_chanctx { - struct cfg80211_chan_def chandef; - struct list_head vifs; -- struct list_head acq[IEEE80211_NUM_ACS]; -+ struct ath_acq acq[IEEE80211_NUM_ACS]; - int hw_queue_base; - - /* do not dereference, use for comparison only */ -@@ -575,6 +589,8 @@ void ath_txq_schedule_all(struct ath_sof - int ath_tx_init(struct ath_softc *sc, int nbufs); - int ath_txq_update(struct ath_softc *sc, int qnum, - struct ath9k_tx_queue_info *q); -+u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, int pktlen, -+ int width, int half_gi, bool shortPreamble); - void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop); - void ath_assign_seq(struct ath_common *common, struct sk_buff *skb); - int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, -@@ -963,6 +979,11 @@ void ath_ant_comb_scan(struct ath_softc - - #define ATH9K_NUM_CHANCTX 2 /* supports 2 operating channels */ - -+#define AIRTIME_USE_TX BIT(0) -+#define AIRTIME_USE_RX BIT(1) -+#define AIRTIME_USE_NEW_QUEUES BIT(2) -+#define AIRTIME_ACTIVE(flags) (!!(flags & (AIRTIME_USE_TX|AIRTIME_USE_RX))) -+ - struct ath_softc { - struct ieee80211_hw *hw; - struct device *dev; -@@ -1005,6 +1026,8 @@ struct ath_softc { - short nbcnvifs; - unsigned long ps_usecount; - -+ u16 airtime_flags; /* AIRTIME_* */ -+ - struct ath_rx rx; - struct ath_tx tx; - struct ath_beacon beacon; ---- a/drivers/net/wireless/ath/ath9k/channel.c -+++ b/drivers/net/wireless/ath/ath9k/channel.c -@@ -118,8 +118,11 @@ void ath_chanctx_init(struct ath_softc * - INIT_LIST_HEAD(&ctx->vifs); - ctx->txpower = ATH_TXPOWER_MAX; - ctx->flush_timeout = HZ / 5; /* 200ms */ -- for (j = 0; j < ARRAY_SIZE(ctx->acq); j++) -- INIT_LIST_HEAD(&ctx->acq[j]); -+ for (j = 0; j < ARRAY_SIZE(ctx->acq); j++) { -+ INIT_LIST_HEAD(&ctx->acq[j].acq_new); -+ INIT_LIST_HEAD(&ctx->acq[j].acq_old); -+ spin_lock_init(&ctx->acq[j].lock); -+ } - } - } - -@@ -1345,8 +1348,11 @@ void ath9k_offchannel_init(struct ath_so - ctx->txpower = ATH_TXPOWER_MAX; - cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20); - -- for (i = 0; i < ARRAY_SIZE(ctx->acq); i++) -- INIT_LIST_HEAD(&ctx->acq[i]); -+ for (i = 0; i < ARRAY_SIZE(ctx->acq); i++) { -+ INIT_LIST_HEAD(&ctx->acq[i].acq_new); -+ INIT_LIST_HEAD(&ctx->acq[i].acq_old); -+ spin_lock_init(&ctx->acq[i].lock); -+ } - - sc->offchannel.chan.offchannel = true; - } ---- a/drivers/net/wireless/ath/ath9k/debug.c -+++ b/drivers/net/wireless/ath/ath9k/debug.c -@@ -1399,5 +1399,8 @@ int ath9k_init_debug(struct ath_hw *ah) - debugfs_create_file("tpc", S_IRUSR | S_IWUSR, - sc->debug.debugfs_phy, sc, &fops_tpc); - -+ debugfs_create_u16("airtime_flags", S_IRUSR | S_IWUSR, -+ sc->debug.debugfs_phy, &sc->airtime_flags); -+ - return 0; - } ---- a/drivers/net/wireless/ath/ath9k/debug.h -+++ b/drivers/net/wireless/ath/ath9k/debug.h -@@ -221,6 +221,11 @@ struct ath_rx_rate_stats { - } cck_stats[4]; - }; - -+struct ath_airtime_stats { -+ u32 rx_airtime; -+ u32 tx_airtime; -+}; -+ - #define ANT_MAIN 0 - #define ANT_ALT 1 - -@@ -314,12 +319,20 @@ ath9k_debug_sync_cause(struct ath_softc - void ath_debug_rate_stats(struct ath_softc *sc, - struct ath_rx_status *rs, - struct sk_buff *skb); -+void ath_debug_airtime(struct ath_softc *sc, -+ struct ath_node *an, -+ u32 rx, u32 tx); - #else - static inline void ath_debug_rate_stats(struct ath_softc *sc, - struct ath_rx_status *rs, - struct sk_buff *skb) - { - } -+static inline void ath_debug_airtime(struct ath_softc *sc, -+ struct ath_node *an, -+ u32 rx, u32 tx) -+{ -+} - #endif /* CPTCFG_ATH9K_STATION_STATISTICS */ - - #endif /* DEBUG_H */ ---- a/drivers/net/wireless/ath/ath9k/debug_sta.c -+++ b/drivers/net/wireless/ath/ath9k/debug_sta.c -@@ -242,6 +242,59 @@ static const struct file_operations fops - .llseek = default_llseek, - }; - -+void ath_debug_airtime(struct ath_softc *sc, -+ struct ath_node *an, -+ u32 rx, -+ u32 tx) -+{ -+ struct ath_airtime_stats *astats = &an->airtime_stats; -+ -+ astats->rx_airtime += rx; -+ astats->tx_airtime += tx; -+} -+ -+static ssize_t read_airtime(struct file *file, char __user *user_buf, -+ size_t count, loff_t *ppos) -+{ -+ struct ath_node *an = file->private_data; -+ struct ath_airtime_stats *astats; -+ static const char *qname[4] = { -+ "VO", "VI", "BE", "BK" -+ }; -+ u32 len = 0, size = 256; -+ char *buf; -+ size_t retval; -+ int i; -+ -+ buf = kzalloc(size, GFP_KERNEL); -+ if (buf == NULL) -+ return -ENOMEM; -+ -+ astats = &an->airtime_stats; -+ -+ len += scnprintf(buf + len, size - len, "RX: %u us\n", astats->rx_airtime); -+ len += scnprintf(buf + len, size - len, "TX: %u us\n", astats->tx_airtime); -+ len += scnprintf(buf + len, size - len, "Deficit: "); -+ for (i = 0; i < 4; i++) -+ len += scnprintf(buf+len, size - len, "%s: %lld us ", qname[i], an->airtime_deficit[i]); -+ if (len < size) -+ buf[len++] = '\n'; -+ -+ retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); -+ kfree(buf); -+ -+ return retval; -+} -+ -+ -+static const struct file_operations fops_airtime = { -+ .read = read_airtime, -+ .open = simple_open, -+ .owner = THIS_MODULE, -+ .llseek = default_llseek, -+}; -+ -+ - void ath9k_sta_add_debugfs(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, -@@ -251,4 +304,5 @@ void ath9k_sta_add_debugfs(struct ieee80 - - debugfs_create_file("node_aggr", S_IRUGO, dir, an, &fops_node_aggr); - debugfs_create_file("node_recv", S_IRUGO, dir, an, &fops_node_recv); -+ debugfs_create_file("airtime", S_IRUGO, dir, an, &fops_airtime); - } ---- a/drivers/net/wireless/ath/ath9k/init.c -+++ b/drivers/net/wireless/ath/ath9k/init.c -@@ -620,6 +620,8 @@ static int ath9k_init_softc(u16 devid, s - - /* Will be cleared in ath9k_start() */ - set_bit(ATH_OP_INVALID, &common->op_flags); -+ sc->airtime_flags = (AIRTIME_USE_TX | AIRTIME_USE_RX | -+ AIRTIME_USE_NEW_QUEUES); - - sc->sc_ah = ah; - sc->dfs_detector = dfs_pattern_detector_init(common, NL80211_DFS_UNSET); ---- a/drivers/net/wireless/ath/ath9k/main.c -+++ b/drivers/net/wireless/ath/ath9k/main.c -@@ -70,10 +70,10 @@ static bool ath9k_has_pending_frames(str - goto out; - - if (txq->mac80211_qnum >= 0) { -- struct list_head *list; -+ struct ath_acq *acq; - -- list = &sc->cur_chan->acq[txq->mac80211_qnum]; -- if (!list_empty(list)) -+ acq = &sc->cur_chan->acq[txq->mac80211_qnum]; -+ if (!list_empty(&acq->acq_new) || !list_empty(&acq->acq_old)) - pending = true; - } - out: ---- a/drivers/net/wireless/ath/ath9k/recv.c -+++ b/drivers/net/wireless/ath/ath9k/recv.c -@@ -991,6 +991,70 @@ static void ath9k_apply_ampdu_details(st - } - } - -+static void ath_rx_count_airtime(struct ath_softc *sc, -+ struct ath_rx_status *rs, -+ struct sk_buff *skb) -+{ -+ struct ath_node *an; -+ struct ath_acq *acq; -+ struct ath_vif *avp; -+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; -+ struct ath_hw *ah = sc->sc_ah; -+ struct ath_common *common = ath9k_hw_common(ah); -+ struct ieee80211_sta *sta; -+ struct ieee80211_rx_status *rxs; -+ const struct ieee80211_rate *rate; -+ bool is_sgi, is_40, is_sp; -+ int phy; -+ u16 len = rs->rs_datalen; -+ u32 airtime = 0; -+ u8 tidno, acno; -+ -+ if (!ieee80211_is_data(hdr->frame_control)) -+ return; -+ -+ rcu_read_lock(); -+ -+ sta = ieee80211_find_sta_by_ifaddr(sc->hw, hdr->addr2, NULL); -+ if (!sta) -+ goto exit; -+ an = (struct ath_node *) sta->drv_priv; -+ avp = (struct ath_vif *) an->vif->drv_priv; -+ tidno = skb->priority & IEEE80211_QOS_CTL_TID_MASK; -+ acno = TID_TO_WME_AC(tidno); -+ acq = &avp->chanctx->acq[acno]; -+ -+ rxs = IEEE80211_SKB_RXCB(skb); -+ -+ is_sgi = !!(rxs->flag & RX_FLAG_SHORT_GI); -+ is_40 = !!(rxs->flag & RX_FLAG_40MHZ); -+ is_sp = !!(rxs->flag & RX_FLAG_SHORTPRE); -+ -+ if (!!(rxs->flag & RX_FLAG_HT)) { -+ /* MCS rates */ -+ -+ airtime += ath_pkt_duration(sc, rxs->rate_idx, len, -+ is_40, is_sgi, is_sp); -+ } else { -+ -+ phy = IS_CCK_RATE(rs->rs_rate) ? WLAN_RC_PHY_CCK : WLAN_RC_PHY_OFDM; -+ rate = &common->sbands[rxs->band].bitrates[rxs->rate_idx]; -+ airtime += ath9k_hw_computetxtime(ah, phy, rate->bitrate * 100, -+ len, rxs->rate_idx, is_sp); -+ } -+ -+ if (!!(sc->airtime_flags & AIRTIME_USE_RX)) { -+ spin_lock_bh(&acq->lock); -+ an->airtime_deficit[acno] -= airtime; -+ if (an->airtime_deficit[acno] <= 0) -+ __ath_tx_queue_tid(sc, ATH_AN_2_TID(an, tidno)); -+ spin_unlock_bh(&acq->lock); -+ } -+ ath_debug_airtime(sc, an, airtime, 0); -+exit: -+ rcu_read_unlock(); -+} -+ - int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) - { - struct ath_rxbuf *bf; -@@ -1137,6 +1201,7 @@ int ath_rx_tasklet(struct ath_softc *sc, - ath9k_antenna_check(sc, &rs); - ath9k_apply_ampdu_details(sc, &rs, rxs); - ath_debug_rate_stats(sc, &rs, skb); -+ ath_rx_count_airtime(sc, &rs, skb); - - hdr = (struct ieee80211_hdr *)skb->data; - if (ieee80211_is_ack(hdr->frame_control)) ---- a/drivers/net/wireless/ath/ath9k/xmit.c -+++ b/drivers/net/wireless/ath/ath9k/xmit.c -@@ -125,21 +125,44 @@ void ath_txq_unlock_complete(struct ath_ - 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; -@@ -154,7 +177,7 @@ void ath9k_wake_tx_queue(struct ieee8021 - 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); -@@ -684,7 +707,7 @@ static void ath_tx_complete_aggr(struct - - 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; -@@ -712,6 +735,53 @@ static bool bf_is_ampdu_not_probing(stru - 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) -@@ -733,6 +803,7 @@ static void ath_tx_process_buffer(struct - - 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); -@@ -1094,8 +1165,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; -@@ -1493,7 +1564,7 @@ ath_tx_form_burst(struct ath_softc *sc, - } - - 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; -@@ -1515,7 +1586,6 @@ static bool ath_tx_sched_aggr(struct ath - 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; - } - -@@ -1639,7 +1709,7 @@ void ath_tx_aggr_wakeup(struct ath_softc - 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); -@@ -1956,9 +2026,10 @@ void ath_tx_cleanupq(struct ath_softc *s - 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; -@@ -1967,48 +2038,55 @@ void ath_txq_schedule(struct ath_softc * - 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); - } -@@ -2862,6 +2940,9 @@ void ath_tx_node_init(struct ath_softc * - 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; |