Skip to content

Commit

Permalink
mac80211: fix TX aggregation stop race
Browse files Browse the repository at this point in the history
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 <[email protected]>
[johannes: "enabled" -> "blocked" and invert logic, simplify init]
Signed-off-by: Johannes Berg <[email protected]>
  • Loading branch information
Alexander Wetzel authored and jmberg-intel committed May 18, 2018
1 parent 4a22b00 commit 39c1134
Showing 1 changed file with 21 additions and 23 deletions.
44 changes: 21 additions & 23 deletions net/mac80211/ht.c
Original file line number Diff line number Diff line change
Expand Up @@ -301,43 +301,39 @@ void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
___ieee80211_stop_tx_ba_session(sta, i, reason);
mutex_unlock(&sta->ampdu_mlme.mtx);

/* stopping might queue the work again - so cancel only afterwards */
cancel_work_sync(&sta->ampdu_mlme.work);

/*
* In case the tear down is part of a reconfigure due to HW restart
* request, it is possible that the low level driver requested to stop
* the BA session, so handle it to properly clean tid_tx data.
*/
mutex_lock(&sta->ampdu_mlme.mtx);
for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
struct tid_ampdu_tx *tid_tx =
rcu_dereference_protected_tid_tx(sta, i);
if(reason == AGG_STOP_DESTROY_STA) {
cancel_work_sync(&sta->ampdu_mlme.work);

if (!tid_tx)
continue;
mutex_lock(&sta->ampdu_mlme.mtx);
for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
struct tid_ampdu_tx *tid_tx =
rcu_dereference_protected_tid_tx(sta, i);

if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, &tid_tx->state))
ieee80211_stop_tx_ba_cb(sta, i, tid_tx);
if (!tid_tx)
continue;

if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, &tid_tx->state))
ieee80211_stop_tx_ba_cb(sta, i, tid_tx);
}
mutex_unlock(&sta->ampdu_mlme.mtx);
}
mutex_unlock(&sta->ampdu_mlme.mtx);
}

void ieee80211_ba_session_work(struct work_struct *work)
{
struct sta_info *sta =
container_of(work, struct sta_info, ampdu_mlme.work);
struct tid_ampdu_tx *tid_tx;
bool blocked;
int tid;

/*
* When this flag is set, new sessions should be
* blocked, and existing sessions will be torn
* down by the code that set the flag, so this
* need not run.
*/
if (test_sta_flag(sta, WLAN_STA_BLOCK_BA))
return;
/* When this flag is set, new sessions should be blocked. */
blocked = test_sta_flag(sta, WLAN_STA_BLOCK_BA);

mutex_lock(&sta->ampdu_mlme.mtx);
for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) {
Expand All @@ -352,7 +348,8 @@ void ieee80211_ba_session_work(struct work_struct *work)
sta, tid, WLAN_BACK_RECIPIENT,
WLAN_REASON_UNSPECIFIED, true);

if (test_and_clear_bit(tid,
if (!blocked &&
test_and_clear_bit(tid,
sta->ampdu_mlme.tid_rx_manage_offl))
___ieee80211_start_rx_ba_session(sta, 0, 0, 0, 1, tid,
IEEE80211_MAX_AMPDU_BUF,
Expand All @@ -367,7 +364,7 @@ void ieee80211_ba_session_work(struct work_struct *work)
spin_lock_bh(&sta->lock);

tid_tx = sta->ampdu_mlme.tid_start_tx[tid];
if (tid_tx) {
if (!blocked && tid_tx) {
/*
* Assign it over to the normal tid_tx array
* where it "goes live".
Expand All @@ -390,7 +387,8 @@ void ieee80211_ba_session_work(struct work_struct *work)
if (!tid_tx)
continue;

if (test_and_clear_bit(HT_AGG_STATE_START_CB, &tid_tx->state))
if (!blocked &&
test_and_clear_bit(HT_AGG_STATE_START_CB, &tid_tx->state))
ieee80211_start_tx_ba_cb(sta, tid, tid_tx);
if (test_and_clear_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state))
___ieee80211_stop_tx_ba_session(sta, tid,
Expand Down

0 comments on commit 39c1134

Please sign in to comment.