|
@@ -161,6 +161,12 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
|
|
|
return -ENOENT;
|
|
|
}
|
|
|
|
|
|
+ /* if we're already stopping ignore any new requests to stop */
|
|
|
+ if (test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) {
|
|
|
+ spin_unlock_bh(&sta->lock);
|
|
|
+ return -EALREADY;
|
|
|
+ }
|
|
|
+
|
|
|
if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) {
|
|
|
/* not even started yet! */
|
|
|
ieee80211_assign_tid_tx(sta, tid, NULL);
|
|
@@ -169,6 +175,8 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+ set_bit(HT_AGG_STATE_STOPPING, &tid_tx->state);
|
|
|
+
|
|
|
spin_unlock_bh(&sta->lock);
|
|
|
|
|
|
#ifdef CONFIG_MAC80211_HT_DEBUG
|
|
@@ -176,8 +184,6 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
|
|
|
sta->sta.addr, tid);
|
|
|
#endif /* CONFIG_MAC80211_HT_DEBUG */
|
|
|
|
|
|
- set_bit(HT_AGG_STATE_STOPPING, &tid_tx->state);
|
|
|
-
|
|
|
del_timer_sync(&tid_tx->addba_resp_timer);
|
|
|
|
|
|
/*
|
|
@@ -187,6 +193,20 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
|
|
|
*/
|
|
|
clear_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state);
|
|
|
|
|
|
+ /*
|
|
|
+ * There might be a few packets being processed right now (on
|
|
|
+ * another CPU) that have already gotten past the aggregation
|
|
|
+ * check when it was still OPERATIONAL and consequently have
|
|
|
+ * IEEE80211_TX_CTL_AMPDU set. In that case, this code might
|
|
|
+ * call into the driver at the same time or even before the
|
|
|
+ * TX paths calls into it, which could confuse the driver.
|
|
|
+ *
|
|
|
+ * Wait for all currently running TX paths to finish before
|
|
|
+ * telling the driver. New packets will not go through since
|
|
|
+ * the aggregation session is no longer OPERATIONAL.
|
|
|
+ */
|
|
|
+ synchronize_net();
|
|
|
+
|
|
|
tid_tx->stop_initiator = initiator;
|
|
|
tid_tx->tx_stop = tx;
|
|
|
|
|
@@ -757,11 +777,27 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
- del_timer(&tid_tx->addba_resp_timer);
|
|
|
+ del_timer_sync(&tid_tx->addba_resp_timer);
|
|
|
|
|
|
#ifdef CONFIG_MAC80211_HT_DEBUG
|
|
|
printk(KERN_DEBUG "switched off addBA timer for tid %d\n", tid);
|
|
|
#endif
|
|
|
+
|
|
|
+ /*
|
|
|
+ * addba_resp_timer may have fired before we got here, and
|
|
|
+ * caused WANT_STOP to be set. If the stop then was already
|
|
|
+ * processed further, STOPPING might be set.
|
|
|
+ */
|
|
|
+ if (test_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state) ||
|
|
|
+ test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) {
|
|
|
+#ifdef CONFIG_MAC80211_HT_DEBUG
|
|
|
+ printk(KERN_DEBUG
|
|
|
+ "got addBA resp for tid %d but we already gave up\n",
|
|
|
+ tid);
|
|
|
+#endif
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
/*
|
|
|
* IEEE 802.11-2007 7.3.1.14:
|
|
|
* In an ADDBA Response frame, when the Status Code field
|