aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@openwrt.org>2013-04-07 18:04:13 -0400
committerJohn W. Linville <linville@tuxdriver.com>2013-04-10 14:10:34 -0400
commit99ba6a4610c8413c9166e600b1797f0a8f1c4498 (patch)
treef2558b71b322a0102751faf6754bc6da82209ce6
parent3747c3eef6df9bf936fc571ab64122e651db6137 (diff)
ath9k: implement buffer holding handling for EDMA FIFO
Inside one FIFO slot queue, EDMA chipsets have the same link pointer re-read race condition as older chipsets, so the same buffer holding logic needs to be used in order to avoid use-after-free bugs. Unlike on older chips, it can be skipped for the end of the queue. Signed-off-by: Felix Fietkau <nbd@openwrt.org> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c30
1 files changed, 19 insertions, 11 deletions
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 94b2ee1220db..5bc580276493 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -516,8 +516,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
516 * not a holding desc. 516 * not a holding desc.
517 */ 517 */
518 INIT_LIST_HEAD(&bf_head); 518 INIT_LIST_HEAD(&bf_head);
519 if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) || 519 if (bf_next != NULL || !bf_last->bf_stale)
520 bf_next != NULL || !bf_last->bf_stale)
521 list_move_tail(&bf->list, &bf_head); 520 list_move_tail(&bf->list, &bf_head);
522 521
523 if (!txpending || (tid->state & AGGR_CLEANUP)) { 522 if (!txpending || (tid->state & AGGR_CLEANUP)) {
@@ -537,8 +536,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
537 !txfail); 536 !txfail);
538 } else { 537 } else {
539 /* retry the un-acked ones */ 538 /* retry the un-acked ones */
540 if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) && 539 if (bf->bf_next == NULL && bf_last->bf_stale) {
541 bf->bf_next == NULL && bf_last->bf_stale) {
542 struct ath_buf *tbf; 540 struct ath_buf *tbf;
543 541
544 tbf = ath_clone_txbuf(sc, bf_last); 542 tbf = ath_clone_txbuf(sc, bf_last);
@@ -2264,6 +2262,7 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
2264 struct ath_txq *txq; 2262 struct ath_txq *txq;
2265 struct ath_buf *bf, *lastbf; 2263 struct ath_buf *bf, *lastbf;
2266 struct list_head bf_head; 2264 struct list_head bf_head;
2265 struct list_head *fifo_list;
2267 int status; 2266 int status;
2268 2267
2269 for (;;) { 2268 for (;;) {
@@ -2291,20 +2290,24 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
2291 2290
2292 TX_STAT_INC(txq->axq_qnum, txprocdesc); 2291 TX_STAT_INC(txq->axq_qnum, txprocdesc);
2293 2292
2294 if (list_empty(&txq->txq_fifo[txq->txq_tailidx])) { 2293 fifo_list = &txq->txq_fifo[txq->txq_tailidx];
2294 if (list_empty(fifo_list)) {
2295 ath_txq_unlock(sc, txq); 2295 ath_txq_unlock(sc, txq);
2296 return; 2296 return;
2297 } 2297 }
2298 2298
2299 bf = list_first_entry(&txq->txq_fifo[txq->txq_tailidx], 2299 bf = list_first_entry(fifo_list, struct ath_buf, list);
2300 struct ath_buf, list); 2300 if (bf->bf_stale) {
2301 list_del(&bf->list);
2302 ath_tx_return_buffer(sc, bf);
2303 bf = list_first_entry(fifo_list, struct ath_buf, list);
2304 }
2305
2301 lastbf = bf->bf_lastbf; 2306 lastbf = bf->bf_lastbf;
2302 2307
2303 INIT_LIST_HEAD(&bf_head); 2308 INIT_LIST_HEAD(&bf_head);
2304 list_cut_position(&bf_head, &txq->txq_fifo[txq->txq_tailidx], 2309 if (list_is_last(&lastbf->list, fifo_list)) {
2305 &lastbf->list); 2310 list_splice_tail_init(fifo_list, &bf_head);
2306
2307 if (list_empty(&txq->txq_fifo[txq->txq_tailidx])) {
2308 INCR(txq->txq_tailidx, ATH_TXFIFO_DEPTH); 2311 INCR(txq->txq_tailidx, ATH_TXFIFO_DEPTH);
2309 2312
2310 if (!list_empty(&txq->axq_q)) { 2313 if (!list_empty(&txq->axq_q)) {
@@ -2315,6 +2318,11 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
2315 list_splice_tail_init(&txq->axq_q, &bf_q); 2318 list_splice_tail_init(&txq->axq_q, &bf_q);
2316 ath_tx_txqaddbuf(sc, txq, &bf_q, true); 2319 ath_tx_txqaddbuf(sc, txq, &bf_q, true);
2317 } 2320 }
2321 } else {
2322 lastbf->bf_stale = true;
2323 if (bf != lastbf)
2324 list_cut_position(&bf_head, fifo_list,
2325 lastbf->list.prev);
2318 } 2326 }
2319 2327
2320 ath_tx_process_buffer(sc, txq, &ts, bf, &bf_head); 2328 ath_tx_process_buffer(sc, txq, &ts, bf, &bf_head);