ath9k: fix a tx processing race condition on AR9300+
authorFelix Fietkau <nbd@openwrt.org>
Sun, 7 Apr 2013 22:05:45 +0000 (22:05 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Sun, 7 Apr 2013 22:05:45 +0000 (22:05 +0000)
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
SVN-Revision: 36267

package/mac80211/patches/300-pending_work.patch

index 7558c3d1a2ff35d3357b3a467ae50b8e9bd6433b..569bf7f06a8eff48c62e0faedbf07e959a900c38 100644 (file)
                dev_err(priv->dev, "ath9k_htc: Please upgrade to FW version %d.%d\n",
                        MAJOR_VERSION_REQ, MINOR_VERSION_REQ);
                return -EINVAL;
+--- 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 
+                * not a holding desc.
+                */
+               INIT_LIST_HEAD(&bf_head);
+-              if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ||
+-                  bf_next != NULL || !bf_last->bf_stale)
++              if (bf_next != NULL || !bf_last->bf_stale)
+                       list_move_tail(&bf->list, &bf_head);
+               if (!txpending || (tid->state & AGGR_CLEANUP)) {
+@@ -537,8 +536,7 @@ static void ath_tx_complete_aggr(struct 
+                               !txfail);
+               } else {
+                       /* retry the un-acked ones */
+-                      if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) &&
+-                          bf->bf_next == NULL && bf_last->bf_stale) {
++                      if (bf->bf_next == NULL && bf_last->bf_stale) {
+                               struct ath_buf *tbf;
+                               tbf = ath_clone_txbuf(sc, bf_last);
+@@ -2264,6 +2262,7 @@ void ath_tx_edma_tasklet(struct ath_soft
+       struct ath_txq *txq;
+       struct ath_buf *bf, *lastbf;
+       struct list_head bf_head;
++      struct list_head *fifo_list;
+       int status;
+       for (;;) {
+@@ -2291,20 +2290,24 @@ void ath_tx_edma_tasklet(struct ath_soft
+               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);
+                       return;
+               }
+-              bf = list_first_entry(&txq->txq_fifo[txq->txq_tailidx],
+-                                    struct ath_buf, list);
++              bf = list_first_entry(fifo_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;
+               INIT_LIST_HEAD(&bf_head);
+-              list_cut_position(&bf_head, &txq->txq_fifo[txq->txq_tailidx],
+-                                &lastbf->list);
+-
+-              if (list_empty(&txq->txq_fifo[txq->txq_tailidx])) {
++              if (list_is_last(&lastbf->list, fifo_list)) {
++                      list_splice_tail_init(fifo_list, &bf_head);
+                       INCR(txq->txq_tailidx, ATH_TXFIFO_DEPTH);
+                       if (!list_empty(&txq->axq_q)) {
+@@ -2315,6 +2318,11 @@ void ath_tx_edma_tasklet(struct ath_soft
+                               list_splice_tail_init(&txq->axq_q, &bf_q);
+                               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);