diff options
Diffstat (limited to 'net/mac80211/scan.c')
| -rw-r--r-- | net/mac80211/scan.c | 150 | 
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);  | 
