From 5ad8bcbba44ccb0ee3607388472ebecea3f09fe0 Mon Sep 17 00:00:00 2001
From: Felix Fietkau <nbd@openwrt.org>
Date: Sat, 10 Dec 2011 21:16:58 +0000
Subject: [PATCH] mac80211: merge an upstream fix for an aggregation related
 race condition

SVN-Revision: 29493
---
 .../mac80211/patches/300-pending_work.patch   | 132 +++++++++++++++++-
 1 file changed, 130 insertions(+), 2 deletions(-)

diff --git a/package/mac80211/patches/300-pending_work.patch b/package/mac80211/patches/300-pending_work.patch
index 34de3ac487..2119b42be6 100644
--- a/package/mac80211/patches/300-pending_work.patch
+++ b/package/mac80211/patches/300-pending_work.patch
@@ -532,7 +532,96 @@
  
  	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
  					  IEEE80211_STYPE_ACTION);
-@@ -437,7 +440,9 @@ int ieee80211_start_tx_ba_session(struct
+@@ -319,6 +322,38 @@ ieee80211_wake_queue_agg(struct ieee8021
+ 	__release(agg_queue);
+ }
+ 
++/*
++ * splice packets from the STA's pending to the local pending,
++ * requires a call to ieee80211_agg_splice_finish later
++ */
++static void __acquires(agg_queue)
++ieee80211_agg_splice_packets(struct ieee80211_local *local,
++			     struct tid_ampdu_tx *tid_tx, u16 tid)
++{
++	int queue = ieee80211_ac_from_tid(tid);
++	unsigned long flags;
++
++	ieee80211_stop_queue_agg(local, tid);
++
++	if (WARN(!tid_tx, "TID %d gone but expected when splicing aggregates"
++			  " from the pending queue\n", tid))
++		return;
++
++	if (!skb_queue_empty(&tid_tx->pending)) {
++		spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
++		/* copy over remaining packets */
++		skb_queue_splice_tail_init(&tid_tx->pending,
++					   &local->pending[queue]);
++		spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
++	}
++}
++
++static void __releases(agg_queue)
++ieee80211_agg_splice_finish(struct ieee80211_local *local, u16 tid)
++{
++	ieee80211_wake_queue_agg(local, tid);
++}
++
+ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
+ {
+ 	struct tid_ampdu_tx *tid_tx;
+@@ -330,19 +365,17 @@ void ieee80211_tx_ba_session_handle_star
+ 	tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
+ 
+ 	/*
+-	 * While we're asking the driver about the aggregation,
+-	 * stop the AC queue so that we don't have to worry
+-	 * about frames that came in while we were doing that,
+-	 * which would require us to put them to the AC pending
+-	 * afterwards which just makes the code more complex.
++	 * Start queuing up packets for this aggregation session.
++	 * We're going to release them once the driver is OK with
++	 * that.
+ 	 */
+-	ieee80211_stop_queue_agg(local, tid);
+-
+ 	clear_bit(HT_AGG_STATE_WANT_START, &tid_tx->state);
+ 
+ 	/*
+-	 * make sure no packets are being processed to get
+-	 * valid starting sequence number
++	 * Make sure no packets are being processed. This ensures that
++	 * we have a valid starting sequence number and that in-flight
++	 * packets have been flushed out and no packets for this TID
++	 * will go into the driver during the ampdu_action call.
+ 	 */
+ 	synchronize_net();
+ 
+@@ -356,10 +389,11 @@ void ieee80211_tx_ba_session_handle_star
+ 					" tid %d\n", tid);
+ #endif
+ 		spin_lock_bh(&sta->lock);
++		ieee80211_agg_splice_packets(local, tid_tx, tid);
+ 		ieee80211_assign_tid_tx(sta, tid, NULL);
++		ieee80211_agg_splice_finish(local, tid);
+ 		spin_unlock_bh(&sta->lock);
+ 
+-		ieee80211_wake_queue_agg(local, tid);
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,40))
+ 		kfree_rcu(tid_tx, rcu_head);
+ #else
+@@ -368,9 +402,6 @@ void ieee80211_tx_ba_session_handle_star
+ 		return;
+ 	}
+ 
+-	/* we can take packets again now */
+-	ieee80211_wake_queue_agg(local, tid);
+-
+ 	/* activate the timer for the recipient's addBA response */
+ 	mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL);
+ #ifdef CONFIG_MAC80211_HT_DEBUG
+@@ -437,7 +468,9 @@ int ieee80211_start_tx_ba_session(struct
  	if (sdata->vif.type != NL80211_IFTYPE_STATION &&
  	    sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
  	    sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
@@ -543,7 +632,7 @@
  		return -EINVAL;
  
  	if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
-@@ -448,6 +453,27 @@ int ieee80211_start_tx_ba_session(struct
+@@ -448,6 +481,27 @@ int ieee80211_start_tx_ba_session(struct
  		return -EINVAL;
  	}
  
@@ -571,6 +660,45 @@
  	spin_lock_bh(&sta->lock);
  
  	/* we have tried too many times, receiver does not want A-MPDU */
+@@ -508,38 +562,6 @@ int ieee80211_start_tx_ba_session(struct
+ }
+ EXPORT_SYMBOL(ieee80211_start_tx_ba_session);
+ 
+-/*
+- * splice packets from the STA's pending to the local pending,
+- * requires a call to ieee80211_agg_splice_finish later
+- */
+-static void __acquires(agg_queue)
+-ieee80211_agg_splice_packets(struct ieee80211_local *local,
+-			     struct tid_ampdu_tx *tid_tx, u16 tid)
+-{
+-	int queue = ieee80211_ac_from_tid(tid);
+-	unsigned long flags;
+-
+-	ieee80211_stop_queue_agg(local, tid);
+-
+-	if (WARN(!tid_tx, "TID %d gone but expected when splicing aggregates"
+-			  " from the pending queue\n", tid))
+-		return;
+-
+-	if (!skb_queue_empty(&tid_tx->pending)) {
+-		spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+-		/* copy over remaining packets */
+-		skb_queue_splice_tail_init(&tid_tx->pending,
+-					   &local->pending[queue]);
+-		spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+-	}
+-}
+-
+-static void __releases(agg_queue)
+-ieee80211_agg_splice_finish(struct ieee80211_local *local, u16 tid)
+-{
+-	ieee80211_wake_queue_agg(local, tid);
+-}
+-
+ static void ieee80211_agg_tx_operational(struct ieee80211_local *local,
+ 					 struct sta_info *sta, u16 tid)
+ {
 --- a/net/mac80211/debugfs_sta.c
 +++ b/net/mac80211/debugfs_sta.c
 @@ -63,11 +63,11 @@ static ssize_t sta_flags_read(struct fil
-- 
2.30.2