mac80211: New stat counters for multicast and unicast forwarded frames
[pandora-kernel.git] / net / mac80211 / rx.c
index de5bba7..7065fd7 100644 (file)
@@ -30,7 +30,6 @@
 static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
                                           struct tid_ampdu_rx *tid_agg_rx,
                                           struct sk_buff *skb,
-                                          struct ieee80211_rx_status *status,
                                           u16 mpdu_seq_num,
                                           int bar_req);
 /*
@@ -59,11 +58,11 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
        return skb;
 }
 
-static inline int should_drop_frame(struct ieee80211_rx_status *status,
-                                   struct sk_buff *skb,
+static inline int should_drop_frame(struct sk_buff *skb,
                                    int present_fcs_len,
                                    int radiotap_len)
 {
+       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 
        if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
@@ -111,10 +110,10 @@ ieee80211_rx_radiotap_len(struct ieee80211_local *local,
 static void
 ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
                                 struct sk_buff *skb,
-                                struct ieee80211_rx_status *status,
                                 struct ieee80211_rate *rate,
                                 int rtap_len)
 {
+       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_radiotap_header *rthdr;
        unsigned char *pos;
 
@@ -220,9 +219,9 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
  */
 static struct sk_buff *
 ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
-                    struct ieee80211_rx_status *status,
                     struct ieee80211_rate *rate)
 {
+       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb);
        struct ieee80211_sub_if_data *sdata;
        int needed_headroom = 0;
        struct sk_buff *skb, *skb2;
@@ -248,8 +247,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
                present_fcs_len = FCS_LEN;
 
        if (!local->monitors) {
-               if (should_drop_frame(status, origskb, present_fcs_len,
-                                     rtap_len)) {
+               if (should_drop_frame(origskb, present_fcs_len, rtap_len)) {
                        dev_kfree_skb(origskb);
                        return NULL;
                }
@@ -257,7 +255,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
                return remove_monitor_info(local, origskb, rtap_len);
        }
 
-       if (should_drop_frame(status, origskb, present_fcs_len, rtap_len)) {
+       if (should_drop_frame(origskb, present_fcs_len, rtap_len)) {
                /* only need to expand headroom if necessary */
                skb = origskb;
                origskb = NULL;
@@ -289,7 +287,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
 
        /* if necessary, prepend radiotap information */
        if (!(status->flag & RX_FLAG_RADIOTAP))
-               ieee80211_add_rx_radiotap_header(local, skb, status, rate,
+               ieee80211_add_rx_radiotap_header(local, skb, rate,
                                                 needed_headroom);
 
        skb_reset_mac_header(skb);
@@ -420,13 +418,13 @@ ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx)
        struct ieee80211_local *local = rx->local;
        struct sk_buff *skb = rx->skb;
 
-       if (unlikely(local->hw_scanning))
-               return ieee80211_scan_rx(rx->sdata, skb, rx->status);
+       if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning)))
+               return ieee80211_scan_rx(rx->sdata, skb);
 
-       if (unlikely(local->sw_scanning)) {
+       if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning) &&
+                    (rx->flags & IEEE80211_RX_IN_SCAN))) {
                /* drop all the other packets during a software scan anyway */
-               if (ieee80211_scan_rx(rx->sdata, skb, rx->status)
-                   != RX_QUEUED)
+               if (ieee80211_scan_rx(rx->sdata, skb) != RX_QUEUED)
                        dev_kfree_skb(skb);
                return RX_QUEUED;
        }
@@ -491,12 +489,21 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
        unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control);
+       char *dev_addr = rx->dev->dev_addr;
 
        if (ieee80211_is_data(hdr->frame_control)) {
-               if (!ieee80211_has_a4(hdr->frame_control))
-                       return RX_DROP_MONITOR;
-               if (memcmp(hdr->addr4, rx->dev->dev_addr, ETH_ALEN) == 0)
-                       return RX_DROP_MONITOR;
+               if (is_multicast_ether_addr(hdr->addr1)) {
+                       if (ieee80211_has_tods(hdr->frame_control) ||
+                               !ieee80211_has_fromds(hdr->frame_control))
+                               return RX_DROP_MONITOR;
+                       if (memcmp(hdr->addr3, dev_addr, ETH_ALEN) == 0)
+                               return RX_DROP_MONITOR;
+               } else {
+                       if (!ieee80211_has_a4(hdr->frame_control))
+                               return RX_DROP_MONITOR;
+                       if (memcmp(hdr->addr4, dev_addr, ETH_ALEN) == 0)
+                               return RX_DROP_MONITOR;
+               }
        }
 
        /* If there is not an established peer link and this is not a peer link
@@ -529,7 +536,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
 
        if (ieee80211_is_data(hdr->frame_control) &&
            is_multicast_ether_addr(hdr->addr1) &&
-           mesh_rmc_check(hdr->addr4, msh_h_get(hdr, hdrlen), rx->sdata))
+           mesh_rmc_check(hdr->addr3, msh_h_get(hdr, hdrlen), rx->sdata))
                return RX_DROP_MONITOR;
 #undef msh_h_get
 
@@ -785,7 +792,7 @@ static void ap_sta_ps_start(struct sta_info *sta)
        struct ieee80211_local *local = sdata->local;
 
        atomic_inc(&sdata->bss->num_sta_ps);
-       set_and_clear_sta_flags(sta, WLAN_STA_PS, WLAN_STA_PSPOLL);
+       set_sta_flags(sta, WLAN_STA_PS);
        drv_sta_notify(local, &sdata->vif, STA_NOTIFY_SLEEP, &sta->sta);
 #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
        printk(KERN_DEBUG "%s: STA %pM aid %d enters power save mode\n",
@@ -801,7 +808,7 @@ static int ap_sta_ps_end(struct sta_info *sta)
 
        atomic_dec(&sdata->bss->num_sta_ps);
 
-       clear_sta_flags(sta, WLAN_STA_PS | WLAN_STA_PSPOLL);
+       clear_sta_flags(sta, WLAN_STA_PS);
        drv_sta_notify(local, &sdata->vif, STA_NOTIFY_AWAKE, &sta->sta);
 
        if (!skb_queue_empty(&sta->ps_tx_buf))
@@ -836,28 +843,22 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
        if (!sta)
                return RX_CONTINUE;
 
-       /* Update last_rx only for IBSS packets which are for the current
-        * BSSID to avoid keeping the current IBSS network alive in cases where
-        * other STAs are using different BSSID. */
+       /*
+        * Update last_rx only for IBSS packets which are for the current
+        * BSSID to avoid keeping the current IBSS network alive in cases
+        * where other STAs start using different BSSID.
+        */
        if (rx->sdata->vif.type == NL80211_IFTYPE_ADHOC) {
                u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len,
                                                NL80211_IFTYPE_ADHOC);
                if (compare_ether_addr(bssid, rx->sdata->u.ibss.bssid) == 0)
                        sta->last_rx = jiffies;
-       } else
-       if (!is_multicast_ether_addr(hdr->addr1) ||
-           rx->sdata->vif.type == NL80211_IFTYPE_STATION) {
-               /* Update last_rx only for unicast frames in order to prevent
-                * the Probe Request frames (the only broadcast frames from a
-                * STA in infrastructure mode) from keeping a connection alive.
+       } else if (!is_multicast_ether_addr(hdr->addr1)) {
+               /*
                 * Mesh beacons will update last_rx when if they are found to
                 * match the current local configuration when processed.
                 */
-               if (rx->sdata->vif.type == NL80211_IFTYPE_STATION &&
-                   ieee80211_is_beacon(hdr->frame_control)) {
-                       rx->sdata->u.mgd.last_beacon = jiffies;
-               } else
-                       sta->last_rx = jiffies;
+               sta->last_rx = jiffies;
        }
 
        if (!(rx->flags & IEEE80211_RX_RA_MATCH))
@@ -1125,14 +1126,15 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
                skb_queue_empty(&rx->sta->ps_tx_buf);
 
        if (skb) {
+               struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
                struct ieee80211_hdr *hdr =
                        (struct ieee80211_hdr *) skb->data;
 
                /*
-                * Tell TX path to send one frame even though the STA may
+                * Tell TX path to send this frame even though the STA may
                 * still remain is PS mode after this frame exchange.
                 */
-               set_sta_flags(rx->sta, WLAN_STA_PSPOLL);
+               info->flags |= IEEE80211_TX_CTL_PSPOLL_RESPONSE;
 
 #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
                printk(KERN_DEBUG "STA %pM aid %d: PS Poll (entries after %d)\n",
@@ -1147,7 +1149,7 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
                else
                        hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
 
-               dev_queue_xmit(skb);
+               ieee80211_add_pending_skb(rx->local, skb);
 
                if (no_pending_pkts)
                        sta_info_clear_tim_bit(rx->sta);
@@ -1487,10 +1489,13 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
        struct ieee80211s_hdr *mesh_hdr;
        unsigned int hdrlen;
        struct sk_buff *skb = rx->skb, *fwd_skb;
+       struct ieee80211_local *local = rx->local;
+       struct ieee80211_sub_if_data *sdata;
 
        hdr = (struct ieee80211_hdr *) skb->data;
        hdrlen = ieee80211_hdrlen(hdr->frame_control);
        mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
+       sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
 
        if (!ieee80211_is_data(hdr->frame_control))
                return RX_CONTINUE;
@@ -1499,11 +1504,10 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
                /* illegal frame */
                return RX_DROP_MONITOR;
 
-       if (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6){
-               struct ieee80211_sub_if_data *sdata;
+       if (!is_multicast_ether_addr(hdr->addr1) &&
+                       (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6)) {
                struct mesh_path *mppath;
 
-               sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
                rcu_read_lock();
                mppath = mpp_path_lookup(mesh_hdr->eaddr2, sdata);
                if (!mppath) {
@@ -1518,7 +1522,9 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
                rcu_read_unlock();
        }
 
-       if (compare_ether_addr(rx->dev->dev_addr, hdr->addr3) == 0)
+       /* Frame has reached destination.  Don't forward */
+       if (!is_multicast_ether_addr(hdr->addr1) &&
+                       compare_ether_addr(rx->dev->dev_addr, hdr->addr3) == 0)
                return RX_CONTINUE;
 
        mesh_hdr->ttl--;
@@ -1529,6 +1535,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
                                                     dropped_frames_ttl);
                else {
                        struct ieee80211_hdr *fwd_hdr;
+                       struct ieee80211_tx_info *info;
+
                        fwd_skb = skb_copy(skb, GFP_ATOMIC);
 
                        if (!fwd_skb && net_ratelimit())
@@ -1536,19 +1544,40 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
                                                   rx->dev->name);
 
                        fwd_hdr =  (struct ieee80211_hdr *) fwd_skb->data;
-                       /*
-                        * Save TA to addr1 to send TA a path error if a
-                        * suitable next hop is not found
-                        */
-                       memcpy(fwd_hdr->addr1, fwd_hdr->addr2, ETH_ALEN);
                        memcpy(fwd_hdr->addr2, rx->dev->dev_addr, ETH_ALEN);
-                       fwd_skb->dev = rx->local->mdev;
-                       fwd_skb->iif = rx->dev->ifindex;
-                       dev_queue_xmit(fwd_skb);
+                       info = IEEE80211_SKB_CB(fwd_skb);
+                       memset(info, 0, sizeof(*info));
+                       info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
+                       info->control.vif = &rx->sdata->vif;
+                       ieee80211_select_queue(local, fwd_skb);
+                       if (is_multicast_ether_addr(fwd_hdr->addr1))
+                               IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh,
+                                                               fwded_mcast);
+                       else {
+                               int err;
+                               /*
+                                * Save TA to addr1 to send TA a path error if a
+                                * suitable next hop is not found
+                                */
+                               memcpy(fwd_hdr->addr1, fwd_hdr->addr2,
+                                               ETH_ALEN);
+                               err = mesh_nexthop_lookup(fwd_skb, sdata);
+                               /* Failed to immediately resolve next hop:
+                                * fwded frame was dropped or will be added
+                                * later to the pending skb queue.  */
+                               if (err)
+                                       return RX_DROP_MONITOR;
+
+                               IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh,
+                                                               fwded_unicast);
+                       }
+                       IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh,
+                                                    fwded_frames);
+                       ieee80211_add_pending_skb(local, fwd_skb);
                }
        }
 
-       if (is_multicast_ether_addr(hdr->addr3) ||
+       if (is_multicast_ether_addr(hdr->addr1) ||
            rx->dev->flags & IFF_PROMISC)
                return RX_CONTINUE;
        else
@@ -1620,7 +1649,7 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx)
                /* manage reordering buffer according to requested */
                /* sequence number */
                rcu_read_lock();
-               ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, NULL, NULL,
+               ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, NULL,
                                                 start_seq_num, 1);
                rcu_read_unlock();
                return RX_DROP_UNUSABLE;
@@ -1644,12 +1673,7 @@ static void ieee80211_process_sa_query_req(struct ieee80211_sub_if_data *sdata,
 
        if (compare_ether_addr(mgmt->sa, sdata->u.mgd.bssid) != 0 ||
            compare_ether_addr(mgmt->bssid, sdata->u.mgd.bssid) != 0) {
-               /* Not from the current AP. */
-               return;
-       }
-
-       if (sdata->u.mgd.state == IEEE80211_STA_MLME_ASSOCIATE) {
-               /* Association in progress; ignore SA Query */
+               /* Not from the current AP or not associated yet. */
                return;
        }
 
@@ -1686,7 +1710,6 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
        struct ieee80211_local *local = rx->local;
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
        struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
-       struct ieee80211_bss *bss;
        int len = rx->skb->len;
 
        if (!ieee80211_is_action(mgmt->frame_control))
@@ -1764,17 +1787,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                        if (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN))
                                return RX_DROP_MONITOR;
 
-                       bss = ieee80211_rx_bss_get(local, sdata->u.mgd.bssid,
-                                          local->hw.conf.channel->center_freq,
-                                          sdata->u.mgd.ssid,
-                                          sdata->u.mgd.ssid_len);
-                       if (!bss)
-                               return RX_DROP_MONITOR;
-
-                       ieee80211_sta_process_chanswitch(sdata,
-                                    &mgmt->u.action.u.chan_switch.sw_elem, bss);
-                       ieee80211_rx_bss_put(local, bss);
-                       break;
+                       return ieee80211_sta_rx_mgmt(sdata, rx->skb);
                }
                break;
        case WLAN_CATEGORY_SA_QUERY:
@@ -1817,19 +1830,18 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
                return RX_DROP_MONITOR;
 
        if (ieee80211_vif_is_mesh(&sdata->vif))
-               return ieee80211_mesh_rx_mgmt(sdata, rx->skb, rx->status);
+               return ieee80211_mesh_rx_mgmt(sdata, rx->skb);
 
        if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
-               return ieee80211_ibss_rx_mgmt(sdata, rx->skb, rx->status);
+               return ieee80211_ibss_rx_mgmt(sdata, rx->skb);
 
        if (sdata->vif.type == NL80211_IFTYPE_STATION)
-               return ieee80211_sta_rx_mgmt(sdata, rx->skb, rx->status);
+               return ieee80211_sta_rx_mgmt(sdata, rx->skb);
 
        return RX_DROP_MONITOR;
 }
 
-static void ieee80211_rx_michael_mic_report(struct net_device *dev,
-                                           struct ieee80211_hdr *hdr,
+static void ieee80211_rx_michael_mic_report(struct ieee80211_hdr *hdr,
                                            struct ieee80211_rx_data *rx)
 {
        int keyidx;
@@ -1866,7 +1878,8 @@ static void ieee80211_rx_michael_mic_report(struct net_device *dev,
            !ieee80211_is_auth(hdr->frame_control))
                goto ignore;
 
-       mac80211_ev_michael_mic_failure(rx->sdata, keyidx, hdr, NULL);
+       mac80211_ev_michael_mic_failure(rx->sdata, keyidx, hdr, NULL,
+                                       GFP_ATOMIC);
  ignore:
        dev_kfree_skb(rx->skb);
        rx->skb = NULL;
@@ -2028,13 +2041,8 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
        case NL80211_IFTYPE_STATION:
                if (!bssid)
                        return 0;
-               if (!ieee80211_bssid_match(bssid, sdata->u.mgd.bssid)) {
-                       if (!(rx->flags & IEEE80211_RX_IN_SCAN))
-                               return 0;
-                       rx->flags &= ~IEEE80211_RX_RA_MATCH;
-               } else if (!multicast &&
-                          compare_ether_addr(sdata->dev->dev_addr,
-                                             hdr->addr1) != 0) {
+               if (!multicast &&
+                   compare_ether_addr(sdata->dev->dev_addr, hdr->addr1) != 0) {
                        if (!(sdata->dev->flags & IFF_PROMISC))
                                return 0;
                        rx->flags &= ~IEEE80211_RX_RA_MATCH;
@@ -2114,9 +2122,9 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
  */
 static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
                                         struct sk_buff *skb,
-                                        struct ieee80211_rx_status *status,
                                         struct ieee80211_rate *rate)
 {
+       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_sub_if_data *sdata;
        struct ieee80211_hdr *hdr;
@@ -2143,11 +2151,12 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
        }
 
        if ((status->flag & RX_FLAG_MMIC_ERROR)) {
-               ieee80211_rx_michael_mic_report(local->mdev, hdr, &rx);
+               ieee80211_rx_michael_mic_report(hdr, &rx);
                return;
        }
 
-       if (unlikely(local->sw_scanning || local->hw_scanning))
+       if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning) ||
+                    test_bit(SCAN_OFF_CHANNEL, &local->scanning)))
                rx.flags |= IEEE80211_RX_IN_SCAN;
 
        ieee80211_parse_qos(&rx);
@@ -2227,20 +2236,21 @@ static void ieee80211_release_reorder_frame(struct ieee80211_hw *hw,
 {
        struct ieee80211_supported_band *sband;
        struct ieee80211_rate *rate;
-       struct ieee80211_rx_status status;
+       struct sk_buff *skb = tid_agg_rx->reorder_buf[index];
+       struct ieee80211_rx_status *status;
 
-       if (!tid_agg_rx->reorder_buf[index])
+       if (!skb)
                goto no_frame;
 
+       status = IEEE80211_SKB_RXCB(skb);
+
        /* release the reordered frames to stack */
-       memcpy(&status, tid_agg_rx->reorder_buf[index]->cb, sizeof(status));
-       sband = hw->wiphy->bands[status.band];
-       if (status.flag & RX_FLAG_HT)
+       sband = hw->wiphy->bands[status->band];
+       if (status->flag & RX_FLAG_HT)
                rate = sband->bitrates; /* TODO: HT rates */
        else
-               rate = &sband->bitrates[status.rate_idx];
-       __ieee80211_rx_handle_packet(hw, tid_agg_rx->reorder_buf[index],
-                                    &status, rate);
+               rate = &sband->bitrates[status->rate_idx];
+       __ieee80211_rx_handle_packet(hw, skb, rate);
        tid_agg_rx->stored_mpdu_num--;
        tid_agg_rx->reorder_buf[index] = NULL;
 
@@ -2265,7 +2275,6 @@ no_frame:
 static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
                                           struct tid_ampdu_rx *tid_agg_rx,
                                           struct sk_buff *skb,
-                                          struct ieee80211_rx_status *rxstatus,
                                           u16 mpdu_seq_num,
                                           int bar_req)
 {
@@ -2324,8 +2333,6 @@ static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
        /* put the frame in the reordering buffer */
        tid_agg_rx->reorder_buf[index] = skb;
        tid_agg_rx->reorder_time[index] = jiffies;
-       memcpy(tid_agg_rx->reorder_buf[index]->cb, rxstatus,
-              sizeof(*rxstatus));
        tid_agg_rx->stored_mpdu_num++;
        /* release the buffer until next missing frame */
        index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn)
@@ -2374,8 +2381,7 @@ static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
 }
 
 static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local,
-                                    struct sk_buff *skb,
-                                    struct ieee80211_rx_status *status)
+                                    struct sk_buff *skb)
 {
        struct ieee80211_hw *hw = &local->hw;
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
@@ -2424,7 +2430,7 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local,
 
        /* according to mpdu sequence number deal with reordering buffer */
        mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4;
-       ret = ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, status,
+       ret = ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb,
                                                mpdu_seq_num, 0);
  end_reorder:
        return ret;
@@ -2434,12 +2440,12 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local,
  * This is the receive path handler. It is called by a low level driver when an
  * 802.11 MPDU is received from the hardware.
  */
-void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
-                   struct ieee80211_rx_status *status)
+void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_rate *rate = NULL;
        struct ieee80211_supported_band *sband;
+       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
 
        if (status->band < 0 ||
            status->band >= IEEE80211_NUM_BANDS) {
@@ -2453,6 +2459,18 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
                return;
        }
 
+       /*
+        * If we're suspending, it is possible although not too likely
+        * that we'd be receiving frames after having already partially
+        * quiesced the stack. We can't process such frames then since
+        * that might, for example, cause stations to be added or other
+        * driver callbacks be invoked.
+        */
+       if (unlikely(local->quiescing || local->suspended)) {
+               kfree_skb(skb);
+               return;
+       }
+
        if (status->flag & RX_FLAG_HT) {
                /* rate_idx is MCS index */
                if (WARN_ON(status->rate_idx < 0 ||
@@ -2482,7 +2500,7 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
         * if it was previously present.
         * Also, frames with less than 16 bytes are dropped.
         */
-       skb = ieee80211_rx_monitor(local, skb, status, rate);
+       skb = ieee80211_rx_monitor(local, skb, rate);
        if (!skb) {
                rcu_read_unlock();
                return;
@@ -2500,8 +2518,8 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
         * frames from other than operational channel), but that should not
         * happen in normal networks.
         */
-       if (!ieee80211_rx_reorder_ampdu(local, skb, status))
-               __ieee80211_rx_handle_packet(hw, skb, status, rate);
+       if (!ieee80211_rx_reorder_ampdu(local, skb))
+               __ieee80211_rx_handle_packet(hw, skb, rate);
 
        rcu_read_unlock();
 }
@@ -2509,16 +2527,12 @@ EXPORT_SYMBOL(__ieee80211_rx);
 
 /* This is a version of the rx handler that can be called from hard irq
  * context. Post the skb on the queue and schedule the tasklet */
-void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb,
-                         struct ieee80211_rx_status *status)
+void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
        struct ieee80211_local *local = hw_to_local(hw);
 
        BUILD_BUG_ON(sizeof(struct ieee80211_rx_status) > sizeof(skb->cb));
 
-       skb->dev = local->mdev;
-       /* copy status into skb->cb for use by tasklet */
-       memcpy(skb->cb, status, sizeof(*status));
        skb->pkt_type = IEEE80211_RX_MSG;
        skb_queue_tail(&local->skb_queue, skb);
        tasklet_schedule(&local->tasklet);