Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[sfrench/cifs-2.6.git] / net / mac80211 / scan.c
index 267b2940faddd9cfcea725926d3dd5f507ee45dd..bcaee5d1283915efdbc8c63f4365c7ed42d486dc 100644 (file)
@@ -83,13 +83,14 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
 
        cbss = cfg80211_inform_bss_frame(local->hw.wiphy, channel,
                                         mgmt, len, signal, GFP_ATOMIC);
-
        if (!cbss)
                return NULL;
 
        cbss->free_priv = ieee80211_rx_bss_free;
        bss = (void *)cbss->priv;
 
+       bss->device_ts = rx_status->device_timestamp;
+
        if (elems->parse_error) {
                if (beacon)
                        bss->corrupt_data |= IEEE80211_BSS_CORRUPT_BEACON;
@@ -164,52 +165,47 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
        return bss;
 }
 
-ieee80211_rx_result
-ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
+void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
 {
        struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
-       struct ieee80211_mgmt *mgmt;
+       struct ieee80211_sub_if_data *sdata1, *sdata2;
+       struct ieee80211_mgmt *mgmt = (void *)skb->data;
        struct ieee80211_bss *bss;
        u8 *elements;
        struct ieee80211_channel *channel;
        size_t baselen;
        int freq;
-       __le16 fc;
-       bool presp, beacon = false;
+       bool beacon;
        struct ieee802_11_elems elems;
 
-       if (skb->len < 2)
-               return RX_DROP_UNUSABLE;
-
-       mgmt = (struct ieee80211_mgmt *) skb->data;
-       fc = mgmt->frame_control;
+       if (skb->len < 24 ||
+           (!ieee80211_is_probe_resp(mgmt->frame_control) &&
+            !ieee80211_is_beacon(mgmt->frame_control)))
+               return;
 
-       if (ieee80211_is_ctl(fc))
-               return RX_CONTINUE;
+       sdata1 = rcu_dereference(local->scan_sdata);
+       sdata2 = rcu_dereference(local->sched_scan_sdata);
 
-       if (skb->len < 24)
-               return RX_CONTINUE;
+       if (likely(!sdata1 && !sdata2))
+               return;
 
-       presp = ieee80211_is_probe_resp(fc);
-       if (presp) {
+       if (ieee80211_is_probe_resp(mgmt->frame_control)) {
                /* ignore ProbeResp to foreign address */
-               if (!ether_addr_equal(mgmt->da, sdata->vif.addr))
-                       return RX_DROP_MONITOR;
+               if ((!sdata1 || !ether_addr_equal(mgmt->da, sdata1->vif.addr)) &&
+                   (!sdata2 || !ether_addr_equal(mgmt->da, sdata2->vif.addr)))
+                       return;
 
-               presp = true;
                elements = mgmt->u.probe_resp.variable;
                baselen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+               beacon = false;
        } else {
-               beacon = ieee80211_is_beacon(fc);
                baselen = offsetof(struct ieee80211_mgmt, u.beacon.variable);
                elements = mgmt->u.beacon.variable;
+               beacon = true;
        }
 
-       if (!presp && !beacon)
-               return RX_CONTINUE;
-
        if (baselen > skb->len)
-               return RX_DROP_MONITOR;
+               return;
 
        ieee802_11_parse_elems(elements, skb->len - baselen, &elems);
 
@@ -219,22 +215,16 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
        else
                freq = rx_status->freq;
 
-       channel = ieee80211_get_channel(sdata->local->hw.wiphy, freq);
+       channel = ieee80211_get_channel(local->hw.wiphy, freq);
 
        if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
-               return RX_DROP_MONITOR;
+               return;
 
-       bss = ieee80211_bss_info_update(sdata->local, rx_status,
+       bss = ieee80211_bss_info_update(local, rx_status,
                                        mgmt, skb->len, &elems,
                                        channel, beacon);
        if (bss)
-               ieee80211_rx_bss_put(sdata->local, bss);
-
-       if (channel == sdata->local->oper_channel)
-               return RX_CONTINUE;
-
-       dev_kfree_skb(skb);
-       return RX_QUEUED;
+               ieee80211_rx_bss_put(local, bss);
 }
 
 /* return false if no more work */
@@ -292,7 +282,13 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
                return;
 
        if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) {
-               int rc = drv_hw_scan(local, local->scan_sdata, local->hw_scan_req);
+               int rc;
+
+               rc = drv_hw_scan(local,
+                       rcu_dereference_protected(local->scan_sdata,
+                                                 lockdep_is_held(&local->mtx)),
+                       local->hw_scan_req);
+
                if (rc == 0)
                        return;
        }
@@ -393,7 +389,10 @@ void ieee80211_run_deferred_scan(struct ieee80211_local *local)
        if (!local->scan_req || local->scanning)
                return;
 
-       if (!ieee80211_can_scan(local, local->scan_sdata))
+       if (!ieee80211_can_scan(local,
+                               rcu_dereference_protected(
+                                       local->scan_sdata,
+                                       lockdep_is_held(&local->mtx))))
                return;
 
        ieee80211_queue_delayed_work(&local->hw, &local->scan_work,
@@ -404,9 +403,12 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
                                            unsigned long *next_delay)
 {
        int i;
-       struct ieee80211_sub_if_data *sdata = local->scan_sdata;
+       struct ieee80211_sub_if_data *sdata;
        enum ieee80211_band band = local->hw.conf.channel->band;
 
+       sdata = rcu_dereference_protected(local->scan_sdata,
+                                         lockdep_is_held(&local->mtx));;
+
        for (i = 0; i < local->scan_req->n_ssids; i++)
                ieee80211_send_probe_req(
                        sdata, NULL,
@@ -438,7 +440,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
        if (!ieee80211_can_scan(local, sdata)) {
                /* wait for the work to finish/time out */
                local->scan_req = req;
-               local->scan_sdata = sdata;
+               rcu_assign_pointer(local->scan_sdata, sdata);
                return 0;
        }
 
@@ -472,7 +474,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
        }
 
        local->scan_req = req;
-       local->scan_sdata = sdata;
+       rcu_assign_pointer(local->scan_sdata, sdata);
 
        if (local->ops->hw_scan) {
                __set_bit(SCAN_HW_SCANNING, &local->scanning);
@@ -532,7 +534,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
                ieee80211_recalc_idle(local);
 
                local->scan_req = NULL;
-               local->scan_sdata = NULL;
+               rcu_assign_pointer(local->scan_sdata, NULL);
        }
 
        return rc;
@@ -719,7 +721,8 @@ void ieee80211_scan_work(struct work_struct *work)
 
        mutex_lock(&local->mtx);
 
-       sdata = local->scan_sdata;
+       sdata = rcu_dereference_protected(local->scan_sdata,
+                                         lockdep_is_held(&local->mtx));
 
        /* When scanning on-channel, the first-callback means completed. */
        if (test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning)) {
@@ -740,7 +743,7 @@ void ieee80211_scan_work(struct work_struct *work)
                int rc;
 
                local->scan_req = NULL;
-               local->scan_sdata = NULL;
+               rcu_assign_pointer(local->scan_sdata, NULL);
 
                rc = __ieee80211_start_scan(sdata, req);
                if (rc) {
@@ -892,7 +895,9 @@ void ieee80211_scan_cancel(struct ieee80211_local *local)
 
        if (test_bit(SCAN_HW_SCANNING, &local->scanning)) {
                if (local->ops->cancel_hw_scan)
-                       drv_cancel_hw_scan(local, local->scan_sdata);
+                       drv_cancel_hw_scan(local,
+                               rcu_dereference_protected(local->scan_sdata,
+                                               lockdep_is_held(&local->mtx)));
                goto out;
        }
 
@@ -914,9 +919,9 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_local *local = sdata->local;
        int ret, i;
 
-       mutex_lock(&sdata->local->mtx);
+       mutex_lock(&local->mtx);
 
-       if (local->sched_scanning) {
+       if (rcu_access_pointer(local->sched_scan_sdata)) {
                ret = -EBUSY;
                goto out;
        }
@@ -927,6 +932,9 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
        }
 
        for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
+               if (!local->hw.wiphy->bands[i])
+                       continue;
+
                local->sched_scan_ies.ie[i] = kzalloc(2 +
                                                      IEEE80211_MAX_SSID_LEN +
                                                      local->scan_ies_len +
@@ -947,7 +955,7 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
        ret = drv_sched_scan_start(local, sdata, req,
                                   &local->sched_scan_ies);
        if (ret == 0) {
-               local->sched_scanning = true;
+               rcu_assign_pointer(local->sched_scan_sdata, sdata);
                goto out;
        }
 
@@ -955,7 +963,7 @@ out_free:
        while (i > 0)
                kfree(local->sched_scan_ies.ie[--i]);
 out:
-       mutex_unlock(&sdata->local->mtx);
+       mutex_unlock(&local->mtx);
        return ret;
 }
 
@@ -964,22 +972,22 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_local *local = sdata->local;
        int ret = 0, i;
 
-       mutex_lock(&sdata->local->mtx);
+       mutex_lock(&local->mtx);
 
        if (!local->ops->sched_scan_stop) {
                ret = -ENOTSUPP;
                goto out;
        }
 
-       if (local->sched_scanning) {
+       if (rcu_access_pointer(local->sched_scan_sdata)) {
                for (i = 0; i < IEEE80211_NUM_BANDS; i++)
                        kfree(local->sched_scan_ies.ie[i]);
 
                drv_sched_scan_stop(local, sdata);
-               local->sched_scanning = false;
+               rcu_assign_pointer(local->sched_scan_sdata, NULL);
        }
 out:
-       mutex_unlock(&sdata->local->mtx);
+       mutex_unlock(&local->mtx);
 
        return ret;
 }
@@ -1003,7 +1011,7 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work)
 
        mutex_lock(&local->mtx);
 
-       if (!local->sched_scanning) {
+       if (!rcu_access_pointer(local->sched_scan_sdata)) {
                mutex_unlock(&local->mtx);
                return;
        }
@@ -1011,7 +1019,7 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work)
        for (i = 0; i < IEEE80211_NUM_BANDS; i++)
                kfree(local->sched_scan_ies.ie[i]);
 
-       local->sched_scanning = false;
+       rcu_assign_pointer(local->sched_scan_sdata, NULL);
 
        mutex_unlock(&local->mtx);