From 2ff74a75b62f6b9481666fc9ef1c94e582c9972a Mon Sep 17 00:00:00 2001
From: Felix Fietkau <nbd@openwrt.org>
Date: Sat, 18 May 2013 19:36:22 +0000
Subject: ath9k: fix some more aggregation related issues

Signed-off-by: Felix Fietkau <nbd@openwrt.org>

SVN-Revision: 36656
---
 package/mac80211/patches/300-pending_work.patch | 337 +++++++++++++++---------
 1 file changed, 212 insertions(+), 125 deletions(-)

(limited to 'package/mac80211/patches/300-pending_work.patch')

diff --git a/package/mac80211/patches/300-pending_work.patch b/package/mac80211/patches/300-pending_work.patch
index e1f6af536b..af9270d0f2 100644
--- a/package/mac80211/patches/300-pending_work.patch
+++ b/package/mac80211/patches/300-pending_work.patch
@@ -8,25 +8,31 @@
  	struct ath_buf_state bf_state;
  };
  
-@@ -253,6 +254,7 @@ struct ath_atx_tid {
- 	int sched;
- 	int paused;
- 	u8 state;
-+	bool stop_cb;
+@@ -250,9 +251,9 @@ struct ath_atx_tid {
+ 	int tidno;
+ 	int baw_head;   /* first un-acked tx buffer */
+ 	int baw_tail;   /* next unused tx buffer slot */
+-	int sched;
+-	int paused;
+-	u8 state;
++	bool sched;
++	bool paused;
++	bool active;
  };
  
  struct ath_node {
-@@ -350,7 +352,8 @@ void ath_tx_tasklet(struct ath_softc *sc
- void ath_tx_edma_tasklet(struct ath_softc *sc);
- int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
- 		      u16 tid, u16 *ssn);
--void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
-+bool ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid,
-+		      bool flush);
- void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
- 
- void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an);
-@@ -658,11 +661,10 @@ enum sc_op_flags {
+@@ -273,10 +274,6 @@ struct ath_node {
+ #endif
+ };
+ 
+-#define AGGR_CLEANUP         BIT(1)
+-#define AGGR_ADDBA_COMPLETE  BIT(2)
+-#define AGGR_ADDBA_PROGRESS  BIT(3)
+-
+ struct ath_tx_control {
+ 	struct ath_txq *txq;
+ 	struct ath_node *an;
+@@ -658,11 +655,10 @@ enum sc_op_flags {
  struct ath_rate_table;
  
  struct ath9k_vif_iter_data {
@@ -161,7 +167,7 @@
  			      u16 tid, u16 *ssn, u8 buf_size)
  {
  	struct ath_softc *sc = hw->priv;
-+	bool flush = false, cb;
++	bool flush = false;
  	int ret = 0;
  
  	local_bh_disable();
@@ -175,10 +181,9 @@
 +		flush = true;
 +	case IEEE80211_AMPDU_TX_STOP_CONT:
  		ath9k_ps_wakeup(sc);
--		ath_tx_aggr_stop(sc, sta, tid);
+ 		ath_tx_aggr_stop(sc, sta, tid);
 -		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
-+		cb = ath_tx_aggr_stop(sc, sta, tid, flush);
-+		if (cb)
++		if (!flush)
 +			ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
  		ath9k_ps_restore(sc);
  		break;
@@ -3697,11 +3702,35 @@
  		 hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
 --- a/drivers/net/wireless/ath/ath9k/xmit.c
 +++ b/drivers/net/wireless/ath/ath9k/xmit.c
-@@ -157,7 +157,27 @@ static void ath_send_bar(struct ath_atx_
+@@ -125,24 +125,6 @@ static void ath_tx_queue_tid(struct ath_
+ 	list_add_tail(&ac->list, &txq->axq_acq);
+ }
+ 
+-static void ath_tx_resume_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
+-{
+-	struct ath_txq *txq = tid->ac->txq;
+-
+-	WARN_ON(!tid->paused);
+-
+-	ath_txq_lock(sc, txq);
+-	tid->paused = false;
+-
+-	if (skb_queue_empty(&tid->buf_q))
+-		goto unlock;
+-
+-	ath_tx_queue_tid(txq, tid);
+-	ath_txq_schedule(sc, txq);
+-unlock:
+-	ath_txq_unlock_complete(sc, txq);
+-}
+-
+ static struct ath_frame_info *get_frame_info(struct sk_buff *skb)
+ {
+ 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+@@ -157,6 +139,13 @@ static void ath_send_bar(struct ath_atx_
  			   seqno << IEEE80211_SEQ_SEQ_SHIFT);
  }
  
--static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
 +static void ath_set_rates(struct ieee80211_vif *vif, struct ieee80211_sta *sta,
 +			  struct ath_buf *bf)
 +{
@@ -3709,44 +3738,10 @@
 +			       ARRAY_SIZE(bf->rates));
 +}
 +
-+static void ath_tx_clear_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
-+{
-+	tid->state &= ~AGGR_ADDBA_COMPLETE;
-+	tid->state &= ~AGGR_CLEANUP;
-+	if (!tid->stop_cb)
-+		return;
-+
-+	ieee80211_start_tx_ba_cb_irqsafe(tid->an->vif, tid->an->sta->addr,
-+					 tid->tidno);
-+	tid->stop_cb = false;
-+}
-+
-+static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid,
-+			     bool flush_packets)
+ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
  {
  	struct ath_txq *txq = tid->ac->txq;
- 	struct sk_buff *skb;
-@@ -174,31 +194,29 @@ static void ath_tx_flush_tid(struct ath_
- 	while ((skb = __skb_dequeue(&tid->buf_q))) {
- 		fi = get_frame_info(skb);
- 		bf = fi->bf;
-+		if (!bf && !flush_packets)
-+			bf = ath_tx_setup_buffer(sc, txq, tid, skb);
- 
- 		if (!bf) {
--			bf = ath_tx_setup_buffer(sc, txq, tid, skb);
--			if (!bf) {
--				ieee80211_free_txskb(sc->hw, skb);
--				continue;
--			}
-+			ieee80211_free_txskb(sc->hw, skb);
-+			continue;
- 		}
- 
--		if (fi->retries) {
-+		if (fi->retries || flush_packets) {
- 			list_add_tail(&bf->list, &bf_head);
- 			ath_tx_update_baw(sc, tid, bf->bf_state.seqno);
+@@ -189,15 +178,11 @@ static void ath_tx_flush_tid(struct ath_
  			ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0);
  			sendbar = true;
  		} else {
@@ -3759,15 +3754,11 @@
 -		tid->state &= ~AGGR_ADDBA_COMPLETE;
 -		tid->state &= ~AGGR_CLEANUP;
 -	}
-+	if (tid->baw_head == tid->baw_tail)
-+		ath_tx_clear_tid(sc, tid);
- 
--	if (sendbar) {
-+	if (sendbar && !flush_packets) {
+-
+ 	if (sendbar) {
  		ath_txq_unlock(sc, txq);
  		ath_send_bar(tid, tid->seq_start);
- 		ath_txq_lock(sc, txq);
-@@ -269,9 +287,7 @@ static void ath_tid_drain(struct ath_sof
+@@ -269,9 +254,7 @@ static void ath_tid_drain(struct ath_sof
  
  		list_add_tail(&bf->list, &bf_head);
  
@@ -3778,7 +3769,7 @@
  		ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0);
  	}
  
-@@ -407,7 +423,7 @@ static void ath_tx_complete_aggr(struct 
+@@ -407,7 +390,7 @@ static void ath_tx_complete_aggr(struct 
  
  	tx_info = IEEE80211_SKB_CB(skb);
  
@@ -3787,16 +3778,53 @@
  
  	retries = ts->ts_longretry + 1;
  	for (i = 0; i < ts->ts_rateindex; i++)
-@@ -594,7 +610,7 @@ static void ath_tx_complete_aggr(struct 
+@@ -483,19 +466,19 @@ static void ath_tx_complete_aggr(struct 
+ 		tx_info = IEEE80211_SKB_CB(skb);
+ 		fi = get_frame_info(skb);
+ 
+-		if (ATH_BA_ISSET(ba, ATH_BA_INDEX(seq_st, seqno))) {
++		if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) {
++			/*
++			 * Outside of the current BlockAck window,
++			 * maybe part of a previous session
++			 */
++			txfail = 1;
++		} else if (ATH_BA_ISSET(ba, ATH_BA_INDEX(seq_st, seqno))) {
+ 			/* transmit completion, subframe is
+ 			 * acked by block ack */
+ 			acked_cnt++;
+ 		} else if (!isaggr && txok) {
+ 			/* transmit completion */
+ 			acked_cnt++;
+-		} else if (tid->state & AGGR_CLEANUP) {
+-			/*
+-			 * cleanup in progress, just fail
+-			 * the un-acked sub-frames
+-			 */
+-			txfail = 1;
+ 		} else if (flush) {
+ 			txpending = 1;
+ 		} else if (fi->retries < ATH_MAX_SW_RETRIES) {
+@@ -519,7 +502,7 @@ static void ath_tx_complete_aggr(struct 
+ 		if (bf_next != NULL || !bf_last->bf_stale)
+ 			list_move_tail(&bf->list, &bf_head);
+ 
+-		if (!txpending || (tid->state & AGGR_CLEANUP)) {
++		if (!txpending) {
+ 			/*
+ 			 * complete the acked-ones/xretried ones; update
+ 			 * block-ack window
+@@ -593,9 +576,6 @@ static void ath_tx_complete_aggr(struct 
+ 		ath_txq_lock(sc, txq);
  	}
  
- 	if (tid->state & AGGR_CLEANUP)
+-	if (tid->state & AGGR_CLEANUP)
 -		ath_tx_flush_tid(sc, tid);
-+		ath_tx_flush_tid(sc, tid, false);
- 
+-
  	rcu_read_unlock();
  
-@@ -612,6 +628,7 @@ static void ath_tx_process_buffer(struct
+ 	if (needreset)
+@@ -612,6 +592,7 @@ static void ath_tx_process_buffer(struct
  				  struct ath_tx_status *ts, struct ath_buf *bf,
  				  struct list_head *bf_head)
  {
@@ -3804,7 +3832,7 @@
  	bool txok, flush;
  
  	txok = !(ts->ts_status & ATH9K_TXERR_MASK);
-@@ -623,8 +640,12 @@ static void ath_tx_process_buffer(struct
+@@ -623,8 +604,12 @@ static void ath_tx_process_buffer(struct
  		txq->axq_ampdu_depth--;
  
  	if (!bf_isampdu(bf)) {
@@ -3818,7 +3846,7 @@
  		ath_tx_complete_buf(sc, bf, txq, bf_head, ts, txok);
  	} else
  		ath_tx_complete_aggr(sc, txq, bf, bf_head, ts, txok);
-@@ -668,7 +689,7 @@ static u32 ath_lookup_rate(struct ath_so
+@@ -668,7 +653,7 @@ static u32 ath_lookup_rate(struct ath_so
  
  	skb = bf->bf_mpdu;
  	tx_info = IEEE80211_SKB_CB(skb);
@@ -3827,7 +3855,7 @@
  
  	/*
  	 * Find the lowest frame length among the rate series that will have a
-@@ -736,8 +757,6 @@ static int ath_compute_num_delims(struct
+@@ -736,8 +721,6 @@ static int ath_compute_num_delims(struct
  				  bool first_subfrm)
  {
  #define FIRST_DESC_NDELIMS 60
@@ -3836,7 +3864,7 @@
  	u32 nsymbits, nsymbols;
  	u16 minlen;
  	u8 flags, rix;
-@@ -778,8 +797,8 @@ static int ath_compute_num_delims(struct
+@@ -778,8 +761,8 @@ static int ath_compute_num_delims(struct
  	if (tid->an->mpdudensity == 0)
  		return ndelim;
  
@@ -3847,7 +3875,7 @@
  	width = (flags & IEEE80211_TX_RC_40_MHZ_WIDTH) ? 1 : 0;
  	half_gi = (flags & IEEE80211_TX_RC_SHORT_GI) ? 1 : 0;
  
-@@ -858,6 +877,7 @@ static enum ATH_AGGR_STATUS ath_tx_form_
+@@ -858,6 +841,7 @@ static enum ATH_AGGR_STATUS ath_tx_form_
  			bf_first = bf;
  
  		if (!rl) {
@@ -3855,7 +3883,7 @@
  			aggr_limit = ath_lookup_rate(sc, bf, tid);
  			rl = 1;
  		}
-@@ -998,14 +1018,14 @@ static void ath_buf_set_rate(struct ath_
+@@ -998,14 +982,14 @@ static void ath_buf_set_rate(struct ath_
  
  	skb = bf->bf_mpdu;
  	tx_info = IEEE80211_SKB_CB(skb);
@@ -3872,55 +3900,92 @@
  		bool is_40, is_sgi, is_sp;
  		int phy;
  
-@@ -1249,18 +1269,23 @@ int ath_tx_aggr_start(struct ath_softc *
- 	return 0;
- }
+@@ -1224,9 +1208,6 @@ int ath_tx_aggr_start(struct ath_softc *
+ 	an = (struct ath_node *)sta->drv_priv;
+ 	txtid = ATH_AN_2_TID(an, tid);
  
--void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
-+bool ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid,
-+		      bool flush)
- {
- 	struct ath_node *an = (struct ath_node *)sta->drv_priv;
+-	if (txtid->state & (AGGR_CLEANUP | AGGR_ADDBA_COMPLETE))
+-		return -EAGAIN;
+-
+ 	/* update ampdu factor/density, they may have changed. This may happen
+ 	 * in HT IBSS when a beacon with HT-info is received after the station
+ 	 * has already been added.
+@@ -1238,7 +1219,7 @@ int ath_tx_aggr_start(struct ath_softc *
+ 		an->mpdudensity = density;
+ 	}
+ 
+-	txtid->state |= AGGR_ADDBA_PROGRESS;
++	txtid->active = true;
+ 	txtid->paused = true;
+ 	*ssn = txtid->seq_start = txtid->seq_next;
+ 	txtid->bar_index = -1;
+@@ -1255,28 +1236,9 @@ void ath_tx_aggr_stop(struct ath_softc *
  	struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid);
  	struct ath_txq *txq = txtid->ac->txq;
-+	bool ret = !flush;
-+
-+	if (flush)
-+		txtid->stop_cb = false;
  
- 	if (txtid->state & AGGR_CLEANUP)
+-	if (txtid->state & AGGR_CLEANUP)
 -		return;
-+		return false;
- 
- 	if (!(txtid->state & AGGR_ADDBA_COMPLETE)) {
- 		txtid->state &= ~AGGR_ADDBA_PROGRESS;
+-
+-	if (!(txtid->state & AGGR_ADDBA_COMPLETE)) {
+-		txtid->state &= ~AGGR_ADDBA_PROGRESS;
 -		return;
-+		return ret;
- 	}
- 
+-	}
+-
  	ath_txq_lock(sc, txq);
-@@ -1272,13 +1297,17 @@ void ath_tx_aggr_stop(struct ath_softc *
- 	 * TID can only be reused after all in-progress subframes have been
- 	 * completed.
- 	 */
++	txtid->active = false;
+ 	txtid->paused = true;
+-
+-	/*
+-	 * If frames are still being transmitted for this TID, they will be
+-	 * cleaned up during tx completion. To prevent race conditions, this
+-	 * TID can only be reused after all in-progress subframes have been
+-	 * completed.
+-	 */
 -	if (txtid->baw_head != txtid->baw_tail)
-+	if (txtid->baw_head != txtid->baw_tail) {
- 		txtid->state |= AGGR_CLEANUP;
+-		txtid->state |= AGGR_CLEANUP;
 -	else
-+		ret = false;
-+		txtid->stop_cb = !flush;
-+	} else {
- 		txtid->state &= ~AGGR_ADDBA_COMPLETE;
-+	}
- 
--	ath_tx_flush_tid(sc, txtid);
-+	ath_tx_flush_tid(sc, txtid, flush);
+-		txtid->state &= ~AGGR_ADDBA_COMPLETE;
+-
+ 	ath_tx_flush_tid(sc, txtid);
  	ath_txq_unlock_complete(sc, txq);
-+	return ret;
+ }
+@@ -1342,18 +1304,28 @@ void ath_tx_aggr_wakeup(struct ath_softc
+ 	}
+ }
+ 
+-void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
++void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta,
++			u16 tidno)
+ {
+-	struct ath_atx_tid *txtid;
++	struct ath_atx_tid *tid;
+ 	struct ath_node *an;
++	struct ath_txq *txq;
+ 
+ 	an = (struct ath_node *)sta->drv_priv;
++	tid = ATH_AN_2_TID(an, tidno);
++	txq = tid->ac->txq;
+ 
+-	txtid = ATH_AN_2_TID(an, tid);
+-	txtid->baw_size = IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor;
+-	txtid->state |= AGGR_ADDBA_COMPLETE;
+-	txtid->state &= ~AGGR_ADDBA_PROGRESS;
+-	ath_tx_resume_tid(sc, txtid);
++	ath_txq_lock(sc, txq);
++
++	tid->baw_size = IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor;
++	tid->paused = false;
++
++	if (!skb_queue_empty(&tid->buf_q)) {
++		ath_tx_queue_tid(txq, tid);
++		ath_txq_schedule(sc, txq);
++	}
++
++	ath_txq_unlock_complete(sc, txq);
  }
  
- void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc,
-@@ -1743,6 +1772,7 @@ static void ath_tx_send_ampdu(struct ath
+ /********************/
+@@ -1743,6 +1715,7 @@ static void ath_tx_send_ampdu(struct ath
  		return;
  	}
  
@@ -3928,7 +3993,7 @@
  	bf->bf_state.bf_type = BUF_AMPDU;
  	INIT_LIST_HEAD(&bf_head);
  	list_add(&bf->list, &bf_head);
-@@ -1892,49 +1922,6 @@ static struct ath_buf *ath_tx_setup_buff
+@@ -1892,49 +1865,6 @@ static struct ath_buf *ath_tx_setup_buff
  	return bf;
  }
  
@@ -3978,7 +4043,7 @@
  /* Upon failure caller should free skb */
  int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
  		 struct ath_tx_control *txctl)
-@@ -1945,8 +1932,11 @@ int ath_tx_start(struct ieee80211_hw *hw
+@@ -1945,8 +1875,11 @@ int ath_tx_start(struct ieee80211_hw *hw
  	struct ieee80211_vif *vif = info->control.vif;
  	struct ath_softc *sc = hw->priv;
  	struct ath_txq *txq = txctl->txq;
@@ -3990,7 +4055,7 @@
  	int q;
  
  	/* NOTE:  sta can be NULL according to net/mac80211.h */
-@@ -2002,8 +1992,41 @@ int ath_tx_start(struct ieee80211_hw *hw
+@@ -2002,8 +1935,41 @@ int ath_tx_start(struct ieee80211_hw *hw
  		txq->stopped = true;
  	}
  
@@ -3999,7 +4064,7 @@
 +		tidno = ieee80211_get_qos_ctl(hdr)[0] &
 +			IEEE80211_QOS_CTL_TID_MASK;
 +		tid = ATH_AN_2_TID(txctl->an, tidno);
-+
+ 
 +		WARN_ON(tid->ac->txq != txctl->txq);
 +	}
 +
@@ -4011,7 +4076,7 @@
 +		ath_tx_send_ampdu(sc, tid, skb, txctl);
 +		goto out;
 +	}
- 
++
 +	bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb);
 +	if (!bf) {
 +		if (txctl->paprd)
@@ -4033,24 +4098,32 @@
  	ath_txq_unlock(sc, txq);
  
  	return 0;
-@@ -2414,6 +2437,7 @@ void ath_tx_node_init(struct ath_softc *
+@@ -2408,12 +2374,10 @@ void ath_tx_node_init(struct ath_softc *
+ 		tid->baw_head  = tid->baw_tail = 0;
+ 		tid->sched     = false;
+ 		tid->paused    = false;
+-		tid->state &= ~AGGR_CLEANUP;
++		tid->active	   = false;
+ 		__skb_queue_head_init(&tid->buf_q);
+ 		acno = TID_TO_WME_AC(tidno);
  		tid->ac = &an->ac[acno];
- 		tid->state &= ~AGGR_ADDBA_COMPLETE;
- 		tid->state &= ~AGGR_ADDBA_PROGRESS;
-+		tid->stop_cb = false;
+-		tid->state &= ~AGGR_ADDBA_COMPLETE;
+-		tid->state &= ~AGGR_ADDBA_PROGRESS;
  	}
  
  	for (acno = 0, ac = &an->ac[acno];
-@@ -2450,8 +2474,7 @@ void ath_tx_node_cleanup(struct ath_soft
+@@ -2450,9 +2414,9 @@ void ath_tx_node_cleanup(struct ath_soft
  		}
  
  		ath_tid_drain(sc, txq, tid);
 -		tid->state &= ~AGGR_ADDBA_COMPLETE;
 -		tid->state &= ~AGGR_CLEANUP;
-+		ath_tx_clear_tid(sc, tid);
++		tid->active = false;
  
  		ath_txq_unlock(sc, txq);
  	}
+ }
++
 --- a/drivers/net/wireless/ath/ath9k/recv.c
 +++ b/drivers/net/wireless/ath/ath9k/recv.c
 @@ -124,7 +124,7 @@ static bool ath_rx_edma_buf_link(struct 
@@ -4117,3 +4190,17 @@
  	if (wdev->sme_state == CFG80211_SME_CONNECTED)
  		__cfg80211_disconnected(dev, NULL, 0, 0, false);
  	else if (wdev->sme_state == CFG80211_SME_CONNECTING)
+--- a/drivers/net/wireless/ath/ath9k/rc.c
++++ b/drivers/net/wireless/ath/ath9k/rc.c
+@@ -1227,10 +1227,7 @@ static bool ath_tx_aggr_check(struct ath
+ 		return false;
+ 
+ 	txtid = ATH_AN_2_TID(an, tidno);
+-
+-	if (!(txtid->state & (AGGR_ADDBA_COMPLETE | AGGR_ADDBA_PROGRESS)))
+-			return true;
+-	return false;
++	return !txtid->active;
+ }
+ 
+ 
-- 
cgit v1.2.3