Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[pandora-kernel.git] / net / mac80211 / rx.c
index 6a15632..a8aa0f2 100644 (file)
@@ -719,16 +719,13 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
 
        tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
 
-       spin_lock(&sta->lock);
-
-       if (!sta->ampdu_mlme.tid_active_rx[tid])
-               goto dont_reorder_unlock;
-
-       tid_agg_rx = sta->ampdu_mlme.tid_rx[tid];
+       tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
+       if (!tid_agg_rx)
+               goto dont_reorder;
 
        /* qos null data frames are excluded */
        if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC)))
-               goto dont_reorder_unlock;
+               goto dont_reorder;
 
        /* new, potentially un-ordered, ampdu frame - process it */
 
@@ -740,20 +737,22 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
        /* if this mpdu is fragmented - terminate rx aggregation session */
        sc = le16_to_cpu(hdr->seq_ctrl);
        if (sc & IEEE80211_SCTL_FRAG) {
-               spin_unlock(&sta->lock);
-               __ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
-                                              WLAN_REASON_QSTA_REQUIRE_SETUP);
-               dev_kfree_skb(skb);
+               skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME;
+               skb_queue_tail(&rx->sdata->skb_queue, skb);
+               ieee80211_queue_work(&local->hw, &rx->sdata->work);
                return;
        }
 
-       if (ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, frames)) {
-               spin_unlock(&sta->lock);
+       /*
+        * No locking needed -- we will only ever process one
+        * RX packet at a time, and thus own tid_agg_rx. All
+        * other code manipulating it needs to (and does) make
+        * sure that we cannot get to it any more before doing
+        * anything with it.
+        */
+       if (ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, frames))
                return;
-       }
 
- dont_reorder_unlock:
-       spin_unlock(&sta->lock);
  dont_reorder:
        __skb_queue_tail(frames, skb);
 }
@@ -1268,11 +1267,13 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
                                                 rx->queue, &(rx->skb));
                if (rx->key && rx->key->conf.alg == ALG_CCMP &&
                    ieee80211_has_protected(fc)) {
+                       int queue = ieee80211_is_mgmt(fc) ?
+                               NUM_RX_DATA_QUEUES : rx->queue;
                        /* Store CCMP PN so that we can verify that the next
                         * fragment has a sequential PN value. */
                        entry->ccmp = 1;
                        memcpy(entry->last_pn,
-                              rx->key->u.ccmp.rx_pn[rx->queue],
+                              rx->key->u.ccmp.rx_pn[queue],
                               CCMP_PN_LEN);
                }
                return RX_QUEUED;
@@ -1292,6 +1293,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
        if (entry->ccmp) {
                int i;
                u8 pn[CCMP_PN_LEN], *rpn;
+               int queue;
                if (!rx->key || rx->key->conf.alg != ALG_CCMP)
                        return RX_DROP_UNUSABLE;
                memcpy(pn, entry->last_pn, CCMP_PN_LEN);
@@ -1300,7 +1302,9 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
                        if (pn[i])
                                break;
                }
-               rpn = rx->key->u.ccmp.rx_pn[rx->queue];
+               queue = ieee80211_is_mgmt(fc) ?
+                       NUM_RX_DATA_QUEUES : rx->queue;
+               rpn = rx->key->u.ccmp.rx_pn[queue];
                if (memcmp(pn, rpn, CCMP_PN_LEN))
                        return RX_DROP_UNUSABLE;
                memcpy(entry->last_pn, pn, CCMP_PN_LEN);
@@ -1830,13 +1834,11 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)
                                  &bar_data, sizeof(bar_data)))
                        return RX_DROP_MONITOR;
 
-               spin_lock(&rx->sta->lock);
                tid = le16_to_cpu(bar_data.control) >> 12;
-               if (!rx->sta->ampdu_mlme.tid_active_rx[tid]) {
-                       spin_unlock(&rx->sta->lock);
+
+               tid_agg_rx = rcu_dereference(rx->sta->ampdu_mlme.tid_rx[tid]);
+               if (!tid_agg_rx)
                        return RX_DROP_MONITOR;
-               }
-               tid_agg_rx = rx->sta->ampdu_mlme.tid_rx[tid];
 
                start_seq_num = le16_to_cpu(bar_data.start_seq_num) >> 4;
 
@@ -1849,7 +1851,6 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)
                ieee80211_release_reorder_frames(hw, tid_agg_rx, start_seq_num,
                                                 frames);
                kfree_skb(skb);
-               spin_unlock(&rx->sta->lock);
                return RX_QUEUED;
        }
 
@@ -1950,30 +1951,27 @@ 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 +
                                   sizeof(mgmt->u.action.u.addba_req)))
-                               return RX_DROP_MONITOR;
-                       ieee80211_process_addba_request(local, rx->sta, mgmt, len);
-                       goto handled;
+                               goto invalid;
+                       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, rx->sta, mgmt, len);
-                       goto handled;
+                               goto invalid;
+                       break;
                case WLAN_ACTION_DELBA:
                        if (len < (IEEE80211_MIN_ACTION_SIZE +
                                   sizeof(mgmt->u.action.u.delba)))
-                               break;
-                       ieee80211_process_delba(sdata, rx->sta, mgmt, len);
-                       goto handled;
+                               goto invalid;
+                       break;
+               default:
+                       goto invalid;
                }
-               break;
+
+               goto queue;
        case WLAN_CATEGORY_SPECTRUM_MGMT:
                if (local->hw.conf.channel->band != IEEE80211_BAND_5GHZ)
                        break;
@@ -2003,7 +2001,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                        if (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN))
                                break;
 
-                       return ieee80211_sta_rx_mgmt(sdata, rx->skb);
+                       goto queue;
                }
                break;
        case WLAN_CATEGORY_SA_QUERY:
@@ -2021,11 +2019,12 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                break;
        case WLAN_CATEGORY_MESH_PLINK:
        case WLAN_CATEGORY_MESH_PATH_SEL:
-               if (ieee80211_vif_is_mesh(&sdata->vif))
-                       return ieee80211_mesh_rx_mgmt(sdata, rx->skb);
-               break;
+               if (!ieee80211_vif_is_mesh(&sdata->vif))
+                       break;
+               goto queue;
        }
 
+ invalid:
        /*
         * For AP mode, hostapd is responsible for handling any action
         * frames that we didn't handle, including returning unknown
@@ -2045,8 +2044,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
         */
        status = IEEE80211_SKB_RXCB(rx->skb);
 
-       if (sdata->vif.type == NL80211_IFTYPE_STATION &&
-           cfg80211_rx_action(rx->sdata->dev, status->freq,
+       if (cfg80211_rx_action(rx->sdata->dev, status->freq,
                               rx->skb->data, rx->skb->len,
                               GFP_ATOMIC))
                goto handled;
@@ -2074,6 +2072,14 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                rx->sta->rx_packets++;
        dev_kfree_skb(rx->skb);
        return RX_QUEUED;
+
+ queue:
+       rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME;
+       skb_queue_tail(&sdata->skb_queue, rx->skb);
+       ieee80211_queue_work(&local->hw, &sdata->work);
+       if (rx->sta)
+               rx->sta->rx_packets++;
+       return RX_QUEUED;
 }
 
 static ieee80211_rx_result debug_noinline
@@ -2081,10 +2087,15 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
 {
        struct ieee80211_sub_if_data *sdata = rx->sdata;
        ieee80211_rx_result rxs;
+       struct ieee80211_mgmt *mgmt = (void *)rx->skb->data;
+       __le16 stype;
 
        if (!(rx->flags & IEEE80211_RX_RA_MATCH))
                return RX_DROP_MONITOR;
 
+       if (rx->skb->len < 24)
+               return RX_DROP_MONITOR;
+
        if (ieee80211_drop_unencrypted_mgmt(rx))
                return RX_DROP_UNUSABLE;
 
@@ -2092,16 +2103,42 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
        if (rxs != RX_CONTINUE)
                return rxs;
 
-       if (ieee80211_vif_is_mesh(&sdata->vif))
-               return ieee80211_mesh_rx_mgmt(sdata, rx->skb);
+       stype = mgmt->frame_control & cpu_to_le16(IEEE80211_FCTL_STYPE);
 
-       if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
-               return ieee80211_ibss_rx_mgmt(sdata, rx->skb);
+       if (!ieee80211_vif_is_mesh(&sdata->vif) &&
+           sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+           sdata->vif.type != NL80211_IFTYPE_STATION)
+               return RX_DROP_MONITOR;
+
+       switch (stype) {
+       case cpu_to_le16(IEEE80211_STYPE_BEACON):
+       case cpu_to_le16(IEEE80211_STYPE_PROBE_RESP):
+               /* process for all: mesh, mlme, ibss */
+               break;
+       case cpu_to_le16(IEEE80211_STYPE_DEAUTH):
+       case cpu_to_le16(IEEE80211_STYPE_DISASSOC):
+               /* process only for station */
+               if (sdata->vif.type != NL80211_IFTYPE_STATION)
+                       return RX_DROP_MONITOR;
+               break;
+       case cpu_to_le16(IEEE80211_STYPE_PROBE_REQ):
+       case cpu_to_le16(IEEE80211_STYPE_AUTH):
+               /* process only for ibss */
+               if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
+                       return RX_DROP_MONITOR;
+               break;
+       default:
+               return RX_DROP_MONITOR;
+       }
 
-       if (sdata->vif.type == NL80211_IFTYPE_STATION)
-               return ieee80211_sta_rx_mgmt(sdata, rx->skb);
+       /* queue up frame and kick off work to process it */
+       rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME;
+       skb_queue_tail(&sdata->skb_queue, rx->skb);
+       ieee80211_queue_work(&rx->local->hw, &sdata->work);
+       if (rx->sta)
+               rx->sta->rx_packets++;
 
-       return RX_DROP_MONITOR;
+       return RX_QUEUED;
 }
 
 static void ieee80211_rx_michael_mic_report(struct ieee80211_hdr *hdr,