aboutsummaryrefslogtreecommitdiff
path: root/net/mac80211/scan.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/scan.c')
-rw-r--r--net/mac80211/scan.c150
1 files changed, 95 insertions, 55 deletions
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 08afe74b98f..f40661eb75b 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -238,6 +238,9 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
enum ieee80211_band band;
int i, ielen, n_chans;
+ if (test_bit(SCAN_HW_CANCELLED, &local->scanning))
+ return false;
+
do {
if (local->hw_scan_band == IEEE80211_NUM_BANDS)
return false;
@@ -268,10 +271,11 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
return true;
}
-static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
- bool was_hw_scan)
+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;
lockdep_assert_held(&local->mtx);
@@ -287,7 +291,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
if (WARN_ON(!local->scan_req))
return;
- if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) {
+ if (hw_scan && !aborted && ieee80211_prep_hw_scan(local)) {
int rc;
rc = drv_hw_scan(local,
@@ -305,7 +309,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
if (local->scan_req != local->int_scan_req)
cfg80211_scan_done(local->scan_req, aborted);
local->scan_req = NULL;
- rcu_assign_pointer(local->scan_sdata, NULL);
+ RCU_INIT_POINTER(local->scan_sdata, NULL);
local->scanning = 0;
local->scan_chandef.chan = NULL;
@@ -313,7 +317,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
/* Set power back to normal operating levels. */
ieee80211_hw_config(local, 0);
- if (!was_hw_scan) {
+ if (!hw_scan) {
ieee80211_configure_filter(local);
drv_sw_scan_complete(local);
ieee80211_offchannel_return(local);
@@ -324,7 +328,8 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
ieee80211_mlme_notify_scan_completed(local);
ieee80211_ibss_notify_scan_completed(local);
ieee80211_mesh_notify_scan_completed(local);
- ieee80211_start_next_roc(local);
+ if (was_scanning)
+ ieee80211_start_next_roc(local);
}
void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
@@ -391,8 +396,7 @@ static bool ieee80211_can_scan(struct ieee80211_local *local,
return false;
if (sdata->vif.type == NL80211_IFTYPE_STATION &&
- sdata->u.mgd.flags & (IEEE80211_STA_BEACON_POLL |
- IEEE80211_STA_CONNECTION_POLL))
+ sdata->u.mgd.flags & IEEE80211_STA_CONNECTION_POLL)
return false;
return true;
@@ -468,9 +472,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
if (local->ops->hw_scan) {
u8 *ies;
- local->hw_scan_ies_bufsize = 2 + IEEE80211_MAX_SSID_LEN +
- local->scan_ies_len +
- req->ie_len;
+ local->hw_scan_ies_bufsize = local->scan_ies_len + req->ie_len;
local->hw_scan_req = kmalloc(
sizeof(*local->hw_scan_req) +
req->n_channels * sizeof(req->channels[0]) +
@@ -524,7 +526,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
ieee80211_hw_config(local, 0);
if ((req->channels[0]->flags &
- IEEE80211_CHAN_PASSIVE_SCAN) ||
+ IEEE80211_CHAN_NO_IR) ||
!local->scan_req->n_ssids) {
next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
} else {
@@ -557,7 +559,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
ieee80211_recalc_idle(local);
local->scan_req = NULL;
- rcu_assign_pointer(local->scan_sdata, NULL);
+ RCU_INIT_POINTER(local->scan_sdata, NULL);
}
return rc;
@@ -570,7 +572,7 @@ ieee80211_scan_get_channel_time(struct ieee80211_channel *chan)
* TODO: channel switching also consumes quite some time,
* add that delay as well to get a better estimation
*/
- if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
+ if (chan->flags & IEEE80211_CHAN_NO_IR)
return IEEE80211_PASSIVE_CHANNEL_TIME;
return IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME;
}
@@ -694,7 +696,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_PASSIVE_SCAN ||
+ if (chan->flags & IEEE80211_CHAN_NO_IR ||
!local->scan_req->n_ssids) {
*next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
local->next_scan_state = SCAN_DECISION;
@@ -745,7 +747,7 @@ void ieee80211_scan_work(struct work_struct *work)
container_of(work, struct ieee80211_local, scan_work.work);
struct ieee80211_sub_if_data *sdata;
unsigned long next_delay = 0;
- bool aborted, hw_scan;
+ bool aborted;
mutex_lock(&local->mtx);
@@ -771,7 +773,7 @@ void ieee80211_scan_work(struct work_struct *work)
int rc;
local->scan_req = NULL;
- rcu_assign_pointer(local->scan_sdata, NULL);
+ RCU_INIT_POINTER(local->scan_sdata, NULL);
rc = __ieee80211_start_scan(sdata, req);
if (rc) {
@@ -784,14 +786,6 @@ void ieee80211_scan_work(struct work_struct *work)
}
/*
- * Avoid re-scheduling when the sdata is going away.
- */
- if (!ieee80211_sdata_running(sdata)) {
- aborted = true;
- goto out_complete;
- }
-
- /*
* as long as no delay is required advance immediately
* without scheduling a new work
*/
@@ -832,8 +826,7 @@ void ieee80211_scan_work(struct work_struct *work)
goto out;
out_complete:
- hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning);
- __ieee80211_scan_completed(&local->hw, aborted, hw_scan);
+ __ieee80211_scan_completed(&local->hw, aborted);
out:
mutex_unlock(&local->mtx);
}
@@ -879,7 +872,7 @@ int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *tmp_ch =
&local->hw.wiphy->bands[band]->channels[i];
- if (tmp_ch->flags & (IEEE80211_CHAN_NO_IBSS |
+ if (tmp_ch->flags & (IEEE80211_CHAN_NO_IR |
IEEE80211_CHAN_DISABLED))
continue;
@@ -893,7 +886,7 @@ int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
local->int_scan_req->n_channels = n_ch;
} else {
- if (WARN_ON_ONCE(chan->flags & (IEEE80211_CHAN_NO_IBSS |
+ if (WARN_ON_ONCE(chan->flags & (IEEE80211_CHAN_NO_IR |
IEEE80211_CHAN_DISABLED)))
goto unlock;
@@ -940,7 +933,23 @@ void ieee80211_scan_cancel(struct ieee80211_local *local)
if (!local->scan_req)
goto out;
+ /*
+ * We have a scan running and the driver already reported completion,
+ * but the worker hasn't run yet or is stuck on the mutex - mark it as
+ * cancelled.
+ */
+ if (test_bit(SCAN_HW_SCANNING, &local->scanning) &&
+ test_bit(SCAN_COMPLETED, &local->scanning)) {
+ set_bit(SCAN_HW_CANCELLED, &local->scanning);
+ goto out;
+ }
+
if (test_bit(SCAN_HW_SCANNING, &local->scanning)) {
+ /*
+ * Make sure that __ieee80211_scan_completed doesn't trigger a
+ * scan on another band.
+ */
+ set_bit(SCAN_HW_CANCELLED, &local->scanning);
if (local->ops->cancel_hw_scan)
drv_cancel_hw_scan(local,
rcu_dereference_protected(local->scan_sdata,
@@ -955,33 +964,25 @@ void ieee80211_scan_cancel(struct ieee80211_local *local)
*/
cancel_delayed_work(&local->scan_work);
/* and clean up */
- __ieee80211_scan_completed(&local->hw, true, false);
+ __ieee80211_scan_completed(&local->hw, true);
out:
mutex_unlock(&local->mtx);
}
-int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
- struct cfg80211_sched_scan_request *req)
+int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_sched_scan_request *req)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_sched_scan_ies sched_scan_ies = {};
struct cfg80211_chan_def chandef;
int ret, i, iebufsz;
- iebufsz = 2 + IEEE80211_MAX_SSID_LEN +
- local->scan_ies_len + req->ie_len;
-
- mutex_lock(&local->mtx);
+ iebufsz = local->scan_ies_len + req->ie_len;
- if (rcu_access_pointer(local->sched_scan_sdata)) {
- ret = -EBUSY;
- goto out;
- }
+ lockdep_assert_held(&local->mtx);
- if (!local->ops->sched_scan_start) {
- ret = -ENOTSUPP;
- goto out;
- }
+ if (!local->ops->sched_scan_start)
+ return -ENOTSUPP;
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
if (!local->hw.wiphy->bands[i])
@@ -1002,13 +1003,39 @@ 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)
+ if (ret == 0) {
rcu_assign_pointer(local->sched_scan_sdata, sdata);
+ local->sched_scan_req = req;
+ }
out_free:
while (i > 0)
kfree(sched_scan_ies.ie[--i]);
-out:
+
+ 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;
+ }
+
+ return ret;
+}
+
+int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_sched_scan_request *req)
+{
+ struct ieee80211_local *local = sdata->local;
+ int ret;
+
+ mutex_lock(&local->mtx);
+
+ if (rcu_access_pointer(local->sched_scan_sdata)) {
+ mutex_unlock(&local->mtx);
+ return -EBUSY;
+ }
+
+ ret = __ieee80211_request_sched_scan_start(sdata, req);
+
mutex_unlock(&local->mtx);
return ret;
}
@@ -1025,9 +1052,14 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
goto out;
}
- if (rcu_access_pointer(local->sched_scan_sdata))
- drv_sched_scan_stop(local, sdata);
+ /* We don't want to restart sched scan anymore. */
+ local->sched_scan_req = NULL;
+ if (rcu_access_pointer(local->sched_scan_sdata)) {
+ ret = drv_sched_scan_stop(local, sdata);
+ if (!ret)
+ rcu_assign_pointer(local->sched_scan_sdata, NULL);
+ }
out:
mutex_unlock(&local->mtx);
@@ -1044,12 +1076,8 @@ void ieee80211_sched_scan_results(struct ieee80211_hw *hw)
}
EXPORT_SYMBOL(ieee80211_sched_scan_results);
-void ieee80211_sched_scan_stopped_work(struct work_struct *work)
+void ieee80211_sched_scan_end(struct ieee80211_local *local)
{
- struct ieee80211_local *local =
- container_of(work, struct ieee80211_local,
- sched_scan_stopped_work);
-
mutex_lock(&local->mtx);
if (!rcu_access_pointer(local->sched_scan_sdata)) {
@@ -1057,19 +1085,31 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work)
return;
}
- rcu_assign_pointer(local->sched_scan_sdata, NULL);
+ RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
+
+ /* If sched scan was aborted by the driver. */
+ local->sched_scan_req = NULL;
mutex_unlock(&local->mtx);
cfg80211_sched_scan_stopped(local->hw.wiphy);
}
+void ieee80211_sched_scan_stopped_work(struct work_struct *work)
+{
+ struct ieee80211_local *local =
+ container_of(work, struct ieee80211_local,
+ sched_scan_stopped_work);
+
+ ieee80211_sched_scan_end(local);
+}
+
void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
trace_api_sched_scan_stopped(local);
- ieee80211_queue_work(&local->hw, &local->sched_scan_stopped_work);
+ schedule_work(&local->sched_scan_stopped_work);
}
EXPORT_SYMBOL(ieee80211_sched_scan_stopped);