diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index d4640117fa8c..dab0271f1c1a 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -184,7 +184,8 @@ enum ATH_AGGR_STATUS { #define ATH_TXFIFO_DEPTH 8 struct ath_txq { - u32 axq_qnum; + int mac80211_qnum; /* mac80211 queue number, -1 means not mac80211 Q */ + u32 axq_qnum; /* ath9k hardware queue number */ u32 *axq_link; struct list_head axq_q; spinlock_t axq_lock; @@ -280,6 +281,11 @@ struct ath_tx_control { #define ATH_TX_XRETRY 0x02 #define ATH_TX_BAR 0x04 +/** + * @txq_map: Index is mac80211 queue number. This is + * not necessarily the same as the hardware queue number + * (axq_qnum). + */ struct ath_tx { u16 seq_no; u32 txqsetup; @@ -643,6 +649,7 @@ struct ath_softc { struct ath9k_debug debug; spinlock_t nodes_lock; struct list_head nodes; /* basically, stations */ + unsigned int tx_complete_poll_work_seen; #endif struct ath_beacon_config cur_beacon_conf; struct delayed_work tx_complete_work; diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 9e009ccd0069..b0cb792d38ca 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -629,9 +629,11 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf, if (buf == NULL) return -ENOMEM; - len += sprintf(buf, "Num-Tx-Queues: %i tx-queues-setup: 0x%x\n" + len += sprintf(buf, "Num-Tx-Queues: %i tx-queues-setup: 0x%x" + " poll-work-seen: %u\n" "%30s %10s%10s%10s\n\n", ATH9K_NUM_TX_QUEUES, sc->tx.txqsetup, + sc->tx_complete_poll_work_seen, "BE", "BK", "VI", "VO"); PR("MPDUs Queued: ", queued); diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 59c01ca4379e..5279653c90c7 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -442,9 +442,10 @@ static int ath9k_init_queues(struct ath_softc *sc) sc->config.cabqReadytime = ATH_CABQ_READY_TIME; ath_cabq_update(sc); - for (i = 0; i < WME_NUM_AC; i++) + for (i = 0; i < WME_NUM_AC; i++) { sc->tx.txq_map[i] = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, i); - + sc->tx.txq_map[i]->mac80211_qnum = i; + } return 0; } diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index a83f2c54508c..2ad732d36f86 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -945,7 +945,7 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) [WME_AC_VI] = ATH_TXQ_AC_VI, [WME_AC_VO] = ATH_TXQ_AC_VO, }; - int qnum, i; + int axq_qnum, i; memset(&qi, 0, sizeof(qi)); qi.tqi_subtype = subtype_txq_to_hwq[subtype]; @@ -979,24 +979,25 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE | TXQ_FLAG_TXDESCINT_ENABLE; } - qnum = ath9k_hw_setuptxqueue(ah, qtype, &qi); - if (qnum == -1) { + axq_qnum = ath9k_hw_setuptxqueue(ah, qtype, &qi); + if (axq_qnum == -1) { /* * NB: don't print a message, this happens * normally on parts with too few tx queues */ return NULL; } - if (qnum >= ARRAY_SIZE(sc->tx.txq)) { + if (axq_qnum >= ARRAY_SIZE(sc->tx.txq)) { ath_err(common, "qnum %u out of range, max %zu!\n", - qnum, ARRAY_SIZE(sc->tx.txq)); - ath9k_hw_releasetxqueue(ah, qnum); + axq_qnum, ARRAY_SIZE(sc->tx.txq)); + ath9k_hw_releasetxqueue(ah, axq_qnum); return NULL; } - if (!ATH_TXQ_SETUP(sc, qnum)) { - struct ath_txq *txq = &sc->tx.txq[qnum]; + if (!ATH_TXQ_SETUP(sc, axq_qnum)) { + struct ath_txq *txq = &sc->tx.txq[axq_qnum]; - txq->axq_qnum = qnum; + txq->axq_qnum = axq_qnum; + txq->mac80211_qnum = -1; txq->axq_link = NULL; INIT_LIST_HEAD(&txq->axq_q); INIT_LIST_HEAD(&txq->axq_acq); @@ -1004,14 +1005,14 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) txq->axq_depth = 0; txq->axq_ampdu_depth = 0; txq->axq_tx_inprogress = false; - sc->tx.txqsetup |= 1<tx.txqsetup |= 1<txq_headidx = txq->txq_tailidx = 0; for (i = 0; i < ATH_TXFIFO_DEPTH; i++) INIT_LIST_HEAD(&txq->txq_fifo[i]); INIT_LIST_HEAD(&txq->txq_fifo_pending); } - return &sc->tx.txq[qnum]; + return &sc->tx.txq[axq_qnum]; } int ath_txq_update(struct ath_softc *sc, int qnum, @@ -1973,17 +1974,16 @@ static void ath_tx_rc_status(struct ath_buf *bf, struct ath_tx_status *ts, tx_info->status.rates[tx_rateindex].count = ts->ts_longretry + 1; } -static void ath_wake_mac80211_queue(struct ath_softc *sc, int qnum) +/* Has no locking. Must hold spin_lock_bh(&txq->axq_lock) + * before calling this. + */ +static void __ath_wake_mac80211_queue(struct ath_softc *sc, struct ath_txq *txq) { - struct ath_txq *txq; - - txq = sc->tx.txq_map[qnum]; - spin_lock_bh(&txq->axq_lock); - if (txq->stopped && txq->pending_frames < ATH_MAX_QDEPTH) { - if (ath_mac80211_start_queue(sc, qnum)) + if (txq->mac80211_qnum >= 0 && + txq->stopped && txq->pending_frames < ATH_MAX_QDEPTH) { + if (ath_mac80211_start_queue(sc, txq->mac80211_qnum)) txq->stopped = 0; } - spin_unlock_bh(&txq->axq_lock); } static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) @@ -2086,10 +2086,9 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) else ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, txok, 0); - if (txq == sc->tx.txq_map[qnum]) - ath_wake_mac80211_queue(sc, qnum); - spin_lock_bh(&txq->axq_lock); + __ath_wake_mac80211_queue(sc, txq); + if (sc->sc_flags & SC_OP_TXAGGR) ath_txq_schedule(sc, txq); spin_unlock_bh(&txq->axq_lock); @@ -2103,6 +2102,9 @@ static void ath_tx_complete_poll_work(struct work_struct *work) struct ath_txq *txq; int i; bool needreset = false; +#ifdef CONFIG_ATH9K_DEBUGFS + sc->tx_complete_poll_work_seen++; +#endif for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) if (ATH_TXQ_SETUP(sc, i)) { @@ -2116,6 +2118,34 @@ static void ath_tx_complete_poll_work(struct work_struct *work) } else { txq->axq_tx_inprogress = true; } + } else { + /* If the queue has pending buffers, then it + * should be doing tx work (and have axq_depth). + * Shouldn't get to this state I think..but + * we do. + */ + if (!(sc->sc_flags & (SC_OP_OFFCHANNEL)) && + (txq->pending_frames > 0 || + !list_empty(&txq->axq_acq) || + txq->stopped)) { + ath_err(ath9k_hw_common(sc->sc_ah), + "txq: %p axq_qnum: %u," + " mac80211_qnum: %i" + " axq_link: %p" + " pending frames: %i" + " axq_acq empty: %i" + " stopped: %i" + " axq_depth: 0 Attempting to" + " restart tx logic.\n", + txq, txq->axq_qnum, + txq->mac80211_qnum, + txq->axq_link, + txq->pending_frames, + list_empty(&txq->axq_acq), + txq->stopped); + __ath_wake_mac80211_queue(sc, txq); + ath_txq_schedule(sc, txq); + } } spin_unlock_bh(&txq->axq_lock); } @@ -2212,10 +2242,9 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) ath_tx_complete_buf(sc, bf, txq, &bf_head, &txs, txok, 0); - if (txq == sc->tx.txq_map[qnum]) - ath_wake_mac80211_queue(sc, qnum); - spin_lock_bh(&txq->axq_lock); + __ath_wake_mac80211_queue(sc, txq); + if (!list_empty(&txq->txq_fifo_pending)) { INIT_LIST_HEAD(&bf_head); bf = list_first_entry(&txq->txq_fifo_pending,