|
@@ -112,11 +112,62 @@ void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq)
|
|
|
ath_tx_status(hw, skb);
|
|
|
}
|
|
|
|
|
|
-void ath9k_wake_tx_queue(struct ieee80211_hw *hw)
|
|
|
+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;
|
|
|
+ struct list_head *tid_list;
|
|
|
+ u8 acno = TID_TO_WME_AC(tid->tidno);
|
|
|
+
|
|
|
+ if (!ctx || !list_empty(&tid->list))
|
|
|
+ return;
|
|
|
+
|
|
|
+
|
|
|
+ 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;
|
|
|
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
|
|
+ struct ath_atx_tid *tid = (struct ath_atx_tid *) queue->drv_priv;
|
|
|
+ struct ath_txq *txq = tid->txq;
|
|
|
+
|
|
|
+ ath_dbg(common, QUEUE, "Waking TX queue: %pM (%d)\n",
|
|
|
+ queue->sta ? queue->sta->addr : queue->vif->addr,
|
|
|
+ tid->tidno);
|
|
|
+
|
|
|
+ ath_txq_lock(sc, txq);
|
|
|
|
|
|
- ath_txq_schedule(sc);
|
|
|
+ tid->has_queued = true;
|
|
|
+ ath_tx_queue_tid(sc, tid);
|
|
|
+ ath_txq_schedule(sc, txq);
|
|
|
+
|
|
|
+ ath_txq_unlock(sc, txq);
|
|
|
}
|
|
|
|
|
|
static struct ath_frame_info *get_frame_info(struct sk_buff *skb)
|
|
@@ -179,9 +230,14 @@ ath_tid_pull(struct ath_atx_tid *tid)
|
|
|
struct ath_frame_info *fi;
|
|
|
int q;
|
|
|
|
|
|
+ if (!tid->has_queued)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
skb = ieee80211_tx_dequeue(hw, txq);
|
|
|
- if (!skb)
|
|
|
+ if (!skb) {
|
|
|
+ tid->has_queued = false;
|
|
|
return NULL;
|
|
|
+ }
|
|
|
|
|
|
if (ath_tx_prepare(hw, skb, &txctl)) {
|
|
|
ieee80211_free_txskb(hw, skb);
|
|
@@ -198,6 +254,12 @@ ath_tid_pull(struct ath_atx_tid *tid)
|
|
|
return skb;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+static bool ath_tid_has_buffered(struct ath_atx_tid *tid)
|
|
|
+{
|
|
|
+ return !skb_queue_empty(&tid->retry_q) || tid->has_queued;
|
|
|
+}
|
|
|
+
|
|
|
static struct sk_buff *ath_tid_dequeue(struct ath_atx_tid *tid)
|
|
|
{
|
|
|
struct sk_buff *skb;
|
|
@@ -609,10 +671,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) {
|
|
|
- struct ieee80211_txq *queue = container_of(
|
|
|
- (void *)tid, struct ieee80211_txq, drv_priv);
|
|
|
-
|
|
|
- ieee80211_schedule_txq(sc->hw, queue);
|
|
|
+ ath_tx_queue_tid(sc, tid);
|
|
|
|
|
|
if (ts->ts_status & (ATH9K_TXERR_FILT | ATH9K_TXERR_XRETRY))
|
|
|
tid->clear_ps_filter = true;
|
|
@@ -660,6 +719,8 @@ static void ath_tx_count_airtime(struct ath_softc *sc, struct ath_node *an,
|
|
|
|
|
|
spin_lock_bh(&acq->lock);
|
|
|
an->airtime_deficit[q] -= airtime;
|
|
|
+ if (an->airtime_deficit[q] <= 0)
|
|
|
+ __ath_tx_queue_tid(sc, tid);
|
|
|
spin_unlock_bh(&acq->lock);
|
|
|
}
|
|
|
ath_debug_airtime(sc, an, 0, airtime);
|
|
@@ -709,6 +770,8 @@ static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq,
|
|
|
} else
|
|
|
ath_tx_complete_aggr(sc, txq, bf, bf_head, sta, tid, ts, txok);
|
|
|
|
|
|
+ if (!flush)
|
|
|
+ ath_txq_schedule(sc, txq);
|
|
|
}
|
|
|
|
|
|
static bool ath_lookup_legacy(struct ath_buf *bf)
|
|
@@ -1443,8 +1506,8 @@ ath_tx_form_burst(struct ath_softc *sc, struct ath_txq *txq,
|
|
|
} while (1);
|
|
|
}
|
|
|
|
|
|
-static int ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
|
|
|
- struct ath_atx_tid *tid)
|
|
|
+static bool ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
|
|
|
+ struct ath_atx_tid *tid)
|
|
|
{
|
|
|
struct ath_buf *bf;
|
|
|
struct ieee80211_tx_info *tx_info;
|
|
@@ -1452,18 +1515,21 @@ static int ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
|
|
|
int aggr_len = 0;
|
|
|
bool aggr;
|
|
|
|
|
|
+ if (!ath_tid_has_buffered(tid))
|
|
|
+ return false;
|
|
|
+
|
|
|
INIT_LIST_HEAD(&bf_q);
|
|
|
|
|
|
bf = ath_tx_get_tid_subframe(sc, txq, tid);
|
|
|
if (!bf)
|
|
|
- return -ENOENT;
|
|
|
+ return false;
|
|
|
|
|
|
tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
|
|
|
aggr = !!(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
|
|
|
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);
|
|
|
- return -ENOBUFS;
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
ath_set_rates(tid->an->vif, tid->an->sta, bf);
|
|
@@ -1473,7 +1539,7 @@ static int ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
|
|
|
ath_tx_form_burst(sc, txq, tid, &bf_q, bf);
|
|
|
|
|
|
if (list_empty(&bf_q))
|
|
|
- return -ENOENT;
|
|
|
+ return false;
|
|
|
|
|
|
if (tid->clear_ps_filter || tid->an->no_ps_filter) {
|
|
|
tid->clear_ps_filter = false;
|
|
@@ -1482,7 +1548,7 @@ static int ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
|
|
|
|
|
|
ath_tx_fill_desc(sc, bf, txq, aggr_len);
|
|
|
ath_tx_txqaddbuf(sc, txq, &bf_q, false);
|
|
|
- return 0;
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
|
|
@@ -1545,49 +1611,52 @@ void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc,
|
|
|
{
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
|
|
struct ath_atx_tid *tid;
|
|
|
- struct ieee80211_txq *queue;
|
|
|
+ struct ath_txq *txq;
|
|
|
int tidno;
|
|
|
|
|
|
ath_dbg(common, XMIT, "%s called\n", __func__);
|
|
|
|
|
|
for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) {
|
|
|
tid = ath_node_to_tid(an, tidno);
|
|
|
- queue = container_of((void *)tid,
|
|
|
- struct ieee80211_txq, drv_priv);
|
|
|
+ txq = tid->txq;
|
|
|
+
|
|
|
+ ath_txq_lock(sc, txq);
|
|
|
+
|
|
|
+ if (list_empty(&tid->list)) {
|
|
|
+ ath_txq_unlock(sc, txq);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
if (!skb_queue_empty(&tid->retry_q))
|
|
|
ieee80211_sta_set_buffered(sta, tid->tidno, true);
|
|
|
|
|
|
+ list_del_init(&tid->list);
|
|
|
+
|
|
|
+ ath_txq_unlock(sc, txq);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an)
|
|
|
{
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
|
|
- struct ieee80211_txq *queue;
|
|
|
struct ath_atx_tid *tid;
|
|
|
struct ath_txq *txq;
|
|
|
int tidno;
|
|
|
- bool sched, wake = false;
|
|
|
|
|
|
ath_dbg(common, XMIT, "%s called\n", __func__);
|
|
|
|
|
|
for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) {
|
|
|
tid = ath_node_to_tid(an, tidno);
|
|
|
txq = tid->txq;
|
|
|
- queue = container_of((void *)tid,
|
|
|
- struct ieee80211_txq, drv_priv);
|
|
|
|
|
|
ath_txq_lock(sc, txq);
|
|
|
tid->clear_ps_filter = true;
|
|
|
- sched = !skb_queue_empty(&tid->retry_q);
|
|
|
- ath_txq_unlock(sc, txq);
|
|
|
-
|
|
|
- if (sched && ieee80211_schedule_txq(sc->hw, queue))
|
|
|
- wake = true;
|
|
|
+ if (ath_tid_has_buffered(tid)) {
|
|
|
+ ath_tx_queue_tid(sc, tid);
|
|
|
+ ath_txq_schedule(sc, txq);
|
|
|
+ }
|
|
|
+ ath_txq_unlock_complete(sc, txq);
|
|
|
}
|
|
|
- if (wake)
|
|
|
- ath_txq_schedule(sc);
|
|
|
}
|
|
|
|
|
|
void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
|
|
@@ -1879,44 +1948,86 @@ void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq)
|
|
|
/* For each acq entry, for each tid, try to schedule packets
|
|
|
* for transmit until ampdu_depth has reached min Q depth.
|
|
|
*/
|
|
|
-void ath_txq_schedule(struct ath_softc *sc)
|
|
|
+void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
|
|
|
{
|
|
|
- struct ieee80211_hw *hw = sc->hw;
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
|
|
- struct ieee80211_txq *queue;
|
|
|
struct ath_atx_tid *tid;
|
|
|
- struct ath_txq *txq;
|
|
|
- int ret = 0;
|
|
|
+ struct list_head *tid_list;
|
|
|
+ struct ath_acq *acq;
|
|
|
+ bool active = AIRTIME_ACTIVE(sc->airtime_flags);
|
|
|
|
|
|
- if (test_bit(ATH_OP_HW_RESET, &common->op_flags))
|
|
|
+ if (txq->mac80211_qnum < 0)
|
|
|
return;
|
|
|
|
|
|
- queue = ieee80211_next_txq(hw);
|
|
|
- if (!queue)
|
|
|
+ if (test_bit(ATH_OP_HW_RESET, &common->op_flags))
|
|
|
return;
|
|
|
|
|
|
- tid = (struct ath_atx_tid *)queue->drv_priv;
|
|
|
- txq = tid->txq;
|
|
|
+ spin_lock_bh(&sc->chan_lock);
|
|
|
+ rcu_read_lock();
|
|
|
+ acq = &sc->cur_chan->acq[txq->mac80211_qnum];
|
|
|
|
|
|
- ath_txq_lock(sc, txq);
|
|
|
- if (txq->mac80211_qnum < 0)
|
|
|
+ if (sc->cur_chan->stopped)
|
|
|
goto out;
|
|
|
|
|
|
- spin_lock_bh(&sc->chan_lock);
|
|
|
- rcu_read_lock();
|
|
|
+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);
|
|
|
|
|
|
- if (!sc->cur_chan->stopped)
|
|
|
- ret = ath_tx_sched_aggr(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 (!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 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);
|
|
|
+}
|
|
|
|
|
|
-out:
|
|
|
+void ath_txq_schedule_all(struct ath_softc *sc)
|
|
|
+{
|
|
|
+ struct ath_txq *txq;
|
|
|
+ int i;
|
|
|
|
|
|
- if (ret != -ENOENT)
|
|
|
- ieee80211_schedule_txq(hw, queue);
|
|
|
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
|
|
|
+ txq = sc->tx.txq_map[i];
|
|
|
|
|
|
- ath_txq_unlock(sc, txq);
|
|
|
+ spin_lock_bh(&txq->axq_lock);
|
|
|
+ ath_txq_schedule(sc, txq);
|
|
|
+ spin_unlock_bh(&txq->axq_lock);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/***********/
|
|
@@ -2534,6 +2645,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
|
|
|
|
|
|
if (list_empty(&txq->axq_q)) {
|
|
|
txq->axq_link = NULL;
|
|
|
+ ath_txq_schedule(sc, txq);
|
|
|
break;
|
|
|
}
|
|
|
bf = list_first_entry(&txq->axq_q, struct ath_buf, list);
|
|
@@ -2585,7 +2697,6 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
|
|
|
ath_tx_process_buffer(sc, txq, &ts, bf, &bf_head);
|
|
|
}
|
|
|
ath_txq_unlock_complete(sc, txq);
|
|
|
- ath_txq_schedule(sc);
|
|
|
}
|
|
|
|
|
|
void ath_tx_tasklet(struct ath_softc *sc)
|
|
@@ -2600,7 +2711,6 @@ void ath_tx_tasklet(struct ath_softc *sc)
|
|
|
ath_tx_processq(sc, &sc->tx.txq[i]);
|
|
|
}
|
|
|
rcu_read_unlock();
|
|
|
- ath_txq_schedule(sc);
|
|
|
}
|
|
|
|
|
|
void ath_tx_edma_tasklet(struct ath_softc *sc)
|
|
@@ -2686,7 +2796,6 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
|
|
|
ath_txq_unlock_complete(sc, txq);
|
|
|
}
|
|
|
rcu_read_unlock();
|
|
|
- ath_txq_schedule(sc);
|
|
|
}
|
|
|
|
|
|
/*****************/
|
|
@@ -2766,6 +2875,7 @@ void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an)
|
|
|
tid->baw_head = tid->baw_tail = 0;
|
|
|
tid->active = false;
|
|
|
tid->clear_ps_filter = true;
|
|
|
+ tid->has_queued = false;
|
|
|
__skb_queue_head_init(&tid->retry_q);
|
|
|
INIT_LIST_HEAD(&tid->list);
|
|
|
acno = TID_TO_WME_AC(tidno);
|