diff options
Diffstat (limited to 'drivers/net/wireless/ath')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/ath9k.h | 9 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/debug.c | 4 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/init.c | 5 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/xmit.c | 79 |
4 files changed, 68 insertions, 29 deletions
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 { | |||
184 | 184 | ||
185 | #define ATH_TXFIFO_DEPTH 8 | 185 | #define ATH_TXFIFO_DEPTH 8 |
186 | struct ath_txq { | 186 | struct ath_txq { |
187 | u32 axq_qnum; | 187 | int mac80211_qnum; /* mac80211 queue number, -1 means not mac80211 Q */ |
188 | u32 axq_qnum; /* ath9k hardware queue number */ | ||
188 | u32 *axq_link; | 189 | u32 *axq_link; |
189 | struct list_head axq_q; | 190 | struct list_head axq_q; |
190 | spinlock_t axq_lock; | 191 | spinlock_t axq_lock; |
@@ -280,6 +281,11 @@ struct ath_tx_control { | |||
280 | #define ATH_TX_XRETRY 0x02 | 281 | #define ATH_TX_XRETRY 0x02 |
281 | #define ATH_TX_BAR 0x04 | 282 | #define ATH_TX_BAR 0x04 |
282 | 283 | ||
284 | /** | ||
285 | * @txq_map: Index is mac80211 queue number. This is | ||
286 | * not necessarily the same as the hardware queue number | ||
287 | * (axq_qnum). | ||
288 | */ | ||
283 | struct ath_tx { | 289 | struct ath_tx { |
284 | u16 seq_no; | 290 | u16 seq_no; |
285 | u32 txqsetup; | 291 | u32 txqsetup; |
@@ -643,6 +649,7 @@ struct ath_softc { | |||
643 | struct ath9k_debug debug; | 649 | struct ath9k_debug debug; |
644 | spinlock_t nodes_lock; | 650 | spinlock_t nodes_lock; |
645 | struct list_head nodes; /* basically, stations */ | 651 | struct list_head nodes; /* basically, stations */ |
652 | unsigned int tx_complete_poll_work_seen; | ||
646 | #endif | 653 | #endif |
647 | struct ath_beacon_config cur_beacon_conf; | 654 | struct ath_beacon_config cur_beacon_conf; |
648 | struct delayed_work tx_complete_work; | 655 | 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, | |||
629 | if (buf == NULL) | 629 | if (buf == NULL) |
630 | return -ENOMEM; | 630 | return -ENOMEM; |
631 | 631 | ||
632 | len += sprintf(buf, "Num-Tx-Queues: %i tx-queues-setup: 0x%x\n" | 632 | len += sprintf(buf, "Num-Tx-Queues: %i tx-queues-setup: 0x%x" |
633 | " poll-work-seen: %u\n" | ||
633 | "%30s %10s%10s%10s\n\n", | 634 | "%30s %10s%10s%10s\n\n", |
634 | ATH9K_NUM_TX_QUEUES, sc->tx.txqsetup, | 635 | ATH9K_NUM_TX_QUEUES, sc->tx.txqsetup, |
636 | sc->tx_complete_poll_work_seen, | ||
635 | "BE", "BK", "VI", "VO"); | 637 | "BE", "BK", "VI", "VO"); |
636 | 638 | ||
637 | PR("MPDUs Queued: ", queued); | 639 | 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) | |||
442 | sc->config.cabqReadytime = ATH_CABQ_READY_TIME; | 442 | sc->config.cabqReadytime = ATH_CABQ_READY_TIME; |
443 | ath_cabq_update(sc); | 443 | ath_cabq_update(sc); |
444 | 444 | ||
445 | for (i = 0; i < WME_NUM_AC; i++) | 445 | for (i = 0; i < WME_NUM_AC; i++) { |
446 | sc->tx.txq_map[i] = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, i); | 446 | sc->tx.txq_map[i] = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, i); |
447 | 447 | sc->tx.txq_map[i]->mac80211_qnum = i; | |
448 | } | ||
448 | return 0; | 449 | return 0; |
449 | } | 450 | } |
450 | 451 | ||
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) | |||
945 | [WME_AC_VI] = ATH_TXQ_AC_VI, | 945 | [WME_AC_VI] = ATH_TXQ_AC_VI, |
946 | [WME_AC_VO] = ATH_TXQ_AC_VO, | 946 | [WME_AC_VO] = ATH_TXQ_AC_VO, |
947 | }; | 947 | }; |
948 | int qnum, i; | 948 | int axq_qnum, i; |
949 | 949 | ||
950 | memset(&qi, 0, sizeof(qi)); | 950 | memset(&qi, 0, sizeof(qi)); |
951 | qi.tqi_subtype = subtype_txq_to_hwq[subtype]; | 951 | 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) | |||
979 | qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE | | 979 | qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE | |
980 | TXQ_FLAG_TXDESCINT_ENABLE; | 980 | TXQ_FLAG_TXDESCINT_ENABLE; |
981 | } | 981 | } |
982 | qnum = ath9k_hw_setuptxqueue(ah, qtype, &qi); | 982 | axq_qnum = ath9k_hw_setuptxqueue(ah, qtype, &qi); |
983 | if (qnum == -1) { | 983 | if (axq_qnum == -1) { |
984 | /* | 984 | /* |
985 | * NB: don't print a message, this happens | 985 | * NB: don't print a message, this happens |
986 | * normally on parts with too few tx queues | 986 | * normally on parts with too few tx queues |
987 | */ | 987 | */ |
988 | return NULL; | 988 | return NULL; |
989 | } | 989 | } |
990 | if (qnum >= ARRAY_SIZE(sc->tx.txq)) { | 990 | if (axq_qnum >= ARRAY_SIZE(sc->tx.txq)) { |
991 | ath_err(common, "qnum %u out of range, max %zu!\n", | 991 | ath_err(common, "qnum %u out of range, max %zu!\n", |
992 | qnum, ARRAY_SIZE(sc->tx.txq)); | 992 | axq_qnum, ARRAY_SIZE(sc->tx.txq)); |
993 | ath9k_hw_releasetxqueue(ah, qnum); | 993 | ath9k_hw_releasetxqueue(ah, axq_qnum); |
994 | return NULL; | 994 | return NULL; |
995 | } | 995 | } |
996 | if (!ATH_TXQ_SETUP(sc, qnum)) { | 996 | if (!ATH_TXQ_SETUP(sc, axq_qnum)) { |
997 | struct ath_txq *txq = &sc->tx.txq[qnum]; | 997 | struct ath_txq *txq = &sc->tx.txq[axq_qnum]; |
998 | 998 | ||
999 | txq->axq_qnum = qnum; | 999 | txq->axq_qnum = axq_qnum; |
1000 | txq->mac80211_qnum = -1; | ||
1000 | txq->axq_link = NULL; | 1001 | txq->axq_link = NULL; |
1001 | INIT_LIST_HEAD(&txq->axq_q); | 1002 | INIT_LIST_HEAD(&txq->axq_q); |
1002 | INIT_LIST_HEAD(&txq->axq_acq); | 1003 | INIT_LIST_HEAD(&txq->axq_acq); |
@@ -1004,14 +1005,14 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) | |||
1004 | txq->axq_depth = 0; | 1005 | txq->axq_depth = 0; |
1005 | txq->axq_ampdu_depth = 0; | 1006 | txq->axq_ampdu_depth = 0; |
1006 | txq->axq_tx_inprogress = false; | 1007 | txq->axq_tx_inprogress = false; |
1007 | sc->tx.txqsetup |= 1<<qnum; | 1008 | sc->tx.txqsetup |= 1<<axq_qnum; |
1008 | 1009 | ||
1009 | txq->txq_headidx = txq->txq_tailidx = 0; | 1010 | txq->txq_headidx = txq->txq_tailidx = 0; |
1010 | for (i = 0; i < ATH_TXFIFO_DEPTH; i++) | 1011 | for (i = 0; i < ATH_TXFIFO_DEPTH; i++) |
1011 | INIT_LIST_HEAD(&txq->txq_fifo[i]); | 1012 | INIT_LIST_HEAD(&txq->txq_fifo[i]); |
1012 | INIT_LIST_HEAD(&txq->txq_fifo_pending); | 1013 | INIT_LIST_HEAD(&txq->txq_fifo_pending); |
1013 | } | 1014 | } |
1014 | return &sc->tx.txq[qnum]; | 1015 | return &sc->tx.txq[axq_qnum]; |
1015 | } | 1016 | } |
1016 | 1017 | ||
1017 | int ath_txq_update(struct ath_softc *sc, int qnum, | 1018 | 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, | |||
1973 | tx_info->status.rates[tx_rateindex].count = ts->ts_longretry + 1; | 1974 | tx_info->status.rates[tx_rateindex].count = ts->ts_longretry + 1; |
1974 | } | 1975 | } |
1975 | 1976 | ||
1976 | static void ath_wake_mac80211_queue(struct ath_softc *sc, int qnum) | 1977 | /* Has no locking. Must hold spin_lock_bh(&txq->axq_lock) |
1978 | * before calling this. | ||
1979 | */ | ||
1980 | static void __ath_wake_mac80211_queue(struct ath_softc *sc, struct ath_txq *txq) | ||
1977 | { | 1981 | { |
1978 | struct ath_txq *txq; | 1982 | if (txq->mac80211_qnum >= 0 && |
1979 | 1983 | txq->stopped && txq->pending_frames < ATH_MAX_QDEPTH) { | |
1980 | txq = sc->tx.txq_map[qnum]; | 1984 | if (ath_mac80211_start_queue(sc, txq->mac80211_qnum)) |
1981 | spin_lock_bh(&txq->axq_lock); | ||
1982 | if (txq->stopped && txq->pending_frames < ATH_MAX_QDEPTH) { | ||
1983 | if (ath_mac80211_start_queue(sc, qnum)) | ||
1984 | txq->stopped = 0; | 1985 | txq->stopped = 0; |
1985 | } | 1986 | } |
1986 | spin_unlock_bh(&txq->axq_lock); | ||
1987 | } | 1987 | } |
1988 | 1988 | ||
1989 | static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) | 1989 | 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) | |||
2086 | else | 2086 | else |
2087 | ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, txok, 0); | 2087 | ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, txok, 0); |
2088 | 2088 | ||
2089 | if (txq == sc->tx.txq_map[qnum]) | ||
2090 | ath_wake_mac80211_queue(sc, qnum); | ||
2091 | |||
2092 | spin_lock_bh(&txq->axq_lock); | 2089 | spin_lock_bh(&txq->axq_lock); |
2090 | __ath_wake_mac80211_queue(sc, txq); | ||
2091 | |||
2093 | if (sc->sc_flags & SC_OP_TXAGGR) | 2092 | if (sc->sc_flags & SC_OP_TXAGGR) |
2094 | ath_txq_schedule(sc, txq); | 2093 | ath_txq_schedule(sc, txq); |
2095 | spin_unlock_bh(&txq->axq_lock); | 2094 | spin_unlock_bh(&txq->axq_lock); |
@@ -2103,6 +2102,9 @@ static void ath_tx_complete_poll_work(struct work_struct *work) | |||
2103 | struct ath_txq *txq; | 2102 | struct ath_txq *txq; |
2104 | int i; | 2103 | int i; |
2105 | bool needreset = false; | 2104 | bool needreset = false; |
2105 | #ifdef CONFIG_ATH9K_DEBUGFS | ||
2106 | sc->tx_complete_poll_work_seen++; | ||
2107 | #endif | ||
2106 | 2108 | ||
2107 | for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) | 2109 | for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) |
2108 | if (ATH_TXQ_SETUP(sc, i)) { | 2110 | if (ATH_TXQ_SETUP(sc, i)) { |
@@ -2116,6 +2118,34 @@ static void ath_tx_complete_poll_work(struct work_struct *work) | |||
2116 | } else { | 2118 | } else { |
2117 | txq->axq_tx_inprogress = true; | 2119 | txq->axq_tx_inprogress = true; |
2118 | } | 2120 | } |
2121 | } else { | ||
2122 | /* If the queue has pending buffers, then it | ||
2123 | * should be doing tx work (and have axq_depth). | ||
2124 | * Shouldn't get to this state I think..but | ||
2125 | * we do. | ||
2126 | */ | ||
2127 | if (!(sc->sc_flags & (SC_OP_OFFCHANNEL)) && | ||
2128 | (txq->pending_frames > 0 || | ||
2129 | !list_empty(&txq->axq_acq) || | ||
2130 | txq->stopped)) { | ||
2131 | ath_err(ath9k_hw_common(sc->sc_ah), | ||
2132 | "txq: %p axq_qnum: %u," | ||
2133 | " mac80211_qnum: %i" | ||
2134 | " axq_link: %p" | ||
2135 | " pending frames: %i" | ||
2136 | " axq_acq empty: %i" | ||
2137 | " stopped: %i" | ||
2138 | " axq_depth: 0 Attempting to" | ||
2139 | " restart tx logic.\n", | ||
2140 | txq, txq->axq_qnum, | ||
2141 | txq->mac80211_qnum, | ||
2142 | txq->axq_link, | ||
2143 | txq->pending_frames, | ||
2144 | list_empty(&txq->axq_acq), | ||
2145 | txq->stopped); | ||
2146 | __ath_wake_mac80211_queue(sc, txq); | ||
2147 | ath_txq_schedule(sc, txq); | ||
2148 | } | ||
2119 | } | 2149 | } |
2120 | spin_unlock_bh(&txq->axq_lock); | 2150 | spin_unlock_bh(&txq->axq_lock); |
2121 | } | 2151 | } |
@@ -2212,10 +2242,9 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) | |||
2212 | ath_tx_complete_buf(sc, bf, txq, &bf_head, | 2242 | ath_tx_complete_buf(sc, bf, txq, &bf_head, |
2213 | &txs, txok, 0); | 2243 | &txs, txok, 0); |
2214 | 2244 | ||
2215 | if (txq == sc->tx.txq_map[qnum]) | ||
2216 | ath_wake_mac80211_queue(sc, qnum); | ||
2217 | |||
2218 | spin_lock_bh(&txq->axq_lock); | 2245 | spin_lock_bh(&txq->axq_lock); |
2246 | __ath_wake_mac80211_queue(sc, txq); | ||
2247 | |||
2219 | if (!list_empty(&txq->txq_fifo_pending)) { | 2248 | if (!list_empty(&txq->txq_fifo_pending)) { |
2220 | INIT_LIST_HEAD(&bf_head); | 2249 | INIT_LIST_HEAD(&bf_head); |
2221 | bf = list_first_entry(&txq->txq_fifo_pending, | 2250 | bf = list_first_entry(&txq->txq_fifo_pending, |