mac80211: make scan_sdata pointer usable with RCU
authorJohannes Berg <johannes.berg@intel.com>
Fri, 6 Jul 2012 19:39:28 +0000 (21:39 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Thu, 12 Jul 2012 10:10:43 +0000 (12:10 +0200)
Making the scan_sdata pointer usable with RCU makes
it possible to dereference it in the RX path to see
if a received frame actually matches the interface
that is scanning. This is just preparations, making
the pointer __rcu.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/scan.c

index c3241c3ec6d1e52b09f46cafec7376d8bd8264ff..9f2534a41243dee57e05a9ae429d3a8d5cc651c2 100644 (file)
@@ -972,7 +972,7 @@ struct ieee80211_local {
        unsigned long leave_oper_channel_time;
        enum mac80211_scan_state next_scan_state;
        struct delayed_work scan_work;
-       struct ieee80211_sub_if_data *scan_sdata;
+       struct ieee80211_sub_if_data __rcu *scan_sdata;
        enum nl80211_channel_type _oper_channel_type;
        struct ieee80211_channel *oper_channel, *csa_channel;
 
index b1edf60fbba70e50b6dfbdac897ac61e6c137a54..e3c49748ce8fc9c4ec3939d3cfe1e720bbe952b1 100644 (file)
@@ -112,10 +112,11 @@ static u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
                }
        }
 
-       if (local->scan_sdata &&
-           !(local->hw.flags & IEEE80211_HW_SCAN_WHILE_IDLE)) {
+       sdata = rcu_dereference_protected(local->scan_sdata,
+                                         lockdep_is_held(&local->mtx));
+       if (sdata && !(local->hw.flags & IEEE80211_HW_SCAN_WHILE_IDLE)) {
                scanning = true;
-               local->scan_sdata->vif.bss_conf.idle = false;
+               sdata->vif.bss_conf.idle = false;
        }
 
        list_for_each_entry(sdata, &local->interfaces, list) {
@@ -628,7 +629,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
 
        clear_bit(SDATA_STATE_RUNNING, &sdata->state);
 
-       if (local->scan_sdata == sdata)
+       if (rcu_access_pointer(local->scan_sdata) == sdata)
                ieee80211_scan_cancel(local);
 
        /*
index 1ff04f689d1fdd47c9df09b6956b4bb9f644fdb0..704dcf847761f3ed7c942d3bc7ce5b25908ce20b 100644 (file)
@@ -293,7 +293,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;
        }
@@ -394,7 +400,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,
@@ -405,9 +414,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,
@@ -439,7 +451,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;
        }
 
@@ -473,7 +485,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);
@@ -533,7 +545,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;
@@ -720,7 +732,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)) {
@@ -741,7 +754,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) {
@@ -893,7 +906,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;
        }