ath9k: Add airtime fairness scheduler

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 <toke@toke.dk>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
Toke Høiland-Jørgensen 2016-12-19 13:59:15 +01:00 committed by Felix Fietkau
parent 600b824087
commit 528f46d082
9 changed files with 728 additions and 15 deletions

View File

@ -0,0 +1,651 @@
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;

View File

@ -0,0 +1,62 @@
From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= <toke@toke.dk>
Date: Mon, 5 Dec 2016 13:27:38 +0200
Subject: [PATCH] ath9k: Turn ath_txq_lock/unlock() into static inlines.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
These are one-line functions that just call spin_lock/unlock_bh(); turn
them into static inlines to avoid the function call overhead.
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
@@ -569,6 +569,15 @@ static inline void ath_chanctx_check_act
#endif /* CPTCFG_ATH9K_CHANNEL_CONTEXT */
+static inline void ath_txq_lock(struct ath_softc *sc, struct ath_txq *txq)
+{
+ spin_lock_bh(&txq->axq_lock);
+}
+static inline void ath_txq_unlock(struct ath_softc *sc, struct ath_txq *txq)
+{
+ spin_unlock_bh(&txq->axq_lock);
+}
+
void ath_startrecv(struct ath_softc *sc);
bool ath_stoprecv(struct ath_softc *sc);
u32 ath_calcrxfilter(struct ath_softc *sc);
@@ -576,8 +585,6 @@ int ath_rx_init(struct ath_softc *sc, in
void ath_rx_cleanup(struct ath_softc *sc);
int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp);
struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype);
-void ath_txq_lock(struct ath_softc *sc, struct ath_txq *txq);
-void ath_txq_unlock(struct ath_softc *sc, struct ath_txq *txq);
void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq);
void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq);
bool ath_drain_all_txq(struct ath_softc *sc);
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -98,18 +98,6 @@ static void ath_tx_status(struct ieee802
dev_kfree_skb(skb);
}
-void ath_txq_lock(struct ath_softc *sc, struct ath_txq *txq)
- __acquires(&txq->axq_lock)
-{
- spin_lock_bh(&txq->axq_lock);
-}
-
-void ath_txq_unlock(struct ath_softc *sc, struct ath_txq *txq)
- __releases(&txq->axq_lock)
-{
- spin_unlock_bh(&txq->axq_lock);
-}
-
void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq)
__releases(&txq->axq_lock)
{

View File

@ -1,6 +1,6 @@
--- a/drivers/net/wireless/ath/ath9k/init.c --- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -773,6 +773,7 @@ static const struct ieee80211_iface_limi @@ -775,6 +775,7 @@ static const struct ieee80211_iface_limi
BIT(NL80211_IFTYPE_AP) }, BIT(NL80211_IFTYPE_AP) },
{ .max = 1, .types = BIT(NL80211_IFTYPE_P2P_CLIENT) | { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO) }, BIT(NL80211_IFTYPE_P2P_GO) },

View File

@ -1,6 +1,6 @@
--- a/drivers/net/wireless/ath/ath9k/init.c --- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -1076,23 +1076,23 @@ static int __init ath9k_init(void) @@ -1078,23 +1078,23 @@ static int __init ath9k_init(void)
{ {
int error; int error;

View File

@ -1,6 +1,6 @@
--- a/drivers/net/wireless/ath/ath9k/ath9k.h --- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -827,6 +827,9 @@ static inline int ath9k_dump_btcoex(stru @@ -850,6 +850,9 @@ static inline int ath9k_dump_btcoex(stru
#ifdef CPTCFG_MAC80211_LEDS #ifdef CPTCFG_MAC80211_LEDS
void ath_init_leds(struct ath_softc *sc); void ath_init_leds(struct ath_softc *sc);
void ath_deinit_leds(struct ath_softc *sc); void ath_deinit_leds(struct ath_softc *sc);
@ -10,9 +10,9 @@
#else #else
static inline void ath_init_leds(struct ath_softc *sc) static inline void ath_init_leds(struct ath_softc *sc)
{ {
@@ -963,6 +966,13 @@ void ath_ant_comb_scan(struct ath_softc @@ -991,6 +994,13 @@ void ath_ant_comb_scan(struct ath_softc
#define AIRTIME_USE_NEW_QUEUES BIT(2)
#define ATH9K_NUM_CHANCTX 2 /* supports 2 operating channels */ #define AIRTIME_ACTIVE(flags) (!!(flags & (AIRTIME_USE_TX|AIRTIME_USE_RX)))
+struct ath_led { +struct ath_led {
+ struct list_head list; + struct list_head list;
@ -24,7 +24,7 @@
struct ath_softc { struct ath_softc {
struct ieee80211_hw *hw; struct ieee80211_hw *hw;
struct device *dev; struct device *dev;
@@ -1015,9 +1025,8 @@ struct ath_softc { @@ -1045,9 +1055,8 @@ struct ath_softc {
spinlock_t chan_lock; spinlock_t chan_lock;
#ifdef CPTCFG_MAC80211_LEDS #ifdef CPTCFG_MAC80211_LEDS
@ -181,7 +181,7 @@
--- a/drivers/net/wireless/ath/ath9k/init.c --- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -988,7 +988,7 @@ int ath9k_init_device(u16 devid, struct @@ -990,7 +990,7 @@ int ath9k_init_device(u16 devid, struct
#ifdef CPTCFG_MAC80211_LEDS #ifdef CPTCFG_MAC80211_LEDS
/* must be initialized before ieee80211_register_hw */ /* must be initialized before ieee80211_register_hw */

View File

@ -55,7 +55,7 @@
ops->spectral_scan_config = ar9003_hw_spectral_scan_config; ops->spectral_scan_config = ar9003_hw_spectral_scan_config;
--- a/drivers/net/wireless/ath/ath9k/init.c --- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -761,7 +761,8 @@ static void ath9k_init_txpower_limits(st @@ -763,7 +763,8 @@ static void ath9k_init_txpower_limits(st
if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
ath9k_init_band_txpower(sc, NL80211_BAND_5GHZ); ath9k_init_band_txpower(sc, NL80211_BAND_5GHZ);
@ -65,7 +65,7 @@
} }
static const struct ieee80211_iface_limit if_limits[] = { static const struct ieee80211_iface_limit if_limits[] = {
@@ -948,6 +949,18 @@ static void ath9k_set_hw_capab(struct at @@ -950,6 +951,18 @@ static void ath9k_set_hw_capab(struct at
SET_IEEE80211_PERM_ADDR(hw, common->macaddr); SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
} }
@ -84,7 +84,7 @@
int ath9k_init_device(u16 devid, struct ath_softc *sc, int ath9k_init_device(u16 devid, struct ath_softc *sc,
const struct ath_bus_ops *bus_ops) const struct ath_bus_ops *bus_ops)
{ {
@@ -993,6 +1006,8 @@ int ath9k_init_device(u16 devid, struct @@ -995,6 +1008,8 @@ int ath9k_init_device(u16 devid, struct
ARRAY_SIZE(ath9k_tpt_blink)); ARRAY_SIZE(ath9k_tpt_blink));
#endif #endif

View File

@ -18,7 +18,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
#include "common.h" #include "common.h"
#include "debug.h" #include "debug.h"
@@ -973,6 +974,14 @@ struct ath_led { @@ -1001,6 +1002,14 @@ struct ath_led {
struct led_classdev cdev; struct led_classdev cdev;
}; };
@ -33,7 +33,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
struct ath_softc { struct ath_softc {
struct ieee80211_hw *hw; struct ieee80211_hw *hw;
struct device *dev; struct device *dev;
@@ -1027,6 +1036,9 @@ struct ath_softc { @@ -1057,6 +1066,9 @@ struct ath_softc {
#ifdef CPTCFG_MAC80211_LEDS #ifdef CPTCFG_MAC80211_LEDS
const char *led_default_trigger; const char *led_default_trigger;
struct list_head leds; struct list_head leds;

View File

@ -10,7 +10,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
--- ---
--- a/drivers/net/wireless/ath/ath9k/ath9k.h --- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -1038,6 +1038,7 @@ struct ath_softc { @@ -1068,6 +1068,7 @@ struct ath_softc {
struct list_head leds; struct list_head leds;
#ifdef CONFIG_GPIOLIB #ifdef CONFIG_GPIOLIB
struct ath9k_gpio_chip *gpiochip; struct ath9k_gpio_chip *gpiochip;

View File

@ -329,7 +329,7 @@ Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
&ratesArray[0], cfgCtl, &ratesArray[0], cfgCtl,
--- a/drivers/net/wireless/ath/ath9k/xmit.c --- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1177,8 +1177,9 @@ static u8 ath_get_rate_txpower(struct at @@ -1236,8 +1236,9 @@ static u8 ath_get_rate_txpower(struct at
if (is_40) { if (is_40) {
u8 power_ht40delta; u8 power_ht40delta;
struct ar5416_eeprom_def *eep = &ah->eeprom.def; struct ar5416_eeprom_def *eep = &ah->eeprom.def;