Commit 99ba6a46 authored by Felix Fietkau's avatar Felix Fietkau Committed by John W. Linville

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: default avatarFelix Fietkau <nbd@openwrt.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 3747c3ee
...@@ -516,8 +516,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, ...@@ -516,8 +516,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
* not a holding desc. * not a holding desc.
*/ */
INIT_LIST_HEAD(&bf_head); INIT_LIST_HEAD(&bf_head);
if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) || if (bf_next != NULL || !bf_last->bf_stale)
bf_next != NULL || !bf_last->bf_stale)
list_move_tail(&bf->list, &bf_head); list_move_tail(&bf->list, &bf_head);
if (!txpending || (tid->state & AGGR_CLEANUP)) { 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,8 +536,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
!txfail); !txfail);
} else { } else {
/* retry the un-acked ones */ /* retry the un-acked ones */
if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) && if (bf->bf_next == NULL && bf_last->bf_stale) {
bf->bf_next == NULL && bf_last->bf_stale) {
struct ath_buf *tbf; struct ath_buf *tbf;
tbf = ath_clone_txbuf(sc, bf_last); tbf = ath_clone_txbuf(sc, bf_last);
...@@ -2264,6 +2262,7 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) ...@@ -2264,6 +2262,7 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
struct ath_txq *txq; struct ath_txq *txq;
struct ath_buf *bf, *lastbf; struct ath_buf *bf, *lastbf;
struct list_head bf_head; struct list_head bf_head;
struct list_head *fifo_list;
int status; int status;
for (;;) { for (;;) {
...@@ -2291,20 +2290,24 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) ...@@ -2291,20 +2290,24 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
TX_STAT_INC(txq->axq_qnum, txprocdesc); TX_STAT_INC(txq->axq_qnum, txprocdesc);
if (list_empty(&txq->txq_fifo[txq->txq_tailidx])) { fifo_list = &txq->txq_fifo[txq->txq_tailidx];
if (list_empty(fifo_list)) {
ath_txq_unlock(sc, txq); ath_txq_unlock(sc, txq);
return; return;
} }
bf = list_first_entry(&txq->txq_fifo[txq->txq_tailidx], bf = list_first_entry(fifo_list, struct ath_buf, list);
struct ath_buf, list); if (bf->bf_stale) {
list_del(&bf->list);
ath_tx_return_buffer(sc, bf);
bf = list_first_entry(fifo_list, struct ath_buf, list);
}
lastbf = bf->bf_lastbf; lastbf = bf->bf_lastbf;
INIT_LIST_HEAD(&bf_head); INIT_LIST_HEAD(&bf_head);
list_cut_position(&bf_head, &txq->txq_fifo[txq->txq_tailidx], if (list_is_last(&lastbf->list, fifo_list)) {
&lastbf->list); list_splice_tail_init(fifo_list, &bf_head);
if (list_empty(&txq->txq_fifo[txq->txq_tailidx])) {
INCR(txq->txq_tailidx, ATH_TXFIFO_DEPTH); INCR(txq->txq_tailidx, ATH_TXFIFO_DEPTH);
if (!list_empty(&txq->axq_q)) { if (!list_empty(&txq->axq_q)) {
...@@ -2315,6 +2318,11 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) ...@@ -2315,6 +2318,11 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
list_splice_tail_init(&txq->axq_q, &bf_q); list_splice_tail_init(&txq->axq_q, &bf_q);
ath_tx_txqaddbuf(sc, txq, &bf_q, true); ath_tx_txqaddbuf(sc, txq, &bf_q, true);
} }
} else {
lastbf->bf_stale = true;
if (bf != lastbf)
list_cut_position(&bf_head, fifo_list,
lastbf->list.prev);
} }
ath_tx_process_buffer(sc, txq, &ts, bf, &bf_head); ath_tx_process_buffer(sc, txq, &ts, bf, &bf_head);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment