mac80211: make scan_sdata pointer usable with RCU

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>
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index c3241c3..9f2534a 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -972,7 +972,7 @@
 	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;
 
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index b1edf60..e3c4974 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -112,10 +112,11 @@
 		}
 	}
 
-	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 @@
 
 	clear_bit(SDATA_STATE_RUNNING, &sdata->state);
 
-	if (local->scan_sdata == sdata)
+	if (rcu_access_pointer(local->scan_sdata) == sdata)
 		ieee80211_scan_cancel(local);
 
 	/*
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 1ff04f6..704dcf8 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -293,7 +293,13 @@
 		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 @@
 	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 @@
 					    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 @@
 	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 @@
 	}
 
 	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 @@
 		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 @@
 
 	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 @@
 		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 @@
 
 	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;
 	}