mac80211: process station blockack action frames from work
authorJohannes Berg <johannes.berg@intel.com>
Fri, 28 May 2010 13:22:58 +0000 (15:22 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 4 Jun 2010 19:50:51 +0000 (15:50 -0400)
Processing an association response could take a bit
of time while we set up the hardware etc. During that
time, the AP might already send a blockack request.
If this happens very quickly on a fairly slow machine,
we can end up processing the blockack request before
the association processing has finished. Since the
blockack processing cannot sleep right now, we also
cannot make it wait in the driver.

As a result, sometimes on slow machines the iwlagn
driver gets totally confused, and no traffic can pass
when the aggregation setup was done before the assoc
setup completed.

I'm working on a proper fix for this, which involves
queuing all blockack category action frames from a
work struct, and also allowing the ampdu_action driver
callback to sleep, which will generally clean up the
code and make things easier.

However, this is a very involved and complex change.
To fix the problem at hand in a way that can also be
backported to stable, I've come up with this patch.
Here, I simply process all aggregation action frames
from the managed interface skb queue, which means
their processing will be serialized with processing
the association response, thereby fixing the problem.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Cc: stable@kernel.org
Signed-off-by: John W. Linville <linville@tuxdriver.com>
net/mac80211/mlme.c
net/mac80211/rx.c

index 0839c4e..3310e70 100644 (file)
@@ -1692,14 +1692,52 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                        rma = ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
                        break;
                case IEEE80211_STYPE_ACTION:
-                       if (mgmt->u.action.category != WLAN_CATEGORY_SPECTRUM_MGMT)
+                       switch (mgmt->u.action.category) {
+                       case WLAN_CATEGORY_BACK: {
+                               struct ieee80211_local *local = sdata->local;
+                               int len = skb->len;
+                               struct sta_info *sta;
+
+                               rcu_read_lock();
+                               sta = sta_info_get(sdata, mgmt->sa);
+                               if (!sta) {
+                                       rcu_read_unlock();
+                                       break;
+                               }
+
+                               local_bh_disable();
+
+                               switch (mgmt->u.action.u.addba_req.action_code) {
+                               case WLAN_ACTION_ADDBA_REQ:
+                                       if (len < (IEEE80211_MIN_ACTION_SIZE +
+                                                  sizeof(mgmt->u.action.u.addba_req)))
+                                               break;
+                                       ieee80211_process_addba_request(local, sta, mgmt, len);
+                                       break;
+                               case WLAN_ACTION_ADDBA_RESP:
+                                       if (len < (IEEE80211_MIN_ACTION_SIZE +
+                                                  sizeof(mgmt->u.action.u.addba_resp)))
+                                               break;
+                                       ieee80211_process_addba_resp(local, sta, mgmt, len);
+                                       break;
+                               case WLAN_ACTION_DELBA:
+                                       if (len < (IEEE80211_MIN_ACTION_SIZE +
+                                                  sizeof(mgmt->u.action.u.delba)))
+                                               break;
+                                       ieee80211_process_delba(sdata, sta, mgmt, len);
+                                       break;
+                               }
+                               local_bh_enable();
+                               rcu_read_unlock();
                                break;
-
-                       ieee80211_sta_process_chanswitch(sdata,
-                                       &mgmt->u.action.u.chan_switch.sw_elem,
-                                       (void *)ifmgd->associated->priv,
-                                       rx_status->mactime);
-                       break;
+                               }
+                       case WLAN_CATEGORY_SPECTRUM_MGMT:
+                               ieee80211_sta_process_chanswitch(sdata,
+                                               &mgmt->u.action.u.chan_switch.sw_elem,
+                                               (void *)ifmgd->associated->priv,
+                                               rx_status->mactime);
+                               break;
+                       }
                }
                mutex_unlock(&ifmgd->mtx);
 
index 5e0b654..be9abc2 100644 (file)
@@ -1944,6 +1944,9 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                if (len < IEEE80211_MIN_ACTION_SIZE + 1)
                        break;
 
+               if (sdata->vif.type == NL80211_IFTYPE_STATION)
+                       return ieee80211_sta_rx_mgmt(sdata, rx->skb);
+
                switch (mgmt->u.action.u.addba_req.action_code) {
                case WLAN_ACTION_ADDBA_REQ:
                        if (len < (IEEE80211_MIN_ACTION_SIZE +