Commit 6ea0a69c authored by Johannes Berg's avatar Johannes Berg

mac80211: rcu-ify scan and scheduled scan request pointers

In order to use the scan and scheduled scan request pointers during
RX to check for randomisation, make them accessible using RCU.
Reviewed-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent ad2b26ab
...@@ -1238,7 +1238,7 @@ struct ieee80211_local { ...@@ -1238,7 +1238,7 @@ struct ieee80211_local {
unsigned long scanning; unsigned long scanning;
struct cfg80211_ssid scan_ssid; struct cfg80211_ssid scan_ssid;
struct cfg80211_scan_request *int_scan_req; struct cfg80211_scan_request *int_scan_req;
struct cfg80211_scan_request *scan_req; struct cfg80211_scan_request __rcu *scan_req;
struct ieee80211_scan_request *hw_scan_req; struct ieee80211_scan_request *hw_scan_req;
struct cfg80211_chan_def scan_chandef; struct cfg80211_chan_def scan_chandef;
enum ieee80211_band hw_scan_band; enum ieee80211_band hw_scan_band;
...@@ -1248,7 +1248,7 @@ struct ieee80211_local { ...@@ -1248,7 +1248,7 @@ struct ieee80211_local {
struct work_struct sched_scan_stopped_work; struct work_struct sched_scan_stopped_work;
struct ieee80211_sub_if_data __rcu *sched_scan_sdata; struct ieee80211_sub_if_data __rcu *sched_scan_sdata;
struct cfg80211_sched_scan_request *sched_scan_req; struct cfg80211_sched_scan_request __rcu *sched_scan_req;
unsigned long leave_oper_channel_time; unsigned long leave_oper_channel_time;
enum mac80211_scan_state next_scan_state; enum mac80211_scan_state next_scan_state;
......
...@@ -234,11 +234,14 @@ ieee80211_prepare_scan_chandef(struct cfg80211_chan_def *chandef, ...@@ -234,11 +234,14 @@ ieee80211_prepare_scan_chandef(struct cfg80211_chan_def *chandef,
/* return false if no more work */ /* return false if no more work */
static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
{ {
struct cfg80211_scan_request *req = local->scan_req; struct cfg80211_scan_request *req;
struct cfg80211_chan_def chandef; struct cfg80211_chan_def chandef;
u8 bands_used = 0; u8 bands_used = 0;
int i, ielen, n_chans; int i, ielen, n_chans;
req = rcu_dereference_protected(local->scan_req,
lockdep_is_held(&local->mtx));
if (test_bit(SCAN_HW_CANCELLED, &local->scanning)) if (test_bit(SCAN_HW_CANCELLED, &local->scanning))
return false; return false;
...@@ -290,6 +293,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) ...@@ -290,6 +293,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_local *local = hw_to_local(hw);
bool hw_scan = local->ops->hw_scan; bool hw_scan = local->ops->hw_scan;
bool was_scanning = local->scanning; bool was_scanning = local->scanning;
struct cfg80211_scan_request *scan_req;
lockdep_assert_held(&local->mtx); lockdep_assert_held(&local->mtx);
...@@ -322,9 +326,12 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) ...@@ -322,9 +326,12 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
kfree(local->hw_scan_req); kfree(local->hw_scan_req);
local->hw_scan_req = NULL; local->hw_scan_req = NULL;
if (local->scan_req != local->int_scan_req) scan_req = rcu_dereference_protected(local->scan_req,
cfg80211_scan_done(local->scan_req, aborted); lockdep_is_held(&local->mtx));
local->scan_req = NULL;
if (scan_req != local->int_scan_req)
cfg80211_scan_done(scan_req, aborted);
RCU_INIT_POINTER(local->scan_req, NULL);
RCU_INIT_POINTER(local->scan_sdata, NULL); RCU_INIT_POINTER(local->scan_sdata, NULL);
local->scanning = 0; local->scanning = 0;
...@@ -440,23 +447,26 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, ...@@ -440,23 +447,26 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
{ {
int i; int i;
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
struct cfg80211_scan_request *scan_req;
enum ieee80211_band band = local->hw.conf.chandef.chan->band; enum ieee80211_band band = local->hw.conf.chandef.chan->band;
u32 tx_flags; u32 tx_flags;
scan_req = rcu_dereference_protected(local->scan_req,
lockdep_is_held(&local->mtx));
tx_flags = IEEE80211_TX_INTFL_OFFCHAN_TX_OK; tx_flags = IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
if (local->scan_req->no_cck) if (scan_req->no_cck)
tx_flags |= IEEE80211_TX_CTL_NO_CCK_RATE; tx_flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
sdata = rcu_dereference_protected(local->scan_sdata, sdata = rcu_dereference_protected(local->scan_sdata,
lockdep_is_held(&local->mtx)); lockdep_is_held(&local->mtx));
for (i = 0; i < local->scan_req->n_ssids; i++) for (i = 0; i < scan_req->n_ssids; i++)
ieee80211_send_probe_req( ieee80211_send_probe_req(
sdata, NULL, sdata, NULL,
local->scan_req->ssids[i].ssid, scan_req->ssids[i].ssid, scan_req->ssids[i].ssid_len,
local->scan_req->ssids[i].ssid_len, scan_req->ie, scan_req->ie_len,
local->scan_req->ie, local->scan_req->ie_len, scan_req->rates[band], false,
local->scan_req->rates[band], false,
tx_flags, local->hw.conf.chandef.chan, true); tx_flags, local->hw.conf.chandef.chan, true);
/* /*
...@@ -480,7 +490,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, ...@@ -480,7 +490,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
if (!ieee80211_can_scan(local, sdata)) { if (!ieee80211_can_scan(local, sdata)) {
/* wait for the work to finish/time out */ /* wait for the work to finish/time out */
local->scan_req = req; rcu_assign_pointer(local->scan_req, req);
rcu_assign_pointer(local->scan_sdata, sdata); rcu_assign_pointer(local->scan_sdata, sdata);
return 0; return 0;
} }
...@@ -530,7 +540,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, ...@@ -530,7 +540,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
*/ */
} }
local->scan_req = req; rcu_assign_pointer(local->scan_req, req);
rcu_assign_pointer(local->scan_sdata, sdata); rcu_assign_pointer(local->scan_sdata, sdata);
if (local->ops->hw_scan) { if (local->ops->hw_scan) {
...@@ -558,7 +568,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, ...@@ -558,7 +568,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
if ((req->channels[0]->flags & if ((req->channels[0]->flags &
IEEE80211_CHAN_NO_IR) || IEEE80211_CHAN_NO_IR) ||
!local->scan_req->n_ssids) { !req->n_ssids) {
next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
} else { } else {
ieee80211_scan_state_send_probe(local, &next_delay); ieee80211_scan_state_send_probe(local, &next_delay);
...@@ -617,6 +627,7 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, ...@@ -617,6 +627,7 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
struct ieee80211_channel *next_chan; struct ieee80211_channel *next_chan;
enum mac80211_scan_state next_scan_state; enum mac80211_scan_state next_scan_state;
struct cfg80211_scan_request *scan_req;
/* /*
* check if at least one STA interface is associated, * check if at least one STA interface is associated,
...@@ -641,7 +652,10 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, ...@@ -641,7 +652,10 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
} }
mutex_unlock(&local->iflist_mtx); mutex_unlock(&local->iflist_mtx);
next_chan = local->scan_req->channels[local->scan_channel_idx]; scan_req = rcu_dereference_protected(local->scan_req,
lockdep_is_held(&local->mtx));
next_chan = scan_req->channels[local->scan_channel_idx];
/* /*
* we're currently scanning a different channel, let's * we're currently scanning a different channel, let's
...@@ -656,7 +670,7 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, ...@@ -656,7 +670,7 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
local->leave_oper_channel_time + HZ / 8); local->leave_oper_channel_time + HZ / 8);
if (associated && !tx_empty) { if (associated && !tx_empty) {
if (local->scan_req->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) if (scan_req->flags & NL80211_SCAN_FLAG_LOW_PRIORITY)
next_scan_state = SCAN_ABORT; next_scan_state = SCAN_ABORT;
else else
next_scan_state = SCAN_SUSPEND; next_scan_state = SCAN_SUSPEND;
...@@ -677,14 +691,18 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, ...@@ -677,14 +691,18 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
int skip; int skip;
struct ieee80211_channel *chan; struct ieee80211_channel *chan;
enum nl80211_bss_scan_width oper_scan_width; enum nl80211_bss_scan_width oper_scan_width;
struct cfg80211_scan_request *scan_req;
scan_req = rcu_dereference_protected(local->scan_req,
lockdep_is_held(&local->mtx));
skip = 0; skip = 0;
chan = local->scan_req->channels[local->scan_channel_idx]; chan = scan_req->channels[local->scan_channel_idx];
local->scan_chandef.chan = chan; local->scan_chandef.chan = chan;
local->scan_chandef.center_freq1 = chan->center_freq; local->scan_chandef.center_freq1 = chan->center_freq;
local->scan_chandef.center_freq2 = 0; local->scan_chandef.center_freq2 = 0;
switch (local->scan_req->scan_width) { switch (scan_req->scan_width) {
case NL80211_BSS_CHAN_WIDTH_5: case NL80211_BSS_CHAN_WIDTH_5:
local->scan_chandef.width = NL80211_CHAN_WIDTH_5; local->scan_chandef.width = NL80211_CHAN_WIDTH_5;
break; break;
...@@ -698,7 +716,7 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, ...@@ -698,7 +716,7 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
oper_scan_width = cfg80211_chandef_to_scan_width( oper_scan_width = cfg80211_chandef_to_scan_width(
&local->_oper_chandef); &local->_oper_chandef);
if (chan == local->_oper_chandef.chan && if (chan == local->_oper_chandef.chan &&
oper_scan_width == local->scan_req->scan_width) oper_scan_width == scan_req->scan_width)
local->scan_chandef = local->_oper_chandef; local->scan_chandef = local->_oper_chandef;
else else
local->scan_chandef.width = NL80211_CHAN_WIDTH_20_NOHT; local->scan_chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
...@@ -727,8 +745,7 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, ...@@ -727,8 +745,7 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
* *
* In any case, it is not necessary for a passive scan. * In any case, it is not necessary for a passive scan.
*/ */
if (chan->flags & IEEE80211_CHAN_NO_IR || if (chan->flags & IEEE80211_CHAN_NO_IR || !scan_req->n_ssids) {
!local->scan_req->n_ssids) {
*next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; *next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
local->next_scan_state = SCAN_DECISION; local->next_scan_state = SCAN_DECISION;
return; return;
...@@ -777,6 +794,7 @@ void ieee80211_scan_work(struct work_struct *work) ...@@ -777,6 +794,7 @@ void ieee80211_scan_work(struct work_struct *work)
struct ieee80211_local *local = struct ieee80211_local *local =
container_of(work, struct ieee80211_local, scan_work.work); container_of(work, struct ieee80211_local, scan_work.work);
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
struct cfg80211_scan_request *scan_req;
unsigned long next_delay = 0; unsigned long next_delay = 0;
bool aborted; bool aborted;
...@@ -784,6 +802,8 @@ void ieee80211_scan_work(struct work_struct *work) ...@@ -784,6 +802,8 @@ void ieee80211_scan_work(struct work_struct *work)
sdata = rcu_dereference_protected(local->scan_sdata, sdata = rcu_dereference_protected(local->scan_sdata,
lockdep_is_held(&local->mtx)); lockdep_is_held(&local->mtx));
scan_req = rcu_dereference_protected(local->scan_req,
lockdep_is_held(&local->mtx));
/* When scanning on-channel, the first-callback means completed. */ /* When scanning on-channel, the first-callback means completed. */
if (test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning)) { if (test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning)) {
...@@ -796,20 +816,19 @@ void ieee80211_scan_work(struct work_struct *work) ...@@ -796,20 +816,19 @@ void ieee80211_scan_work(struct work_struct *work)
goto out_complete; goto out_complete;
} }
if (!sdata || !local->scan_req) if (!sdata || !scan_req)
goto out; goto out;
if (!local->scanning) { if (!local->scanning) {
struct cfg80211_scan_request *req = local->scan_req;
int rc; int rc;
local->scan_req = NULL; RCU_INIT_POINTER(local->scan_req, NULL);
RCU_INIT_POINTER(local->scan_sdata, NULL); RCU_INIT_POINTER(local->scan_sdata, NULL);
rc = __ieee80211_start_scan(sdata, req); rc = __ieee80211_start_scan(sdata, scan_req);
if (rc) { if (rc) {
/* need to complete scan in cfg80211 */ /* need to complete scan in cfg80211 */
local->scan_req = req; rcu_assign_pointer(local->scan_req, scan_req);
aborted = true; aborted = true;
goto out_complete; goto out_complete;
} else } else
...@@ -829,7 +848,7 @@ void ieee80211_scan_work(struct work_struct *work) ...@@ -829,7 +848,7 @@ void ieee80211_scan_work(struct work_struct *work)
switch (local->next_scan_state) { switch (local->next_scan_state) {
case SCAN_DECISION: case SCAN_DECISION:
/* if no more bands/channels left, complete scan */ /* if no more bands/channels left, complete scan */
if (local->scan_channel_idx >= local->scan_req->n_channels) { if (local->scan_channel_idx >= scan_req->n_channels) {
aborted = false; aborted = false;
goto out_complete; goto out_complete;
} }
...@@ -1043,7 +1062,7 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, ...@@ -1043,7 +1062,7 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies); ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
if (ret == 0) { if (ret == 0) {
rcu_assign_pointer(local->sched_scan_sdata, sdata); rcu_assign_pointer(local->sched_scan_sdata, sdata);
local->sched_scan_req = req; rcu_assign_pointer(local->sched_scan_req, req);
} }
kfree(ie); kfree(ie);
...@@ -1052,7 +1071,7 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, ...@@ -1052,7 +1071,7 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
if (ret) { if (ret) {
/* Clean in case of failure after HW restart or upon resume. */ /* Clean in case of failure after HW restart or upon resume. */
RCU_INIT_POINTER(local->sched_scan_sdata, NULL); RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
local->sched_scan_req = NULL; RCU_INIT_POINTER(local->sched_scan_req, NULL);
} }
return ret; return ret;
...@@ -1090,7 +1109,7 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata) ...@@ -1090,7 +1109,7 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
} }
/* We don't want to restart sched scan anymore. */ /* We don't want to restart sched scan anymore. */
local->sched_scan_req = NULL; RCU_INIT_POINTER(local->sched_scan_req, NULL);
if (rcu_access_pointer(local->sched_scan_sdata)) { if (rcu_access_pointer(local->sched_scan_sdata)) {
ret = drv_sched_scan_stop(local, sdata); ret = drv_sched_scan_stop(local, sdata);
...@@ -1125,7 +1144,7 @@ void ieee80211_sched_scan_end(struct ieee80211_local *local) ...@@ -1125,7 +1144,7 @@ void ieee80211_sched_scan_end(struct ieee80211_local *local)
RCU_INIT_POINTER(local->sched_scan_sdata, NULL); RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
/* If sched scan was aborted by the driver. */ /* If sched scan was aborted by the driver. */
local->sched_scan_req = NULL; RCU_INIT_POINTER(local->sched_scan_req, NULL);
mutex_unlock(&local->mtx); mutex_unlock(&local->mtx);
......
...@@ -1721,6 +1721,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) ...@@ -1721,6 +1721,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
int res, i; int res, i;
bool reconfig_due_to_wowlan = false; bool reconfig_due_to_wowlan = false;
struct ieee80211_sub_if_data *sched_scan_sdata; struct ieee80211_sub_if_data *sched_scan_sdata;
struct cfg80211_sched_scan_request *sched_scan_req;
bool sched_scan_stopped = false; bool sched_scan_stopped = false;
#ifdef CONFIG_PM #ifdef CONFIG_PM
...@@ -2011,13 +2012,15 @@ int ieee80211_reconfig(struct ieee80211_local *local) ...@@ -2011,13 +2012,15 @@ int ieee80211_reconfig(struct ieee80211_local *local)
mutex_lock(&local->mtx); mutex_lock(&local->mtx);
sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata, sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata,
lockdep_is_held(&local->mtx)); lockdep_is_held(&local->mtx));
if (sched_scan_sdata && local->sched_scan_req) sched_scan_req = rcu_dereference_protected(local->sched_scan_req,
lockdep_is_held(&local->mtx));
if (sched_scan_sdata && sched_scan_req)
/* /*
* Sched scan stopped, but we don't want to report it. Instead, * Sched scan stopped, but we don't want to report it. Instead,
* we're trying to reschedule. * we're trying to reschedule.
*/ */
if (__ieee80211_request_sched_scan_start(sched_scan_sdata, if (__ieee80211_request_sched_scan_start(sched_scan_sdata,
local->sched_scan_req)) sched_scan_req))
sched_scan_stopped = true; sched_scan_stopped = true;
mutex_unlock(&local->mtx); mutex_unlock(&local->mtx);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment