Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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;
@@ -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;
+49 −30
Original line number Diff line number Diff line
@@ -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;

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

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

	/*
@@ -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;
	}
@@ -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) {
@@ -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);
@@ -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,
@@ -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
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -777,6 +794,7 @@ 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;

@@ -784,6 +802,8 @@ void ieee80211_scan_work(struct work_struct *work)

	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)) {
@@ -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
@@ -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;
			}
@@ -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);
@@ -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;
@@ -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);
@@ -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);

+5 −2
Original line number Diff line number Diff line
@@ -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
@@ -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);