diff options
author | Johannes Berg <johannes.berg@intel.com> | 2017-09-06 09:01:42 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2017-09-06 09:22:02 -0400 |
commit | bde59c475e0883e4c4294bcd9b9c7e08ae18c828 (patch) | |
tree | 2bc99ff875407d538dd1c6c8f2cced8878528ad9 /net/mac80211 | |
parent | 98e93e968e4947cd71c2eb69e323682daa453ee7 (diff) |
mac80211: fix deadlock in driver-managed RX BA session start
When an RX BA session is started by the driver, and it has to tell
mac80211 about it, the corresponding bit in tid_rx_manage_offl gets
set and the BA session work is scheduled. Upon testing this bit, it
will call __ieee80211_start_rx_ba_session(), thus deadlocking as it
already holds the ampdu_mlme.mtx, which that acquires again.
Fix this by adding ___ieee80211_start_rx_ba_session(), a version of
the function that requires the mutex already held.
Cc: stable@vger.kernel.org
Fixes: 699cb58c8a52 ("mac80211: manage RX BA session offload without SKB queue")
Reported-by: Matteo Croce <mcroce@redhat.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/agg-rx.c | 32 | ||||
-rw-r--r-- | net/mac80211/ht.c | 6 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 4 |
3 files changed, 28 insertions, 14 deletions
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 2b36eff5d97e..2849a1fc41c5 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c | |||
@@ -245,10 +245,10 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d | |||
245 | ieee80211_tx_skb(sdata, skb); | 245 | ieee80211_tx_skb(sdata, skb); |
246 | } | 246 | } |
247 | 247 | ||
248 | void __ieee80211_start_rx_ba_session(struct sta_info *sta, | 248 | void ___ieee80211_start_rx_ba_session(struct sta_info *sta, |
249 | u8 dialog_token, u16 timeout, | 249 | u8 dialog_token, u16 timeout, |
250 | u16 start_seq_num, u16 ba_policy, u16 tid, | 250 | u16 start_seq_num, u16 ba_policy, u16 tid, |
251 | u16 buf_size, bool tx, bool auto_seq) | 251 | u16 buf_size, bool tx, bool auto_seq) |
252 | { | 252 | { |
253 | struct ieee80211_local *local = sta->sdata->local; | 253 | struct ieee80211_local *local = sta->sdata->local; |
254 | struct tid_ampdu_rx *tid_agg_rx; | 254 | struct tid_ampdu_rx *tid_agg_rx; |
@@ -267,7 +267,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, | |||
267 | ht_dbg(sta->sdata, | 267 | ht_dbg(sta->sdata, |
268 | "STA %pM requests BA session on unsupported tid %d\n", | 268 | "STA %pM requests BA session on unsupported tid %d\n", |
269 | sta->sta.addr, tid); | 269 | sta->sta.addr, tid); |
270 | goto end_no_lock; | 270 | goto end; |
271 | } | 271 | } |
272 | 272 | ||
273 | if (!sta->sta.ht_cap.ht_supported) { | 273 | if (!sta->sta.ht_cap.ht_supported) { |
@@ -275,14 +275,14 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, | |||
275 | "STA %pM erroneously requests BA session on tid %d w/o QoS\n", | 275 | "STA %pM erroneously requests BA session on tid %d w/o QoS\n", |
276 | sta->sta.addr, tid); | 276 | sta->sta.addr, tid); |
277 | /* send a response anyway, it's an error case if we get here */ | 277 | /* send a response anyway, it's an error case if we get here */ |
278 | goto end_no_lock; | 278 | goto end; |
279 | } | 279 | } |
280 | 280 | ||
281 | if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { | 281 | if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { |
282 | ht_dbg(sta->sdata, | 282 | ht_dbg(sta->sdata, |
283 | "Suspend in progress - Denying ADDBA request (%pM tid %d)\n", | 283 | "Suspend in progress - Denying ADDBA request (%pM tid %d)\n", |
284 | sta->sta.addr, tid); | 284 | sta->sta.addr, tid); |
285 | goto end_no_lock; | 285 | goto end; |
286 | } | 286 | } |
287 | 287 | ||
288 | /* sanity check for incoming parameters: | 288 | /* sanity check for incoming parameters: |
@@ -296,7 +296,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, | |||
296 | ht_dbg_ratelimited(sta->sdata, | 296 | ht_dbg_ratelimited(sta->sdata, |
297 | "AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n", | 297 | "AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n", |
298 | sta->sta.addr, tid, ba_policy, buf_size); | 298 | sta->sta.addr, tid, ba_policy, buf_size); |
299 | goto end_no_lock; | 299 | goto end; |
300 | } | 300 | } |
301 | /* determine default buffer size */ | 301 | /* determine default buffer size */ |
302 | if (buf_size == 0) | 302 | if (buf_size == 0) |
@@ -311,7 +311,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, | |||
311 | buf_size, sta->sta.addr); | 311 | buf_size, sta->sta.addr); |
312 | 312 | ||
313 | /* examine state machine */ | 313 | /* examine state machine */ |
314 | mutex_lock(&sta->ampdu_mlme.mtx); | 314 | lockdep_assert_held(&sta->ampdu_mlme.mtx); |
315 | 315 | ||
316 | if (test_bit(tid, sta->ampdu_mlme.agg_session_valid)) { | 316 | if (test_bit(tid, sta->ampdu_mlme.agg_session_valid)) { |
317 | if (sta->ampdu_mlme.tid_rx_token[tid] == dialog_token) { | 317 | if (sta->ampdu_mlme.tid_rx_token[tid] == dialog_token) { |
@@ -415,15 +415,25 @@ end: | |||
415 | __clear_bit(tid, sta->ampdu_mlme.unexpected_agg); | 415 | __clear_bit(tid, sta->ampdu_mlme.unexpected_agg); |
416 | sta->ampdu_mlme.tid_rx_token[tid] = dialog_token; | 416 | sta->ampdu_mlme.tid_rx_token[tid] = dialog_token; |
417 | } | 417 | } |
418 | mutex_unlock(&sta->ampdu_mlme.mtx); | ||
419 | 418 | ||
420 | end_no_lock: | ||
421 | if (tx) | 419 | if (tx) |
422 | ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid, | 420 | ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid, |
423 | dialog_token, status, 1, buf_size, | 421 | dialog_token, status, 1, buf_size, |
424 | timeout); | 422 | timeout); |
425 | } | 423 | } |
426 | 424 | ||
425 | void __ieee80211_start_rx_ba_session(struct sta_info *sta, | ||
426 | u8 dialog_token, u16 timeout, | ||
427 | u16 start_seq_num, u16 ba_policy, u16 tid, | ||
428 | u16 buf_size, bool tx, bool auto_seq) | ||
429 | { | ||
430 | mutex_lock(&sta->ampdu_mlme.mtx); | ||
431 | ___ieee80211_start_rx_ba_session(sta, dialog_token, timeout, | ||
432 | start_seq_num, ba_policy, tid, | ||
433 | buf_size, tx, auto_seq); | ||
434 | mutex_unlock(&sta->ampdu_mlme.mtx); | ||
435 | } | ||
436 | |||
427 | void ieee80211_process_addba_request(struct ieee80211_local *local, | 437 | void ieee80211_process_addba_request(struct ieee80211_local *local, |
428 | struct sta_info *sta, | 438 | struct sta_info *sta, |
429 | struct ieee80211_mgmt *mgmt, | 439 | struct ieee80211_mgmt *mgmt, |
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 4cba7fca10d4..d6d0b4201e40 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c | |||
@@ -351,9 +351,9 @@ void ieee80211_ba_session_work(struct work_struct *work) | |||
351 | 351 | ||
352 | if (test_and_clear_bit(tid, | 352 | if (test_and_clear_bit(tid, |
353 | sta->ampdu_mlme.tid_rx_manage_offl)) | 353 | sta->ampdu_mlme.tid_rx_manage_offl)) |
354 | __ieee80211_start_rx_ba_session(sta, 0, 0, 0, 1, tid, | 354 | ___ieee80211_start_rx_ba_session(sta, 0, 0, 0, 1, tid, |
355 | IEEE80211_MAX_AMPDU_BUF, | 355 | IEEE80211_MAX_AMPDU_BUF, |
356 | false, true); | 356 | false, true); |
357 | 357 | ||
358 | if (test_and_clear_bit(tid + IEEE80211_NUM_TIDS, | 358 | if (test_and_clear_bit(tid + IEEE80211_NUM_TIDS, |
359 | sta->ampdu_mlme.tid_rx_manage_offl)) | 359 | sta->ampdu_mlme.tid_rx_manage_offl)) |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 2197c62a0a6e..9675814f64db 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -1760,6 +1760,10 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, | |||
1760 | u8 dialog_token, u16 timeout, | 1760 | u8 dialog_token, u16 timeout, |
1761 | u16 start_seq_num, u16 ba_policy, u16 tid, | 1761 | u16 start_seq_num, u16 ba_policy, u16 tid, |
1762 | u16 buf_size, bool tx, bool auto_seq); | 1762 | u16 buf_size, bool tx, bool auto_seq); |
1763 | void ___ieee80211_start_rx_ba_session(struct sta_info *sta, | ||
1764 | u8 dialog_token, u16 timeout, | ||
1765 | u16 start_seq_num, u16 ba_policy, u16 tid, | ||
1766 | u16 buf_size, bool tx, bool auto_seq); | ||
1763 | void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, | 1767 | void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, |
1764 | enum ieee80211_agg_stop_reason reason); | 1768 | enum ieee80211_agg_stop_reason reason); |
1765 | void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, | 1769 | void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, |