mac80211: Optimize scans on current operating channel.

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@candelatech.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Ben Greear
2011-02-04 11:54:17 -08:00
committed by John W. Linville
parent cb8d61de2d
commit b23b025fe2
7 changed files with 233 additions and 108 deletions

View File

@ -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);
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
if (!was_hw_scan) {
ieee80211_configure_filter(local);
drv_sw_scan_complete(local);
ieee80211_offchannel_return(local, true);
}
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);
}
if (!was_hw_scan) {
bool on_oper_chan2;
ieee80211_configure_filter(local);
drv_sw_scan_complete(local);
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);
}
ieee80211_recalc_idle(local);
mutex_unlock(&local->mtx);
@ -341,13 +365,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);
@ -487,7 +513,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
@ -503,7 +543,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),
@ -521,12 +560,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;
@ -535,9 +568,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?
@ -560,15 +594,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;
@ -584,8 +618,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++;