Commit bde59c47 authored by Johannes Berg's avatar Johannes Berg

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: 699cb58c ("mac80211: manage RX BA session offload without SKB queue")
Reported-by: default avatarMatteo Croce <mcroce@redhat.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 98e93e96
...@@ -245,7 +245,7 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d ...@@ -245,7 +245,7 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d
ieee80211_tx_skb(sdata, skb); ieee80211_tx_skb(sdata, skb);
} }
void __ieee80211_start_rx_ba_session(struct sta_info *sta, void ___ieee80211_start_rx_ba_session(struct sta_info *sta,
u8 dialog_token, u16 timeout, u8 dialog_token, u16 timeout,
u16 start_seq_num, u16 ba_policy, u16 tid, u16 start_seq_num, u16 ba_policy, u16 tid,
u16 buf_size, bool tx, bool auto_seq) u16 buf_size, bool tx, bool auto_seq)
...@@ -267,7 +267,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, ...@@ -267,7 +267,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
ht_dbg(sta->sdata, ht_dbg(sta->sdata,
"STA %pM requests BA session on unsupported tid %d\n", "STA %pM requests BA session on unsupported tid %d\n",
sta->sta.addr, tid); sta->sta.addr, tid);
goto end_no_lock; goto end;
} }
if (!sta->sta.ht_cap.ht_supported) { if (!sta->sta.ht_cap.ht_supported) {
...@@ -275,14 +275,14 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, ...@@ -275,14 +275,14 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
"STA %pM erroneously requests BA session on tid %d w/o QoS\n", "STA %pM erroneously requests BA session on tid %d w/o QoS\n",
sta->sta.addr, tid); sta->sta.addr, tid);
/* send a response anyway, it's an error case if we get here */ /* send a response anyway, it's an error case if we get here */
goto end_no_lock; goto end;
} }
if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
ht_dbg(sta->sdata, ht_dbg(sta->sdata,
"Suspend in progress - Denying ADDBA request (%pM tid %d)\n", "Suspend in progress - Denying ADDBA request (%pM tid %d)\n",
sta->sta.addr, tid); sta->sta.addr, tid);
goto end_no_lock; goto end;
} }
/* sanity check for incoming parameters: /* sanity check for incoming parameters:
...@@ -296,7 +296,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, ...@@ -296,7 +296,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
ht_dbg_ratelimited(sta->sdata, ht_dbg_ratelimited(sta->sdata,
"AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n", "AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n",
sta->sta.addr, tid, ba_policy, buf_size); sta->sta.addr, tid, ba_policy, buf_size);
goto end_no_lock; goto end;
} }
/* determine default buffer size */ /* determine default buffer size */
if (buf_size == 0) if (buf_size == 0)
...@@ -311,7 +311,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, ...@@ -311,7 +311,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
buf_size, sta->sta.addr); buf_size, sta->sta.addr);
/* examine state machine */ /* examine state machine */
mutex_lock(&sta->ampdu_mlme.mtx); lockdep_assert_held(&sta->ampdu_mlme.mtx);
if (test_bit(tid, sta->ampdu_mlme.agg_session_valid)) { if (test_bit(tid, sta->ampdu_mlme.agg_session_valid)) {
if (sta->ampdu_mlme.tid_rx_token[tid] == dialog_token) { if (sta->ampdu_mlme.tid_rx_token[tid] == dialog_token) {
...@@ -415,15 +415,25 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, ...@@ -415,15 +415,25 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
__clear_bit(tid, sta->ampdu_mlme.unexpected_agg); __clear_bit(tid, sta->ampdu_mlme.unexpected_agg);
sta->ampdu_mlme.tid_rx_token[tid] = dialog_token; sta->ampdu_mlme.tid_rx_token[tid] = dialog_token;
} }
mutex_unlock(&sta->ampdu_mlme.mtx);
end_no_lock:
if (tx) if (tx)
ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid, ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid,
dialog_token, status, 1, buf_size, dialog_token, status, 1, buf_size,
timeout); timeout);
} }
void __ieee80211_start_rx_ba_session(struct sta_info *sta,
u8 dialog_token, u16 timeout,
u16 start_seq_num, u16 ba_policy, u16 tid,
u16 buf_size, bool tx, bool auto_seq)
{
mutex_lock(&sta->ampdu_mlme.mtx);
___ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
start_seq_num, ba_policy, tid,
buf_size, tx, auto_seq);
mutex_unlock(&sta->ampdu_mlme.mtx);
}
void ieee80211_process_addba_request(struct ieee80211_local *local, void ieee80211_process_addba_request(struct ieee80211_local *local,
struct sta_info *sta, struct sta_info *sta,
struct ieee80211_mgmt *mgmt, struct ieee80211_mgmt *mgmt,
......
...@@ -351,7 +351,7 @@ void ieee80211_ba_session_work(struct work_struct *work) ...@@ -351,7 +351,7 @@ void ieee80211_ba_session_work(struct work_struct *work)
if (test_and_clear_bit(tid, if (test_and_clear_bit(tid,
sta->ampdu_mlme.tid_rx_manage_offl)) sta->ampdu_mlme.tid_rx_manage_offl))
__ieee80211_start_rx_ba_session(sta, 0, 0, 0, 1, tid, ___ieee80211_start_rx_ba_session(sta, 0, 0, 0, 1, tid,
IEEE80211_MAX_AMPDU_BUF, IEEE80211_MAX_AMPDU_BUF,
false, true); false, true);
......
...@@ -1760,6 +1760,10 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, ...@@ -1760,6 +1760,10 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
u8 dialog_token, u16 timeout, u8 dialog_token, u16 timeout,
u16 start_seq_num, u16 ba_policy, u16 tid, u16 start_seq_num, u16 ba_policy, u16 tid,
u16 buf_size, bool tx, bool auto_seq); u16 buf_size, bool tx, bool auto_seq);
void ___ieee80211_start_rx_ba_session(struct sta_info *sta,
u8 dialog_token, u16 timeout,
u16 start_seq_num, u16 ba_policy, u16 tid,
u16 buf_size, bool tx, bool auto_seq);
void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
enum ieee80211_agg_stop_reason reason); enum ieee80211_agg_stop_reason reason);
void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment