Search Linux Wireless

Re: [PATCH v10] mac80211: Optimize scans on current operating channel.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I'm not going to be able to review this properly until I get back -- so
maybe we should just merge it and fix fallout, if any, later?

johannes

On Fri, 2011-02-04 at 11:54 -0800, greearb@xxxxxxxxxxxxxxx wrote:
> From: Ben Greear <greearb@xxxxxxxxxxxxxxx>
> 
> This should decrease un-necessary flushes, on/off channel work,
> and channel changes in cases where the only scanned channel is
> the current operating channel.
> 
> * Removes SCAN_OFF_CHANNEL flag, uses SDATA_STATE_OFFCHANNEL
>   and is-scanning flags instead.
> 
> * Add helper method to determine if we are currently configured
>   for the operating channel.
> 
> * Do no blindly go off/on channel in work.c  Instead, only call
>   appropriate on/off code when we really need to change channels.
>   Always enable offchannel-ps mode when starting work,
>   and disable it when we are done.
> 
> * Consolidate ieee80211_offchannel_stop_station and
>   ieee80211_offchannel_stop_beaconing, call it
>   ieee80211_offchannel_stop_vifs instead.
> 
> * Accept non-beacon frames when scanning on operating channel.
> 
> * Scan state machine optimized to minimize on/off channel
>   transitions.  Also, when going on-channel, go ahead and
>   re-enable beaconing.  We're going to be there for 200ms,
>   so seems like some useful beaconing could happen.
>   Always enable offchannel-ps mode when starting software
>   scan, and disable it when we are done.
> 
> * Grab local->mtx earlier in __ieee80211_scan_completed_finish
>   so that we are protected when calling hw_config(), etc.
> 
> * Pass probe-responses up the stack if scanning on local
>   channel, so that mlme can take a look.
> 
> Signed-off-by: Ben Greear <greearb@xxxxxxxxxxxxxxx>
> ---
> 
> v10:  Make sure NIC is awake when scanning, and allowed to go
>   back to sleep when done scanning.
>   Make sure offchannel-ps mode is enabled for work_work items
>   regardless of whether work is on the operating channel or not.
>   And always disable offchannel-ps mode when done with work.
> 
> :100644 100644 533fd32... ea14d73... M	net/mac80211/ieee80211_i.h
> :100644 100644 09a2744... c155c0b... M	net/mac80211/main.c
> :100644 100644 b4e5267... 13427b1... M	net/mac80211/offchannel.c
> :100644 100644 c08b8e9... a448856... M	net/mac80211/rx.c
> :100644 100644 f246d8a... 93da164... M	net/mac80211/scan.c
> :100644 100644 8fbbc7a... 7b7a503... M	net/mac80211/tx.c
> :100644 100644 36305e0... 6bf787a... M	net/mac80211/work.c
>  net/mac80211/ieee80211_i.h |   13 +++---
>  net/mac80211/main.c        |   53 +++++++++++++++++++++++---
>  net/mac80211/offchannel.c  |   68 ++++++++++++++++++---------------
>  net/mac80211/rx.c          |   12 +----
>  net/mac80211/scan.c        |   88 +++++++++++++++++++++++++++++++------------
>  net/mac80211/tx.c          |    3 +-
>  net/mac80211/work.c        |   66 +++++++++++++++++++++++++++-----
>  7 files changed, 214 insertions(+), 89 deletions(-)
> 
> diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
> index 533fd32..ea14d73 100644
> --- a/net/mac80211/ieee80211_i.h
> +++ b/net/mac80211/ieee80211_i.h
> @@ -654,8 +654,6 @@ struct tpt_led_trigger {
>   *	well be on the operating channel
>   * @SCAN_HW_SCANNING: The hardware is scanning for us, we have no way to
>   *	determine if we are on the operating channel or not
> - * @SCAN_OFF_CHANNEL: We're off our operating channel for scanning,
> - *	gets only set in conjunction with SCAN_SW_SCANNING
>   * @SCAN_COMPLETED: Set for our scan work function when the driver reported
>   *	that the scan completed.
>   * @SCAN_ABORTED: Set for our scan work function when the driver reported
> @@ -664,7 +662,6 @@ struct tpt_led_trigger {
>  enum {
>  	SCAN_SW_SCANNING,
>  	SCAN_HW_SCANNING,
> -	SCAN_OFF_CHANNEL,
>  	SCAN_COMPLETED,
>  	SCAN_ABORTED,
>  };
> @@ -1147,10 +1144,14 @@ void ieee80211_rx_bss_put(struct ieee80211_local *local,
>  			  struct ieee80211_bss *bss);
>  
>  /* off-channel helpers */
> -void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local);
> -void ieee80211_offchannel_stop_station(struct ieee80211_local *local);
> +bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local);
> +void ieee80211_offchannel_enable_all_ps(struct ieee80211_local *local,
> +					bool tell_ap);
> +void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local,
> +				    bool offchannel_ps_enable);
>  void ieee80211_offchannel_return(struct ieee80211_local *local,
> -				 bool enable_beaconing);
> +				 bool enable_beaconing,
> +				 bool offchannel_ps_disable);
>  void ieee80211_hw_roc_setup(struct ieee80211_local *local);
>  
>  /* interface handling */
> diff --git a/net/mac80211/main.c b/net/mac80211/main.c
> index 09a2744..c155c0b 100644
> --- a/net/mac80211/main.c
> +++ b/net/mac80211/main.c
> @@ -98,6 +98,41 @@ static void ieee80211_reconfig_filter(struct work_struct *work)
>  	ieee80211_configure_filter(local);
>  }
>  
> +/*
> + * Returns true if we are logically configured to be on
> + * the operating channel AND the hardware-conf is currently
> + * configured on the operating channel.  Compares channel-type
> + * as well.
> + */
> +bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local)
> +{
> +	struct ieee80211_channel *chan, *scan_chan;
> +	enum nl80211_channel_type channel_type;
> +
> +	/* This logic needs to match logic in ieee80211_hw_config */
> +	if (local->scan_channel) {
> +		chan = local->scan_channel;
> +		channel_type = NL80211_CHAN_NO_HT;
> +	} else if (local->tmp_channel) {
> +		chan = scan_chan = local->tmp_channel;
> +		channel_type = local->tmp_channel_type;
> +	} else {
> +		chan = local->oper_channel;
> +		channel_type = local->_oper_channel_type;
> +	}
> +
> +	if (chan != local->oper_channel ||
> +	    channel_type != local->_oper_channel_type)
> +		return false;
> +
> +	/* Check current hardware-config against oper_channel. */
> +	if ((local->oper_channel != local->hw.conf.channel) ||
> +	    (local->_oper_channel_type != local->hw.conf.channel_type))
> +		return false;
> +
> +	return true;
> +}
> +
>  int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
>  {
>  	struct ieee80211_channel *chan, *scan_chan;
> @@ -110,21 +145,27 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
>  
>  	scan_chan = local->scan_channel;
>  
> +	/* If this off-channel logic ever changes,  ieee80211_on_oper_channel
> +	 * may need to change as well.
> +	 */
>  	offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
>  	if (scan_chan) {
>  		chan = scan_chan;
>  		channel_type = NL80211_CHAN_NO_HT;
> -		local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL;
> -	} else if (local->tmp_channel &&
> -		   local->oper_channel != local->tmp_channel) {
> +	} else if (local->tmp_channel) {
>  		chan = scan_chan = local->tmp_channel;
>  		channel_type = local->tmp_channel_type;
> -		local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL;
>  	} else {
>  		chan = local->oper_channel;
>  		channel_type = local->_oper_channel_type;
> -		local->hw.conf.flags &= ~IEEE80211_CONF_OFFCHANNEL;
>  	}
> +
> +	if (chan != local->oper_channel ||
> +	    channel_type != local->_oper_channel_type)
> +		local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL;
> +	else
> +		local->hw.conf.flags &= ~IEEE80211_CONF_OFFCHANNEL;
> +
>  	offchannel_flag ^= local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
>  
>  	if (offchannel_flag || chan != local->hw.conf.channel ||
> @@ -231,7 +272,7 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
>  
>  	if (changed & BSS_CHANGED_BEACON_ENABLED) {
>  		if (local->quiescing || !ieee80211_sdata_running(sdata) ||
> -		    test_bit(SCAN_SW_SCANNING, &local->scanning)) {
> +		    test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) {
>  			sdata->vif.bss_conf.enable_beacon = false;
>  		} else {
>  			/*
> diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
> index b4e5267..13427b1 100644
> --- a/net/mac80211/offchannel.c
> +++ b/net/mac80211/offchannel.c
> @@ -17,10 +17,14 @@
>  #include "driver-trace.h"
>  
>  /*
> - * inform AP that we will go to sleep so that it will buffer the frames
> - * while we scan
> + * Tell our hardware to disable PS.
> + * Optionally inform AP that we will go to sleep so that it will buffer
> + * the frames while we are doing off-channel work.  This is optional
> + * because we *may* be doing work on-operating channel, and want our
> + * hardware unconditionally awake, but still let the AP send us normal frames.
>   */
> -static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata)
> +static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata,
> +					   bool tell_ap)
>  {
>  	struct ieee80211_local *local = sdata->local;
>  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
> @@ -41,8 +45,8 @@ static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata)
>  		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
>  	}
>  
> -	if (!(local->offchannel_ps_enabled) ||
> -	    !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK))
> +	if (tell_ap && (!local->offchannel_ps_enabled ||
> +			!(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)))
>  		/*
>  		 * If power save was enabled, no need to send a nullfunc
>  		 * frame because AP knows that we are sleeping. But if the
> @@ -77,6 +81,9 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)
>  		 * we are sleeping, let's just enable power save mode in
>  		 * hardware.
>  		 */
> +		/* TODO:  Only set hardware if CONF_PS changed?
> +		 * TODO:  Should we set offchannel_ps_enabled to false?
> +		 */
>  		local->hw.conf.flags |= IEEE80211_CONF_PS;
>  		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
>  	} else if (local->hw.conf.dynamic_ps_timeout > 0) {
> @@ -95,63 +102,61 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)
>  	ieee80211_sta_reset_conn_monitor(sdata);
>  }
>  
> -void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local)
> +void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local,
> +				    bool offchannel_ps_enable)
>  {
>  	struct ieee80211_sub_if_data *sdata;
>  
> +	/*
> +	 * notify the AP about us leaving the channel and stop all
> +	 * STA interfaces.
> +	 */
>  	mutex_lock(&local->iflist_mtx);
>  	list_for_each_entry(sdata, &local->interfaces, list) {
>  		if (!ieee80211_sdata_running(sdata))
>  			continue;
>  
> -		/* disable beaconing */
> +		if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
> +			set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
> +
> +		/* Check to see if we should disable beaconing. */
>  		if (sdata->vif.type == NL80211_IFTYPE_AP ||
>  		    sdata->vif.type == NL80211_IFTYPE_ADHOC ||
>  		    sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
>  			ieee80211_bss_info_change_notify(
>  				sdata, BSS_CHANGED_BEACON_ENABLED);
>  
> -		/*
> -		 * only handle non-STA interfaces here, STA interfaces
> -		 * are handled in ieee80211_offchannel_stop_station(),
> -		 * e.g., from the background scan state machine.
> -		 *
> -		 * In addition, do not stop monitor interface to allow it to be
> -		 * used from user space controlled off-channel operations.
> -		 */
> -		if (sdata->vif.type != NL80211_IFTYPE_STATION &&
> -		    sdata->vif.type != NL80211_IFTYPE_MONITOR) {
> -			set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
> +		if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
>  			netif_tx_stop_all_queues(sdata->dev);
> +			if (offchannel_ps_enable &&
> +			    (sdata->vif.type == NL80211_IFTYPE_STATION) &&
> +			    sdata->u.mgd.associated)
> +				ieee80211_offchannel_ps_enable(sdata, true);
>  		}
>  	}
>  	mutex_unlock(&local->iflist_mtx);
>  }
>  
> -void ieee80211_offchannel_stop_station(struct ieee80211_local *local)
> +void ieee80211_offchannel_enable_all_ps(struct ieee80211_local *local,
> +					bool tell_ap)
>  {
>  	struct ieee80211_sub_if_data *sdata;
>  
> -	/*
> -	 * notify the AP about us leaving the channel and stop all STA interfaces
> -	 */
>  	mutex_lock(&local->iflist_mtx);
>  	list_for_each_entry(sdata, &local->interfaces, list) {
>  		if (!ieee80211_sdata_running(sdata))
>  			continue;
>  
> -		if (sdata->vif.type == NL80211_IFTYPE_STATION) {
> -			set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
> -			netif_tx_stop_all_queues(sdata->dev);
> -			if (sdata->u.mgd.associated)
> -				ieee80211_offchannel_ps_enable(sdata);
> -		}
> +		if (sdata->vif.type == NL80211_IFTYPE_STATION &&
> +		    sdata->u.mgd.associated)
> +			ieee80211_offchannel_ps_enable(sdata, tell_ap);
>  	}
>  	mutex_unlock(&local->iflist_mtx);
>  }
>  
>  void ieee80211_offchannel_return(struct ieee80211_local *local,
> -				 bool enable_beaconing)
> +				 bool enable_beaconing,
> +				 bool offchannel_ps_disable)
>  {
>  	struct ieee80211_sub_if_data *sdata;
>  
> @@ -161,7 +166,8 @@ void ieee80211_offchannel_return(struct ieee80211_local *local,
>  			continue;
>  
>  		/* Tell AP we're back */
> -		if (sdata->vif.type == NL80211_IFTYPE_STATION) {
> +		if (offchannel_ps_disable &&
> +		    sdata->vif.type == NL80211_IFTYPE_STATION) {
>  			if (sdata->u.mgd.associated)
>  				ieee80211_offchannel_ps_disable(sdata);
>  		}
> @@ -181,7 +187,7 @@ void ieee80211_offchannel_return(struct ieee80211_local *local,
>  			netif_tx_wake_all_queues(sdata->dev);
>  		}
>  
> -		/* re-enable beaconing */
> +		/* Check to see if we should re-enable beaconing */
>  		if (enable_beaconing &&
>  		    (sdata->vif.type == NL80211_IFTYPE_AP ||
>  		     sdata->vif.type == NL80211_IFTYPE_ADHOC ||
> diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
> index c08b8e9..a448856 100644
> --- a/net/mac80211/rx.c
> +++ b/net/mac80211/rx.c
> @@ -409,16 +409,10 @@ ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx)
>  	if (likely(!(status->rx_flags & IEEE80211_RX_IN_SCAN)))
>  		return RX_CONTINUE;
>  
> -	if (test_bit(SCAN_HW_SCANNING, &local->scanning))
> +	if (test_bit(SCAN_HW_SCANNING, &local->scanning) ||
> +	    test_bit(SCAN_SW_SCANNING, &local->scanning))
>  		return ieee80211_scan_rx(rx->sdata, skb);
>  
> -	if (test_bit(SCAN_SW_SCANNING, &local->scanning)) {
> -		/* drop all the other packets during a software scan anyway */
> -		if (ieee80211_scan_rx(rx->sdata, skb) != RX_QUEUED)
> -			dev_kfree_skb(skb);
> -		return RX_QUEUED;
> -	}
> -
>  	/* scanning finished during invoking of handlers */
>  	I802_DEBUG_INC(local->rx_handlers_drop_passive_scan);
>  	return RX_DROP_UNUSABLE;
> @@ -2790,7 +2784,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
>  		local->dot11ReceivedFragmentCount++;
>  
>  	if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning) ||
> -		     test_bit(SCAN_OFF_CHANNEL, &local->scanning)))
> +		     test_bit(SCAN_SW_SCANNING, &local->scanning)))
>  		status->rx_flags |= IEEE80211_RX_IN_SCAN;
>  
>  	if (ieee80211_is_mgmt(fc))
> diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
> index f246d8a..93da164 100644
> --- a/net/mac80211/scan.c
> +++ b/net/mac80211/scan.c
> @@ -212,6 +212,14 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
>  	if (bss)
>  		ieee80211_rx_bss_put(sdata->local, bss);
>  
> +	/* If we are on-operating-channel, and this packet is for the
> +	 * current channel, pass the pkt on up the stack so that
> +	 * the rest of the stack can make use of it.
> +	 */
> +	if (ieee80211_cfg_on_oper_channel(sdata->local)
> +	    && (channel == sdata->local->oper_channel))
> +		return RX_CONTINUE;
> +
>  	dev_kfree_skb(skb);
>  	return RX_QUEUED;
>  }
> @@ -293,15 +301,31 @@ static void __ieee80211_scan_completed_finish(struct ieee80211_hw *hw,
>  					      bool was_hw_scan)
>  {
>  	struct ieee80211_local *local = hw_to_local(hw);
> +	bool on_oper_chan;
> +	bool enable_beacons = false;
> +
> +	mutex_lock(&local->mtx);
> +	on_oper_chan = ieee80211_cfg_on_oper_channel(local);
> +
> +	if (was_hw_scan || !on_oper_chan) {
> +		if (WARN_ON(local->scan_channel))
> +			local->scan_channel = NULL;
> +		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
> +	}
>  
> -	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
>  	if (!was_hw_scan) {
> +		bool on_oper_chan2;
>  		ieee80211_configure_filter(local);
>  		drv_sw_scan_complete(local);
> -		ieee80211_offchannel_return(local, true);
> +		on_oper_chan2 = ieee80211_cfg_on_oper_channel(local);
> +		/* We should always be on-channel at this point. */
> +		WARN_ON(!on_oper_chan2);
> +		if (on_oper_chan2 && (on_oper_chan != on_oper_chan2))
> +			enable_beacons = true;
> +
> +		ieee80211_offchannel_return(local, enable_beacons, true);
>  	}
>  
> -	mutex_lock(&local->mtx);
>  	ieee80211_recalc_idle(local);
>  	mutex_unlock(&local->mtx);
>  
> @@ -398,13 +422,15 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
>  
>  	drv_sw_scan_start(local);
>  
> -	ieee80211_offchannel_stop_beaconing(local);
> -
>  	local->leave_oper_channel_time = 0;
>  	local->next_scan_state = SCAN_DECISION;
>  	local->scan_channel_idx = 0;
>  
> -	drv_flush(local, false);
> +	/* We always want to use off-channel PS, even if we
> +	 * are not really leaving oper-channel.  Don't
> +	 * tell the AP though, as long as we are on-channel.
> +	 */
> +	ieee80211_offchannel_enable_all_ps(local, false);
>  
>  	ieee80211_configure_filter(local);
>  
> @@ -544,7 +570,21 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
>  	}
>  	mutex_unlock(&local->iflist_mtx);
>  
> -	if (local->scan_channel) {
> +	next_chan = local->scan_req->channels[local->scan_channel_idx];
> +
> +	if (ieee80211_cfg_on_oper_channel(local)) {
> +		/* We're currently on operating channel. */
> +		if ((next_chan == local->oper_channel) &&
> +		    (local->_oper_channel_type == NL80211_CHAN_NO_HT))
> +			/* We don't need to move off of operating channel. */
> +			local->next_scan_state = SCAN_SET_CHANNEL;
> +		else
> +			/*
> +			 * We do need to leave operating channel, as next
> +			 * scan is somewhere else.
> +			 */
> +			local->next_scan_state = SCAN_LEAVE_OPER_CHANNEL;
> +	} else {
>  		/*
>  		 * we're currently scanning a different channel, let's
>  		 * see if we can scan another channel without interfering
> @@ -560,7 +600,6 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
>  		 *
>  		 * Otherwise switch back to the operating channel.
>  		 */
> -		next_chan = local->scan_req->channels[local->scan_channel_idx];
>  
>  		bad_latency = time_after(jiffies +
>  				ieee80211_scan_get_channel_time(next_chan),
> @@ -578,12 +617,6 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
>  			local->next_scan_state = SCAN_ENTER_OPER_CHANNEL;
>  		else
>  			local->next_scan_state = SCAN_SET_CHANNEL;
> -	} else {
> -		/*
> -		 * we're on the operating channel currently, let's
> -		 * leave that channel now to scan another one
> -		 */
> -		local->next_scan_state = SCAN_LEAVE_OPER_CHANNEL;
>  	}
>  
>  	*next_delay = 0;
> @@ -592,9 +625,10 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
>  static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local,
>  						    unsigned long *next_delay)
>  {
> -	ieee80211_offchannel_stop_station(local);
> -
> -	__set_bit(SCAN_OFF_CHANNEL, &local->scanning);
> +	/* PS will already be in off-channel mode,
> +	 * we do that once at the beginning of scanning.
> +	 */
> +	ieee80211_offchannel_stop_vifs(local, false);
>  
>  	/*
>  	 * What if the nullfunc frames didn't arrive?
> @@ -617,15 +651,15 @@ static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *loca
>  {
>  	/* switch back to the operating channel */
>  	local->scan_channel = NULL;
> -	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
> +	if (!ieee80211_cfg_on_oper_channel(local))
> +		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
>  
>  	/*
> -	 * Only re-enable station mode interface now; beaconing will be
> -	 * re-enabled once the full scan has been completed.
> +	 * Re-enable vifs and beaconing.  Leave PS
> +	 * in off-channel state..will put that back
> +	 * on-channel at the end of scanning.
>  	 */
> -	ieee80211_offchannel_return(local, false);
> -
> -	__clear_bit(SCAN_OFF_CHANNEL, &local->scanning);
> +	ieee80211_offchannel_return(local, true, false);
>  
>  	*next_delay = HZ / 5;
>  	local->next_scan_state = SCAN_DECISION;
> @@ -641,8 +675,12 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
>  	chan = local->scan_req->channels[local->scan_channel_idx];
>  
>  	local->scan_channel = chan;
> -	if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL))
> -		skip = 1;
> +
> +	/* Only call hw-config if we really need to change channels. */
> +	if ((chan != local->hw.conf.channel) ||
> +	    (local->hw.conf.channel_type != NL80211_CHAN_NO_HT))
> +		if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL))
> +			skip = 1;
>  
>  	/* advance state machine to next channel/band */
>  	local->scan_channel_idx++;
> diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
> index 8fbbc7a..7b7a503 100644
> --- a/net/mac80211/tx.c
> +++ b/net/mac80211/tx.c
> @@ -257,7 +257,8 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
>  	if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED))
>  		return TX_CONTINUE;
>  
> -	if (unlikely(test_bit(SCAN_OFF_CHANNEL, &tx->local->scanning)) &&
> +	if (unlikely(test_bit(SCAN_SW_SCANNING, &tx->local->scanning)) &&
> +	    test_bit(SDATA_STATE_OFFCHANNEL, &tx->sdata->state) &&
>  	    !ieee80211_is_probe_req(hdr->frame_control) &&
>  	    !ieee80211_is_nullfunc(hdr->frame_control))
>  		/*
> diff --git a/net/mac80211/work.c b/net/mac80211/work.c
> index 36305e0..6bf787a 100644
> --- a/net/mac80211/work.c
> +++ b/net/mac80211/work.c
> @@ -924,18 +924,44 @@ static void ieee80211_work_work(struct work_struct *work)
>  		}
>  
>  		if (!started && !local->tmp_channel) {
> -			/*
> -			 * TODO: could optimize this by leaving the
> -			 *	 station vifs in awake mode if they
> -			 *	 happen to be on the same channel as
> -			 *	 the requested channel
> -			 */
> -			ieee80211_offchannel_stop_beaconing(local);
> -			ieee80211_offchannel_stop_station(local);
> +			bool on_oper_chan;
> +			bool tmp_chan_changed = false;
> +			bool on_oper_chan2;
> +			on_oper_chan = ieee80211_cfg_on_oper_channel(local);
> +			if (local->tmp_channel)
> +				if ((local->tmp_channel != wk->chan) ||
> +				    (local->tmp_channel_type != wk->chan_type))
> +					tmp_chan_changed = true;
>  
>  			local->tmp_channel = wk->chan;
>  			local->tmp_channel_type = wk->chan_type;
> -			ieee80211_hw_config(local, 0);
> +			/*
> +			 * Leave the station vifs in awake mode if they
> +			 * happen to be on the same channel as
> +			 * the requested channel.
> +			 */
> +			on_oper_chan2 = ieee80211_cfg_on_oper_channel(local);
> +			if (on_oper_chan != on_oper_chan2) {
> +				if (on_oper_chan2) {
> +					/* going off oper channel, PS too */
> +					ieee80211_offchannel_stop_vifs(local,
> +								       true);
> +					ieee80211_hw_config(local, 0);
> +				} else {
> +					/* going on channel, but leave PS
> +					 * off-channel. */
> +					ieee80211_hw_config(local, 0);
> +					ieee80211_offchannel_return(local,
> +								    true,
> +								    false);
> +				}
> +			} else if (tmp_chan_changed)
> +				/* Still off-channel, but on some other
> +				 * channel, so update hardware.
> +				 * PS should already be off-channel.
> +				 */
> +				ieee80211_hw_config(local, 0);
> +
>  			started = true;
>  			wk->timeout = jiffies;
>  		}
> @@ -1011,9 +1037,27 @@ static void ieee80211_work_work(struct work_struct *work)
>  	}
>  
>  	if (!remain_off_channel && local->tmp_channel) {
> +		bool on_oper_chan = ieee80211_cfg_on_oper_channel(local);
>  		local->tmp_channel = NULL;
> -		ieee80211_hw_config(local, 0);
> -		ieee80211_offchannel_return(local, true);
> +		/* If tmp_channel wasn't operating channel, then
> +		 * we need to go back on-channel.
> +		 * NOTE:  If we can ever be here while scannning,
> +		 * or if the hw_config() channel config logic changes,
> +		 * then we may need to do a more thorough check to see if
> +		 * we still need to do a hardware config.  Currently,
> +		 * we cannot be here while scanning, however.
> +		 */
> +		if (ieee80211_cfg_on_oper_channel(local) && !on_oper_chan)
> +			ieee80211_hw_config(local, 0);
> +
> +		/* At the least, we need to disable offchannel_ps,
> +		 * so just go ahead and run the entire offchannel
> +		 * return logic here.  We *could* skip enabling
> +		 * beaconing if we were already on-oper-channel
> +		 * as a future optimization.
> +		 */
> +		ieee80211_offchannel_return(local, true, true);
> +
>  		/* give connection some time to breathe */
>  		run_again(local, jiffies + HZ/2);
>  	}


--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux