summaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2017-09-06 09:01:42 -0400
committerJohannes Berg <johannes.berg@intel.com>2017-09-06 09:22:02 -0400
commitbde59c475e0883e4c4294bcd9b9c7e08ae18c828 (patch)
tree2bc99ff875407d538dd1c6c8f2cced8878528ad9 /net/mac80211
parent98e93e968e4947cd71c2eb69e323682daa453ee7 (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.c32
-rw-r--r--net/mac80211/ht.c6
-rw-r--r--net/mac80211/ieee80211_i.h4
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
248void __ieee80211_start_rx_ba_session(struct sta_info *sta, 248void ___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
420end_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
425void __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
427void ieee80211_process_addba_request(struct ieee80211_local *local, 437void 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);
1763void ___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);
1763void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, 1767void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
1764 enum ieee80211_agg_stop_reason reason); 1768 enum ieee80211_agg_stop_reason reason);
1765void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, 1769void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,