Skip to content

Commit

Permalink
mac80211: Optimize scans on current operating channel.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Ben Greear authored and John W. Linville committed Feb 4, 2011
1 parent cb8d61d commit b23b025
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 89 deletions.
13 changes: 7 additions & 6 deletions net/mac80211/ieee80211_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -655,8 +655,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
Expand All @@ -665,7 +663,6 @@ struct tpt_led_trigger {
enum {
SCAN_SW_SCANNING,
SCAN_HW_SCANNING,
SCAN_OFF_CHANNEL,
SCAN_COMPLETED,
SCAN_ABORTED,
};
Expand Down Expand Up @@ -1148,10 +1145,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 */
Expand Down
53 changes: 47 additions & 6 deletions net/mac80211/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 ||
Expand Down Expand Up @@ -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 {
/*
Expand Down
68 changes: 37 additions & 31 deletions net/mac80211/offchannel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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;

Expand All @@ -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);
}
Expand All @@ -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 ||
Expand Down
12 changes: 3 additions & 9 deletions net/mac80211/rx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -2793,7 +2787,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))
Expand Down
Loading

0 comments on commit b23b025

Please sign in to comment.