From 528f46d0826afa01877ccc7670f2120a7a3b3ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Mon, 19 Dec 2016 13:59:15 +0100 Subject: ath9k: Add airtime fairness scheduler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a patch that introduces airtime fairness scheduling to ath9k, which can significantly improve network efficiency in mixed-rate environments. Signed-off-by: Toke Høiland-Jørgensen Signed-off-by: Felix Fietkau --- ...duce-airtime-fairness-scheduling-between-.patch | 651 +++++++++++++++++++++ 1 file changed, 651 insertions(+) create mode 100644 package/kernel/mac80211/patches/344-ath9k-Introduce-airtime-fairness-scheduling-between-.patch (limited to 'package/kernel/mac80211/patches/344-ath9k-Introduce-airtime-fairness-scheduling-between-.patch') 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 new file mode 100644 index 0000000000..10c6573b8c --- /dev/null +++ b/package/kernel/mac80211/patches/344-ath9k-Introduce-airtime-fairness-scheduling-between-.patch @@ -0,0 +1,651 @@ +From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= +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 +Signed-off-by: Kalle Valo +--- + +--- 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; -- cgit v1.2.3