From: Felix Fietkau Date: Wed, 3 Nov 2010 14:55:59 +0000 (+0000) Subject: ath9k: fix queue pending frame counter tracking by cleaning up tx queue selection X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=8bab3639b3053479badcb91cce12d634f248b906;p=openwrt%2Fstaging%2Fnbd.git ath9k: fix queue pending frame counter tracking by cleaning up tx queue selection SVN-Revision: 23823 --- diff --git a/package/mac80211/patches/572-ath9k_xmit_queue_cleanup.patch b/package/mac80211/patches/572-ath9k_xmit_queue_cleanup.patch new file mode 100644 index 0000000000..522c1ef969 --- /dev/null +++ b/package/mac80211/patches/572-ath9k_xmit_queue_cleanup.patch @@ -0,0 +1,686 @@ +--- a/drivers/net/wireless/ath/ath9k/ath9k.h ++++ b/drivers/net/wireless/ath/ath9k/ath9k.h +@@ -195,7 +195,6 @@ enum ATH_AGGR_STATUS { + + #define ATH_TXFIFO_DEPTH 8 + struct ath_txq { +- int axq_class; + u32 axq_qnum; + u32 *axq_link; + struct list_head axq_q; +@@ -208,11 +207,12 @@ struct ath_txq { + struct list_head txq_fifo_pending; + u8 txq_headidx; + u8 txq_tailidx; ++ int pending_frames; + }; + + struct ath_atx_ac { ++ struct ath_txq *txq; + int sched; +- int qnum; + struct list_head list; + struct list_head tid_q; + }; +@@ -290,12 +290,11 @@ struct ath_tx_control { + struct ath_tx { + u16 seq_no; + u32 txqsetup; +- int hwq_map[WME_NUM_AC]; + spinlock_t txbuflock; + struct list_head txbuf; + struct ath_txq txq[ATH9K_NUM_TX_QUEUES]; + struct ath_descdma txdma; +- int pending_frames[WME_NUM_AC]; ++ struct ath_txq *txq_map[WME_NUM_AC]; + }; + + struct ath_rx_edma { +@@ -325,7 +324,6 @@ 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_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq); +-int ath_tx_setup(struct ath_softc *sc, int haltype); + void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx); + void ath_draintxq(struct ath_softc *sc, + struct ath_txq *txq, bool retry_tx); +@@ -665,7 +663,6 @@ struct ath_wiphy { + + void ath9k_tasklet(unsigned long data); + int ath_reset(struct ath_softc *sc, bool retry_tx); +-int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc); + int ath_cabq_update(struct ath_softc *); + + static inline void ath_read_cachesize(struct ath_common *common, int *csz) +--- a/drivers/net/wireless/ath/ath9k/beacon.c ++++ b/drivers/net/wireless/ath/ath9k/beacon.c +@@ -28,7 +28,7 @@ int ath_beaconq_config(struct ath_softc + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_tx_queue_info qi, qi_be; +- int qnum; ++ struct ath_txq *txq; + + ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi); + if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { +@@ -38,8 +38,8 @@ int ath_beaconq_config(struct ath_softc + qi.tqi_cwmax = 0; + } else { + /* Adhoc mode; important thing is to use 2x cwmin. */ +- qnum = sc->tx.hwq_map[WME_AC_BE]; +- ath9k_hw_get_txq_props(ah, qnum, &qi_be); ++ txq = sc->tx.txq_map[WME_AC_BE]; ++ ath9k_hw_get_txq_props(ah, txq->axq_qnum, &qi_be); + qi.tqi_aifs = qi_be.tqi_aifs; + qi.tqi_cwmin = 4*qi_be.tqi_cwmin; + qi.tqi_cwmax = qi_be.tqi_cwmax; +--- a/drivers/net/wireless/ath/ath9k/common.h ++++ b/drivers/net/wireless/ath/ath9k/common.h +@@ -31,10 +31,11 @@ + #define WME_MAX_BA WME_BA_BMP_SIZE + #define ATH_TID_MAX_BUFS (2 * WME_MAX_BA) + +-#define WME_AC_BE 0 +-#define WME_AC_BK 1 +-#define WME_AC_VI 2 +-#define WME_AC_VO 3 ++/* These must match mac80211 skb queue mapping numbers */ ++#define WME_AC_VO 0 ++#define WME_AC_VI 1 ++#define WME_AC_BE 2 ++#define WME_AC_BK 3 + #define WME_NUM_AC 4 + + #define ATH_RSSI_DUMMY_MARKER 0x127 +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -331,7 +331,7 @@ void ath_paprd_calibrate(struct work_str + struct ath_tx_control txctl; + struct ath9k_hw_cal_data *caldata = ah->caldata; + struct ath_common *common = ath9k_hw_common(ah); +- int qnum, ftype; ++ int ftype; + int chain_ok = 0; + int chain; + int len = 1800; +@@ -358,8 +358,7 @@ void ath_paprd_calibrate(struct work_str + memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN); + + memset(&txctl, 0, sizeof(txctl)); +- qnum = sc->tx.hwq_map[WME_AC_BE]; +- txctl.txq = &sc->tx.txq[qnum]; ++ txctl.txq = sc->tx.txq_map[WME_AC_BE]; + + ath9k_ps_wakeup(sc); + ar9003_paprd_init_table(ah); +@@ -1025,56 +1024,6 @@ int ath_reset(struct ath_softc *sc, bool + return r; + } + +-static int ath_get_hal_qnum(u16 queue, struct ath_softc *sc) +-{ +- int qnum; +- +- switch (queue) { +- case 0: +- qnum = sc->tx.hwq_map[WME_AC_VO]; +- break; +- case 1: +- qnum = sc->tx.hwq_map[WME_AC_VI]; +- break; +- case 2: +- qnum = sc->tx.hwq_map[WME_AC_BE]; +- break; +- case 3: +- qnum = sc->tx.hwq_map[WME_AC_BK]; +- break; +- default: +- qnum = sc->tx.hwq_map[WME_AC_BE]; +- break; +- } +- +- return qnum; +-} +- +-int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc) +-{ +- int qnum; +- +- switch (queue) { +- case WME_AC_VO: +- qnum = 0; +- break; +- case WME_AC_VI: +- qnum = 1; +- break; +- case WME_AC_BE: +- qnum = 2; +- break; +- case WME_AC_BK: +- qnum = 3; +- break; +- default: +- qnum = -1; +- break; +- } +- +- return qnum; +-} +- + /* XXX: Remove me once we don't depend on ath9k_channel for all + * this redundant data */ + void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw, +@@ -1244,7 +1193,6 @@ static int ath9k_tx(struct ieee80211_hw + struct ath_tx_control txctl; + int padpos, padsize; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; +- int qnum; + + if (aphy->state != ATH_WIPHY_ACTIVE && aphy->state != ATH_WIPHY_SCAN) { + ath_print(common, ATH_DBG_XMIT, +@@ -1317,8 +1265,7 @@ static int ath9k_tx(struct ieee80211_hw + memmove(skb->data, skb->data + padsize, padpos); + } + +- qnum = ath_get_hal_qnum(skb_get_queue_mapping(skb), sc); +- txctl.txq = &sc->tx.txq[qnum]; ++ txctl.txq = sc->tx.txq_map[skb_get_queue_mapping(skb)]; + + ath_print(common, ATH_DBG_XMIT, "transmitting packet, skb: %p\n", skb); + +@@ -1802,12 +1749,15 @@ static int ath9k_conf_tx(struct ieee8021 + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); ++ struct ath_txq *txq; + struct ath9k_tx_queue_info qi; +- int ret = 0, qnum; ++ int ret = 0; + + if (queue >= WME_NUM_AC) + return 0; + ++ txq = sc->tx.txq_map[queue]; ++ + mutex_lock(&sc->mutex); + + memset(&qi, 0, sizeof(struct ath9k_tx_queue_info)); +@@ -1816,20 +1766,19 @@ static int ath9k_conf_tx(struct ieee8021 + qi.tqi_cwmin = params->cw_min; + qi.tqi_cwmax = params->cw_max; + qi.tqi_burstTime = params->txop; +- qnum = ath_get_hal_qnum(queue, sc); + + ath_print(common, ATH_DBG_CONFIG, + "Configure tx [queue/halq] [%d/%d], " + "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n", +- queue, qnum, params->aifs, params->cw_min, ++ queue, txq->axq_qnum, params->aifs, params->cw_min, + params->cw_max, params->txop); + +- ret = ath_txq_update(sc, qnum, &qi); ++ ret = ath_txq_update(sc, txq->axq_qnum, &qi); + if (ret) + ath_print(common, ATH_DBG_FATAL, "TXQ Update failed\n"); + + if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) +- if ((qnum == sc->tx.hwq_map[WME_AC_BE]) && !ret) ++ if (queue == WME_AC_BE && !ret) + ath_beaconq_config(sc); + + mutex_unlock(&sc->mutex); +--- a/drivers/net/wireless/ath/ath9k/xmit.c ++++ b/drivers/net/wireless/ath/ath9k/xmit.c +@@ -124,7 +124,7 @@ static void ath_tx_queue_tid(struct ath_ + + static void ath_tx_resume_tid(struct ath_softc *sc, struct ath_atx_tid *tid) + { +- struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum]; ++ struct ath_txq *txq = tid->ac->txq; + + WARN_ON(!tid->paused); + +@@ -142,7 +142,7 @@ unlock: + + static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid) + { +- struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum]; ++ struct ath_txq *txq = tid->ac->txq; + struct ath_buf *bf; + struct list_head bf_head; + struct ath_tx_status ts; +@@ -817,7 +817,7 @@ void ath_tx_aggr_stop(struct ath_softc * + { + struct ath_node *an = (struct ath_node *)sta->drv_priv; + struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid); +- struct ath_txq *txq = &sc->tx.txq[txtid->ac->qnum]; ++ struct ath_txq *txq = txtid->ac->txq; + + if (txtid->state & AGGR_CLEANUP) + return; +@@ -888,10 +888,16 @@ struct ath_txq *ath_txq_setup(struct ath + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_tx_queue_info qi; ++ static const int subtype_txq_to_hwq[] = { ++ [WME_AC_BE] = ATH_TXQ_AC_BE, ++ [WME_AC_BK] = ATH_TXQ_AC_BK, ++ [WME_AC_VI] = ATH_TXQ_AC_VI, ++ [WME_AC_VO] = ATH_TXQ_AC_VO, ++ }; + int qnum, i; + + memset(&qi, 0, sizeof(qi)); +- qi.tqi_subtype = subtype; ++ qi.tqi_subtype = subtype_txq_to_hwq[subtype]; + qi.tqi_aifs = ATH9K_TXQ_USEDEFAULT; + qi.tqi_cwmin = ATH9K_TXQ_USEDEFAULT; + qi.tqi_cwmax = ATH9K_TXQ_USEDEFAULT; +@@ -940,7 +946,6 @@ struct ath_txq *ath_txq_setup(struct ath + if (!ATH_TXQ_SETUP(sc, qnum)) { + struct ath_txq *txq = &sc->tx.txq[qnum]; + +- txq->axq_class = subtype; + txq->axq_qnum = qnum; + txq->axq_link = NULL; + INIT_LIST_HEAD(&txq->axq_q); +@@ -1210,24 +1215,6 @@ void ath_txq_schedule(struct ath_softc * + } + } + +-int ath_tx_setup(struct ath_softc *sc, int haltype) +-{ +- struct ath_txq *txq; +- +- if (haltype >= ARRAY_SIZE(sc->tx.hwq_map)) { +- ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL, +- "HAL AC %u out of range, max %zu!\n", +- haltype, ARRAY_SIZE(sc->tx.hwq_map)); +- return 0; +- } +- txq = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, haltype); +- if (txq != NULL) { +- sc->tx.hwq_map[haltype] = txq->axq_qnum; +- return 1; +- } else +- return 0; +-} +- + /***********/ + /* TX, DMA */ + /***********/ +@@ -1747,6 +1734,7 @@ int ath_tx_start(struct ieee80211_hw *hw + return -1; + } + ++ q = skb_get_queue_mapping(skb); + r = ath_tx_setup_buffer(hw, bf, skb, txctl); + if (unlikely(r)) { + ath_print(common, ATH_DBG_FATAL, "TX mem alloc failure\n"); +@@ -1756,8 +1744,9 @@ int ath_tx_start(struct ieee80211_hw *hw + * we will at least have to run TX completionon one buffer + * on the queue */ + spin_lock_bh(&txq->axq_lock); +- if (!txq->stopped && txq->axq_depth > 1) { +- ath_mac80211_stop_queue(sc, skb_get_queue_mapping(skb)); ++ if (txq == sc->tx.txq_map[q] && !txq->stopped && ++ txq->axq_depth > 1) { ++ ath_mac80211_stop_queue(sc, q); + txq->stopped = 1; + } + spin_unlock_bh(&txq->axq_lock); +@@ -1767,13 +1756,10 @@ int ath_tx_start(struct ieee80211_hw *hw + return r; + } + +- q = skb_get_queue_mapping(skb); +- if (q >= 4) +- q = 0; +- + spin_lock_bh(&txq->axq_lock); +- if (++sc->tx.pending_frames[q] > ATH_MAX_QDEPTH && !txq->stopped) { +- ath_mac80211_stop_queue(sc, skb_get_queue_mapping(skb)); ++ if (txq == sc->tx.txq_map[q] && ++ ++txq->pending_frames > ATH_MAX_QDEPTH && !txq->stopped) { ++ ath_mac80211_stop_queue(sc, q); + txq->stopped = 1; + } + spin_unlock_bh(&txq->axq_lock); +@@ -1887,12 +1873,12 @@ static void ath_tx_complete(struct ath_s + if (unlikely(tx_info->pad[0] & ATH_TX_INFO_FRAME_TYPE_INTERNAL)) + ath9k_tx_status(hw, skb); + else { +- q = skb_get_queue_mapping(skb); +- if (q >= 4) +- q = 0; ++ struct ath_txq *txq; + +- if (--sc->tx.pending_frames[q] < 0) +- sc->tx.pending_frames[q] = 0; ++ q = skb_get_queue_mapping(skb); ++ txq = sc->tx.txq_map[q]; ++ if (--txq->pending_frames < 0) ++ txq->pending_frames = 0; + + ieee80211_tx_status(hw, skb); + } +@@ -1927,7 +1913,7 @@ static void ath_tx_complete_buf(struct a + else + complete(&sc->paprd_complete); + } else { +- ath_debug_stat_tx(sc, txq, bf, ts); ++ ath_debug_stat_tx(sc, bf, ts); + ath_tx_complete(sc, skb, bf->aphy, tx_flags); + } + /* At this point, skb (bf->bf_mpdu) is consumed...make sure we don't +@@ -2018,16 +2004,13 @@ static void ath_tx_rc_status(struct ath_ + tx_info->status.rates[tx_rateindex].count = ts->ts_longretry + 1; + } + +-static void ath_wake_mac80211_queue(struct ath_softc *sc, struct ath_txq *txq) ++static void ath_wake_mac80211_queue(struct ath_softc *sc, int qnum) + { +- int qnum; +- +- qnum = ath_get_mac80211_qnum(txq->axq_class, sc); +- if (qnum == -1) +- return; ++ struct ath_txq *txq; + ++ txq = sc->tx.txq_map[qnum]; + spin_lock_bh(&txq->axq_lock); +- if (txq->stopped && sc->tx.pending_frames[qnum] < ATH_MAX_QDEPTH) { ++ if (txq->stopped && txq->pending_frames < ATH_MAX_QDEPTH) { + if (ath_mac80211_start_queue(sc, qnum)) + txq->stopped = 0; + } +@@ -2044,6 +2027,7 @@ static void ath_tx_processq(struct ath_s + struct ath_tx_status ts; + int txok; + int status; ++ int qnum; + + ath_print(common, ATH_DBG_QUEUE, "tx queue %d (%x), link %p\n", + txq->axq_qnum, ath9k_hw_gettxbuf(sc->sc_ah, txq->axq_qnum), +@@ -2119,12 +2103,15 @@ static void ath_tx_processq(struct ath_s + ath_tx_rc_status(bf, &ts, txok ? 0 : 1, txok, true); + } + ++ qnum = skb_get_queue_mapping(bf->bf_mpdu); ++ + if (bf_isampdu(bf)) + ath_tx_complete_aggr(sc, txq, bf, &bf_head, &ts, txok); + else + ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, txok, 0); + +- ath_wake_mac80211_queue(sc, txq); ++ if (txq == sc->tx.txq_map[qnum]) ++ ath_wake_mac80211_queue(sc, qnum); + + spin_lock_bh(&txq->axq_lock); + if (sc->sc_flags & SC_OP_TXAGGR) +@@ -2194,6 +2181,7 @@ void ath_tx_edma_tasklet(struct ath_soft + struct list_head bf_head; + int status; + int txok; ++ int qnum; + + for (;;) { + status = ath9k_hw_txprocdesc(ah, NULL, (void *)&txs); +@@ -2237,13 +2225,16 @@ void ath_tx_edma_tasklet(struct ath_soft + ath_tx_rc_status(bf, &txs, txok ? 0 : 1, txok, true); + } + ++ qnum = skb_get_queue_mapping(bf->bf_mpdu); ++ + if (bf_isampdu(bf)) + ath_tx_complete_aggr(sc, txq, bf, &bf_head, &txs, txok); + else + ath_tx_complete_buf(sc, bf, txq, &bf_head, + &txs, txok, 0); + +- ath_wake_mac80211_queue(sc, txq); ++ if (txq == sc->tx.txq_map[qnum]) ++ ath_wake_mac80211_queue(sc, qnum); + + spin_lock_bh(&txq->axq_lock); + if (!list_empty(&txq->txq_fifo_pending)) { +@@ -2375,7 +2366,7 @@ void ath_tx_node_init(struct ath_softc * + for (acno = 0, ac = &an->ac[acno]; + acno < WME_NUM_AC; acno++, ac++) { + ac->sched = false; +- ac->qnum = sc->tx.hwq_map[acno]; ++ ac->txq = sc->tx.txq_map[acno]; + INIT_LIST_HEAD(&ac->tid_q); + } + } +@@ -2385,17 +2376,13 @@ void ath_tx_node_cleanup(struct ath_soft + struct ath_atx_ac *ac; + struct ath_atx_tid *tid; + struct ath_txq *txq; +- int i, tidno; ++ int tidno; + + for (tidno = 0, tid = &an->tid[tidno]; + tidno < WME_NUM_TID; tidno++, tid++) { +- i = tid->ac->qnum; +- +- if (!ATH_TXQ_SETUP(sc, i)) +- continue; + +- txq = &sc->tx.txq[i]; + ac = tid->ac; ++ txq = ac->txq; + + spin_lock_bh(&txq->axq_lock); + +--- a/drivers/net/wireless/ath/ath9k/hw.h ++++ b/drivers/net/wireless/ath/ath9k/hw.h +@@ -157,6 +157,13 @@ + #define PAPRD_GAIN_TABLE_ENTRIES 32 + #define PAPRD_TABLE_SZ 24 + ++enum ath_hw_txq_subtype { ++ ATH_TXQ_AC_BE = 0, ++ ATH_TXQ_AC_BK = 1, ++ ATH_TXQ_AC_VI = 2, ++ ATH_TXQ_AC_VO = 3, ++}; ++ + enum ath_ini_subsys { + ATH_INI_PRE = 0, + ATH_INI_CORE, +--- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c ++++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +@@ -20,8 +20,15 @@ + /* TX */ + /******/ + ++static const int subtype_txq_to_hwq[] = { ++ [WME_AC_BE] = ATH_TXQ_AC_BE, ++ [WME_AC_BK] = ATH_TXQ_AC_BK, ++ [WME_AC_VI] = ATH_TXQ_AC_VI, ++ [WME_AC_VO] = ATH_TXQ_AC_VO, ++}; ++ + #define ATH9K_HTC_INIT_TXQ(subtype) do { \ +- qi.tqi_subtype = subtype; \ ++ qi.tqi_subtype = subtype_txq_to_hwq[subtype]; \ + qi.tqi_aifs = ATH9K_TXQ_USEDEFAULT; \ + qi.tqi_cwmin = ATH9K_TXQ_USEDEFAULT; \ + qi.tqi_cwmax = ATH9K_TXQ_USEDEFAULT; \ +--- a/drivers/net/wireless/ath/ath9k/init.c ++++ b/drivers/net/wireless/ath/ath9k/init.c +@@ -396,7 +396,8 @@ static void ath9k_init_crypto(struct ath + + static int ath9k_init_btcoex(struct ath_softc *sc) + { +- int r, qnum; ++ struct ath_txq *txq; ++ int r; + + switch (sc->sc_ah->btcoex_hw.scheme) { + case ATH_BTCOEX_CFG_NONE: +@@ -409,8 +410,8 @@ static int ath9k_init_btcoex(struct ath_ + r = ath_init_btcoex_timer(sc); + if (r) + return -1; +- qnum = sc->tx.hwq_map[WME_AC_BE]; +- ath9k_hw_init_btcoex_hw(sc->sc_ah, qnum); ++ txq = sc->tx.txq_map[WME_AC_BE]; ++ ath9k_hw_init_btcoex_hw(sc->sc_ah, txq->axq_qnum); + sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; + break; + default: +@@ -423,59 +424,18 @@ static int ath9k_init_btcoex(struct ath_ + + static int ath9k_init_queues(struct ath_softc *sc) + { +- struct ath_common *common = ath9k_hw_common(sc->sc_ah); + int i = 0; + +- for (i = 0; i < ARRAY_SIZE(sc->tx.hwq_map); i++) +- sc->tx.hwq_map[i] = -1; +- + sc->beacon.beaconq = ath9k_hw_beaconq_setup(sc->sc_ah); +- if (sc->beacon.beaconq == -1) { +- ath_print(common, ATH_DBG_FATAL, +- "Unable to setup a beacon xmit queue\n"); +- goto err; +- } +- + sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0); +- if (sc->beacon.cabq == NULL) { +- ath_print(common, ATH_DBG_FATAL, +- "Unable to setup CAB xmit queue\n"); +- goto err; +- } + + sc->config.cabqReadytime = ATH_CABQ_READY_TIME; + ath_cabq_update(sc); + +- if (!ath_tx_setup(sc, WME_AC_BK)) { +- ath_print(common, ATH_DBG_FATAL, +- "Unable to setup xmit queue for BK traffic\n"); +- goto err; +- } +- +- if (!ath_tx_setup(sc, WME_AC_BE)) { +- ath_print(common, ATH_DBG_FATAL, +- "Unable to setup xmit queue for BE traffic\n"); +- goto err; +- } +- if (!ath_tx_setup(sc, WME_AC_VI)) { +- ath_print(common, ATH_DBG_FATAL, +- "Unable to setup xmit queue for VI traffic\n"); +- goto err; +- } +- if (!ath_tx_setup(sc, WME_AC_VO)) { +- ath_print(common, ATH_DBG_FATAL, +- "Unable to setup xmit queue for VO traffic\n"); +- goto err; +- } ++ for (i = 0; i < WME_NUM_AC; i++) ++ sc->tx.txq_map[i] = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, i); + + return 0; +- +-err: +- for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) +- if (ATH_TXQ_SETUP(sc, i)) +- ath_tx_cleanupq(sc, &sc->tx.txq[i]); +- +- return -EIO; + } + + static int ath9k_init_channels_rates(struct ath_softc *sc) +--- a/drivers/net/wireless/ath/ath9k/virtual.c ++++ b/drivers/net/wireless/ath/ath9k/virtual.c +@@ -187,7 +187,7 @@ static int ath9k_send_nullfunc(struct at + info->control.rates[1].idx = -1; + + memset(&txctl, 0, sizeof(struct ath_tx_control)); +- txctl.txq = &sc->tx.txq[sc->tx.hwq_map[WME_AC_VO]]; ++ txctl.txq = sc->tx.txq_map[WME_AC_VO]; + txctl.frame_type = ps ? ATH9K_IFT_PAUSE : ATH9K_IFT_UNPAUSE; + + if (ath_tx_start(aphy->hw, skb, &txctl) != 0) +--- a/drivers/net/wireless/ath/ath9k/debug.c ++++ b/drivers/net/wireless/ath/ath9k/debug.c +@@ -579,10 +579,10 @@ static const struct file_operations fops + do { \ + len += snprintf(buf + len, size - len, \ + "%s%13u%11u%10u%10u\n", str, \ +- sc->debug.stats.txstats[sc->tx.hwq_map[WME_AC_BE]].elem, \ +- sc->debug.stats.txstats[sc->tx.hwq_map[WME_AC_BK]].elem, \ +- sc->debug.stats.txstats[sc->tx.hwq_map[WME_AC_VI]].elem, \ +- sc->debug.stats.txstats[sc->tx.hwq_map[WME_AC_VO]].elem); \ ++ sc->debug.stats.txstats[WME_AC_BE].elem, \ ++ sc->debug.stats.txstats[WME_AC_BK].elem, \ ++ sc->debug.stats.txstats[WME_AC_VI].elem, \ ++ sc->debug.stats.txstats[WME_AC_VO].elem); \ + } while(0) + + static ssize_t read_file_xmit(struct file *file, char __user *user_buf, +@@ -624,33 +624,35 @@ static ssize_t read_file_xmit(struct fil + return retval; + } + +-void ath_debug_stat_tx(struct ath_softc *sc, struct ath_txq *txq, +- struct ath_buf *bf, struct ath_tx_status *ts) ++void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, ++ struct ath_tx_status *ts) + { +- TX_STAT_INC(txq->axq_qnum, tx_pkts_all); +- sc->debug.stats.txstats[txq->axq_qnum].tx_bytes_all += bf->bf_mpdu->len; ++ int qnum = skb_get_queue_mapping(bf->bf_mpdu); ++ ++ TX_STAT_INC(qnum, tx_pkts_all); ++ sc->debug.stats.txstats[qnum].tx_bytes_all += bf->bf_mpdu->len; + + if (bf_isampdu(bf)) { + if (bf_isxretried(bf)) +- TX_STAT_INC(txq->axq_qnum, a_xretries); ++ TX_STAT_INC(qnum, a_xretries); + else +- TX_STAT_INC(txq->axq_qnum, a_completed); ++ TX_STAT_INC(qnum, a_completed); + } else { +- TX_STAT_INC(txq->axq_qnum, completed); ++ TX_STAT_INC(qnum, completed); + } + + if (ts->ts_status & ATH9K_TXERR_FIFO) +- TX_STAT_INC(txq->axq_qnum, fifo_underrun); ++ TX_STAT_INC(qnum, fifo_underrun); + if (ts->ts_status & ATH9K_TXERR_XTXOP) +- TX_STAT_INC(txq->axq_qnum, xtxop); ++ TX_STAT_INC(qnum, xtxop); + if (ts->ts_status & ATH9K_TXERR_TIMER_EXPIRED) +- TX_STAT_INC(txq->axq_qnum, timer_exp); ++ TX_STAT_INC(qnum, timer_exp); + if (ts->ts_flags & ATH9K_TX_DESC_CFG_ERR) +- TX_STAT_INC(txq->axq_qnum, desc_cfg_err); ++ TX_STAT_INC(qnum, desc_cfg_err); + if (ts->ts_flags & ATH9K_TX_DATA_UNDERRUN) +- TX_STAT_INC(txq->axq_qnum, data_underrun); ++ TX_STAT_INC(qnum, data_underrun); + if (ts->ts_flags & ATH9K_TX_DELIM_UNDERRUN) +- TX_STAT_INC(txq->axq_qnum, delim_underrun); ++ TX_STAT_INC(qnum, delim_underrun); + } + + static const struct file_operations fops_xmit = { +--- a/drivers/net/wireless/ath/ath9k/debug.h ++++ b/drivers/net/wireless/ath/ath9k/debug.h +@@ -169,8 +169,8 @@ void ath9k_exit_debug(struct ath_hw *ah) + int ath9k_debug_create_root(void); + void ath9k_debug_remove_root(void); + void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status); +-void ath_debug_stat_tx(struct ath_softc *sc, struct ath_txq *txq, +- struct ath_buf *bf, struct ath_tx_status *ts); ++void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, ++ struct ath_tx_status *ts); + void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs); + + #else