ath9k: fix a tx processing race condition on AR9300+
Signed-off-by: Felix Fietkau <nbd@openwrt.org> SVN-Revision: 36267
This commit is contained in:
		@@ -1036,3 +1036,77 @@
 | 
			
		||||
 		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);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user