4.13-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Johannes Berg <johannes.b...@intel.com>

commit bde59c475e0883e4c4294bcd9b9c7e08ae18c828 upstream.

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.

Fixes: 699cb58c8a52 ("mac80211: manage RX BA session offload without SKB queue")
Reported-by: Matteo Croce <mcr...@redhat.com>
Signed-off-by: Johannes Berg <johannes.b...@intel.com>
Signed-off-by: Greg Kroah-Hartman <gre...@linuxfoundation.org>

---
 net/mac80211/agg-rx.c      |   32 +++++++++++++++++++++-----------
 net/mac80211/ht.c          |    6 +++---
 net/mac80211/ieee80211_i.h |    4 ++++
 3 files changed, 28 insertions(+), 14 deletions(-)

--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -245,10 +245,10 @@ static void ieee80211_send_addba_resp(st
        ieee80211_tx_skb(sdata, skb);
 }
 
-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_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)
 {
        struct ieee80211_local *local = sta->sdata->local;
        struct tid_ampdu_rx *tid_agg_rx;
@@ -267,7 +267,7 @@ void __ieee80211_start_rx_ba_session(str
                ht_dbg(sta->sdata,
                       "STA %pM requests BA session on unsupported tid %d\n",
                       sta->sta.addr, tid);
-               goto end_no_lock;
+               goto end;
        }
 
        if (!sta->sta.ht_cap.ht_supported) {
@@ -275,14 +275,14 @@ void __ieee80211_start_rx_ba_session(str
                       "STA %pM erroneously requests BA session on tid %d w/o 
QoS\n",
                       sta->sta.addr, tid);
                /* 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)) {
                ht_dbg(sta->sdata,
                       "Suspend in progress - Denying ADDBA request (%pM tid 
%d)\n",
                       sta->sta.addr, tid);
-               goto end_no_lock;
+               goto end;
        }
 
        /* sanity check for incoming parameters:
@@ -296,7 +296,7 @@ void __ieee80211_start_rx_ba_session(str
                ht_dbg_ratelimited(sta->sdata,
                                   "AddBA Req with bad params from %pM on tid 
%u. policy %d, buffer size %d\n",
                                   sta->sta.addr, tid, ba_policy, buf_size);
-               goto end_no_lock;
+               goto end;
        }
        /* determine default buffer size */
        if (buf_size == 0)
@@ -311,7 +311,7 @@ void __ieee80211_start_rx_ba_session(str
               buf_size, sta->sta.addr);
 
        /* 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 (sta->ampdu_mlme.tid_rx_token[tid] == dialog_token) {
@@ -415,15 +415,25 @@ end:
                __clear_bit(tid, sta->ampdu_mlme.unexpected_agg);
                sta->ampdu_mlme.tid_rx_token[tid] = dialog_token;
        }
-       mutex_unlock(&sta->ampdu_mlme.mtx);
 
-end_no_lock:
        if (tx)
                ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid,
                                          dialog_token, status, 1, buf_size,
                                          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,
                                     struct sta_info *sta,
                                     struct ieee80211_mgmt *mgmt,
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -333,9 +333,9 @@ void ieee80211_ba_session_work(struct wo
 
                if (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,
-                                                       false, true);
+                       ___ieee80211_start_rx_ba_session(sta, 0, 0, 0, 1, tid,
+                                                        
IEEE80211_MAX_AMPDU_BUF,
+                                                        false, true);
 
                if (test_and_clear_bit(tid + IEEE80211_NUM_TIDS,
                                       sta->ampdu_mlme.tid_rx_manage_offl))
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1760,6 +1760,10 @@ void __ieee80211_start_rx_ba_session(str
                                     u8 dialog_token, u16 timeout,
                                     u16 start_seq_num, u16 ba_policy, u16 tid,
                                     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,
                                         enum ieee80211_agg_stop_reason reason);
 void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,


Reply via email to