diff options
author | Felix Fietkau <nbd@openwrt.org> | 2013-04-07 18:04:13 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2013-04-10 14:10:34 -0400 |
commit | 99ba6a4610c8413c9166e600b1797f0a8f1c4498 (patch) | |
tree | f2558b71b322a0102751faf6754bc6da82209ce6 | |
parent | 3747c3eef6df9bf936fc571ab64122e651db6137 (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.c | 30 |
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); |