Skip to content

Commit

Permalink
mac80211: rcu-ify scan and scheduled scan request pointers
Browse files Browse the repository at this point in the history
In order to use the scan and scheduled scan request pointers during
RX to check for randomisation, make them accessible using RCU.

Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
  • Loading branch information
Johannes Berg committed Nov 19, 2014
1 parent ad2b26a commit 6ea0a69
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 34 deletions.
4 changes: 2 additions & 2 deletions net/mac80211/ieee80211_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -1238,7 +1238,7 @@ struct ieee80211_local {
unsigned long scanning;
struct cfg80211_ssid scan_ssid;
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 cfg80211_chan_def scan_chandef;
enum ieee80211_band hw_scan_band;
Expand All @@ -1248,7 +1248,7 @@ struct ieee80211_local {

struct work_struct sched_scan_stopped_work;
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;
enum mac80211_scan_state next_scan_state;
Expand Down
79 changes: 49 additions & 30 deletions net/mac80211/scan.c
Original file line number Diff line number Diff line change
Expand Up @@ -234,11 +234,14 @@ ieee80211_prepare_scan_chandef(struct cfg80211_chan_def *chandef,
/* return false if no more work */
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;
u8 bands_used = 0;
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))
return false;

Expand Down Expand Up @@ -290,6 +293,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
struct ieee80211_local *local = hw_to_local(hw);
bool hw_scan = local->ops->hw_scan;
bool was_scanning = local->scanning;
struct cfg80211_scan_request *scan_req;

lockdep_assert_held(&local->mtx);

Expand Down Expand Up @@ -322,9 +326,12 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
kfree(local->hw_scan_req);
local->hw_scan_req = NULL;

if (local->scan_req != local->int_scan_req)
cfg80211_scan_done(local->scan_req, aborted);
local->scan_req = NULL;
scan_req = rcu_dereference_protected(local->scan_req,
lockdep_is_held(&local->mtx));

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);

local->scanning = 0;
Expand Down Expand Up @@ -440,23 +447,26 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
{
int i;
struct ieee80211_sub_if_data *sdata;
struct cfg80211_scan_request *scan_req;
enum ieee80211_band band = local->hw.conf.chandef.chan->band;
u32 tx_flags;

scan_req = rcu_dereference_protected(local->scan_req,
lockdep_is_held(&local->mtx));

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;

sdata = rcu_dereference_protected(local->scan_sdata,
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(
sdata, NULL,
local->scan_req->ssids[i].ssid,
local->scan_req->ssids[i].ssid_len,
local->scan_req->ie, local->scan_req->ie_len,
local->scan_req->rates[band], false,
scan_req->ssids[i].ssid, scan_req->ssids[i].ssid_len,
scan_req->ie, scan_req->ie_len,
scan_req->rates[band], false,
tx_flags, local->hw.conf.chandef.chan, true);

/*
Expand All @@ -480,7 +490,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;
rcu_assign_pointer(local->scan_req, req);
rcu_assign_pointer(local->scan_sdata, sdata);
return 0;
}
Expand Down Expand Up @@ -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);

if (local->ops->hw_scan) {
Expand Down Expand Up @@ -558,7 +568,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,

if ((req->channels[0]->flags &
IEEE80211_CHAN_NO_IR) ||
!local->scan_req->n_ssids) {
!req->n_ssids) {
next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
} else {
ieee80211_scan_state_send_probe(local, &next_delay);
Expand Down Expand Up @@ -617,6 +627,7 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata;
struct ieee80211_channel *next_chan;
enum mac80211_scan_state next_scan_state;
struct cfg80211_scan_request *scan_req;

/*
* check if at least one STA interface is associated,
Expand All @@ -641,7 +652,10 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
}
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
Expand All @@ -656,7 +670,7 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
local->leave_oper_channel_time + HZ / 8);

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;
else
next_scan_state = SCAN_SUSPEND;
Expand All @@ -677,14 +691,18 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
int skip;
struct ieee80211_channel *chan;
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;
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.center_freq1 = chan->center_freq;
local->scan_chandef.center_freq2 = 0;
switch (local->scan_req->scan_width) {
switch (scan_req->scan_width) {
case NL80211_BSS_CHAN_WIDTH_5:
local->scan_chandef.width = NL80211_CHAN_WIDTH_5;
break;
Expand All @@ -698,7 +716,7 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
oper_scan_width = cfg80211_chandef_to_scan_width(
&local->_oper_chandef);
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;
else
local->scan_chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
Expand Down Expand Up @@ -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.
*/
if (chan->flags & IEEE80211_CHAN_NO_IR ||
!local->scan_req->n_ssids) {
if (chan->flags & IEEE80211_CHAN_NO_IR || !scan_req->n_ssids) {
*next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
local->next_scan_state = SCAN_DECISION;
return;
Expand Down Expand Up @@ -777,13 +794,16 @@ void ieee80211_scan_work(struct work_struct *work)
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, scan_work.work);
struct ieee80211_sub_if_data *sdata;
struct cfg80211_scan_request *scan_req;
unsigned long next_delay = 0;
bool aborted;

mutex_lock(&local->mtx);

sdata = rcu_dereference_protected(local->scan_sdata,
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. */
if (test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning)) {
Expand All @@ -796,20 +816,19 @@ void ieee80211_scan_work(struct work_struct *work)
goto out_complete;
}

if (!sdata || !local->scan_req)
if (!sdata || !scan_req)
goto out;

if (!local->scanning) {
struct cfg80211_scan_request *req = local->scan_req;
int rc;

local->scan_req = NULL;
RCU_INIT_POINTER(local->scan_req, NULL);
RCU_INIT_POINTER(local->scan_sdata, NULL);

rc = __ieee80211_start_scan(sdata, req);
rc = __ieee80211_start_scan(sdata, scan_req);
if (rc) {
/* need to complete scan in cfg80211 */
local->scan_req = req;
rcu_assign_pointer(local->scan_req, scan_req);
aborted = true;
goto out_complete;
} else
Expand All @@ -829,7 +848,7 @@ void ieee80211_scan_work(struct work_struct *work)
switch (local->next_scan_state) {
case SCAN_DECISION:
/* 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;
goto out_complete;
}
Expand Down Expand Up @@ -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);
if (ret == 0) {
rcu_assign_pointer(local->sched_scan_sdata, sdata);
local->sched_scan_req = req;
rcu_assign_pointer(local->sched_scan_req, req);
}

kfree(ie);
Expand All @@ -1052,7 +1071,7 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
if (ret) {
/* Clean in case of failure after HW restart or upon resume. */
RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
local->sched_scan_req = NULL;
RCU_INIT_POINTER(local->sched_scan_req, NULL);
}

return ret;
Expand Down Expand Up @@ -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. */
local->sched_scan_req = NULL;
RCU_INIT_POINTER(local->sched_scan_req, NULL);

if (rcu_access_pointer(local->sched_scan_sdata)) {
ret = drv_sched_scan_stop(local, sdata);
Expand Down Expand Up @@ -1125,7 +1144,7 @@ void ieee80211_sched_scan_end(struct ieee80211_local *local)
RCU_INIT_POINTER(local->sched_scan_sdata, NULL);

/* 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);

Expand Down
7 changes: 5 additions & 2 deletions net/mac80211/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -1721,6 +1721,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
int res, i;
bool reconfig_due_to_wowlan = false;
struct ieee80211_sub_if_data *sched_scan_sdata;
struct cfg80211_sched_scan_request *sched_scan_req;
bool sched_scan_stopped = false;

#ifdef CONFIG_PM
Expand Down Expand Up @@ -2011,13 +2012,15 @@ int ieee80211_reconfig(struct ieee80211_local *local)
mutex_lock(&local->mtx);
sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata,
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,
* we're trying to reschedule.
*/
if (__ieee80211_request_sched_scan_start(sched_scan_sdata,
local->sched_scan_req))
sched_scan_req))
sched_scan_stopped = true;
mutex_unlock(&local->mtx);

Expand Down

0 comments on commit 6ea0a69

Please sign in to comment.