aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/ht.c
diff options
context:
space:
mode:
authorAlexander Wetzel <alexander.wetzel@web.de>2018-05-14 16:33:34 -0400
committerJohannes Berg <johannes.berg@intel.com>2018-05-18 05:14:36 -0400
commit39c1134c66b4552f665da576cb625f184a44a8a3 (patch)
treeb725235aefb3cb2c970d2dc1e443647100b9a819 /net/mac80211/ht.c
parent4a22b00b288649f6d697b350ec606574479a2df3 (diff)
mac80211: fix TX aggregation stop race
The mac80211 tear down code is not waiting for the driver call back. This can bring down the the TX path (TID) till the user manually reconnects. (Observed with iwldvm and enabled TX aggregation.) The race can be prevented when the ampdu_mlme worker handles the tear down. The race: * ieee80211_sta_tear_down_BA_sessions calls ___ieee80211_stop_tx_ba_session for all TIDs, * then cancels the ampdu_mlme worker * and cleanups the TIDs the driver already has called back for. * ieee80211_stop_tx_ba_cb will never be called for a TID if the callback came after the the check in ieee80211_sta_tear_down_BA_sessions. Signed-off-by: Alexander Wetzel <Alexander.Wetzel@web.de> [johannes: "enabled" -> "blocked" and invert logic, simplify init] Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/ht.c')
-rw-r--r--net/mac80211/ht.c44
1 files changed, 21 insertions, 23 deletions
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index c78036a0ac94..26a7ba3b698f 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -301,26 +301,27 @@ void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
301 ___ieee80211_stop_tx_ba_session(sta, i, reason); 301 ___ieee80211_stop_tx_ba_session(sta, i, reason);
302 mutex_unlock(&sta->ampdu_mlme.mtx); 302 mutex_unlock(&sta->ampdu_mlme.mtx);
303 303
304 /* stopping might queue the work again - so cancel only afterwards */
305 cancel_work_sync(&sta->ampdu_mlme.work);
306
307 /* 304 /*
308 * In case the tear down is part of a reconfigure due to HW restart 305 * In case the tear down is part of a reconfigure due to HW restart
309 * request, it is possible that the low level driver requested to stop 306 * request, it is possible that the low level driver requested to stop
310 * the BA session, so handle it to properly clean tid_tx data. 307 * the BA session, so handle it to properly clean tid_tx data.
311 */ 308 */
312 mutex_lock(&sta->ampdu_mlme.mtx); 309 if(reason == AGG_STOP_DESTROY_STA) {
313 for (i = 0; i < IEEE80211_NUM_TIDS; i++) { 310 cancel_work_sync(&sta->ampdu_mlme.work);
314 struct tid_ampdu_tx *tid_tx =
315 rcu_dereference_protected_tid_tx(sta, i);
316 311
317 if (!tid_tx) 312 mutex_lock(&sta->ampdu_mlme.mtx);
318 continue; 313 for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
314 struct tid_ampdu_tx *tid_tx =
315 rcu_dereference_protected_tid_tx(sta, i);
319 316
320 if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, &tid_tx->state)) 317 if (!tid_tx)
321 ieee80211_stop_tx_ba_cb(sta, i, tid_tx); 318 continue;
319
320 if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, &tid_tx->state))
321 ieee80211_stop_tx_ba_cb(sta, i, tid_tx);
322 }
323 mutex_unlock(&sta->ampdu_mlme.mtx);
322 } 324 }
323 mutex_unlock(&sta->ampdu_mlme.mtx);
324} 325}
325 326
326void ieee80211_ba_session_work(struct work_struct *work) 327void ieee80211_ba_session_work(struct work_struct *work)
@@ -328,16 +329,11 @@ void ieee80211_ba_session_work(struct work_struct *work)
328 struct sta_info *sta = 329 struct sta_info *sta =
329 container_of(work, struct sta_info, ampdu_mlme.work); 330 container_of(work, struct sta_info, ampdu_mlme.work);
330 struct tid_ampdu_tx *tid_tx; 331 struct tid_ampdu_tx *tid_tx;
332 bool blocked;
331 int tid; 333 int tid;
332 334
333 /* 335 /* When this flag is set, new sessions should be blocked. */
334 * When this flag is set, new sessions should be 336 blocked = test_sta_flag(sta, WLAN_STA_BLOCK_BA);
335 * blocked, and existing sessions will be torn
336 * down by the code that set the flag, so this
337 * need not run.
338 */
339 if (test_sta_flag(sta, WLAN_STA_BLOCK_BA))
340 return;
341 337
342 mutex_lock(&sta->ampdu_mlme.mtx); 338 mutex_lock(&sta->ampdu_mlme.mtx);
343 for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) { 339 for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) {
@@ -352,7 +348,8 @@ void ieee80211_ba_session_work(struct work_struct *work)
352 sta, tid, WLAN_BACK_RECIPIENT, 348 sta, tid, WLAN_BACK_RECIPIENT,
353 WLAN_REASON_UNSPECIFIED, true); 349 WLAN_REASON_UNSPECIFIED, true);
354 350
355 if (test_and_clear_bit(tid, 351 if (!blocked &&
352 test_and_clear_bit(tid,
356 sta->ampdu_mlme.tid_rx_manage_offl)) 353 sta->ampdu_mlme.tid_rx_manage_offl))
357 ___ieee80211_start_rx_ba_session(sta, 0, 0, 0, 1, tid, 354 ___ieee80211_start_rx_ba_session(sta, 0, 0, 0, 1, tid,
358 IEEE80211_MAX_AMPDU_BUF, 355 IEEE80211_MAX_AMPDU_BUF,
@@ -367,7 +364,7 @@ void ieee80211_ba_session_work(struct work_struct *work)
367 spin_lock_bh(&sta->lock); 364 spin_lock_bh(&sta->lock);
368 365
369 tid_tx = sta->ampdu_mlme.tid_start_tx[tid]; 366 tid_tx = sta->ampdu_mlme.tid_start_tx[tid];
370 if (tid_tx) { 367 if (!blocked && tid_tx) {
371 /* 368 /*
372 * Assign it over to the normal tid_tx array 369 * Assign it over to the normal tid_tx array
373 * where it "goes live". 370 * where it "goes live".
@@ -390,7 +387,8 @@ void ieee80211_ba_session_work(struct work_struct *work)
390 if (!tid_tx) 387 if (!tid_tx)
391 continue; 388 continue;
392 389
393 if (test_and_clear_bit(HT_AGG_STATE_START_CB, &tid_tx->state)) 390 if (!blocked &&
391 test_and_clear_bit(HT_AGG_STATE_START_CB, &tid_tx->state))
394 ieee80211_start_tx_ba_cb(sta, tid, tid_tx); 392 ieee80211_start_tx_ba_cb(sta, tid, tid_tx);
395 if (test_and_clear_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state)) 393 if (test_and_clear_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state))
396 ___ieee80211_stop_tx_ba_session(sta, tid, 394 ___ieee80211_stop_tx_ba_session(sta, tid,