summaryrefslogtreecommitdiffstats
path: root/net/mac80211/scan.c
diff options
context:
space:
mode:
authorHelmut Schaa <Helmut.Schaa@gmx.de>2010-02-24 14:19:21 +0100
committerJohn W. Linville <linville@tuxdriver.com>2010-03-09 21:03:07 +0100
commitdf13cce53a7b28a81460e6bfc4857e9df4956141 (patch)
tree5c7be2b33088f457b0510e7de660a88b8dc9c397 /net/mac80211/scan.c
parentath9k: never read from the AR_IMR_S2 register (diff)
downloadlinux-df13cce53a7b28a81460e6bfc4857e9df4956141.tar.xz
linux-df13cce53a7b28a81460e6bfc4857e9df4956141.zip
mac80211: Improve software scan timing
The current software scan implemenation in mac80211 returns to the operating channel after each scanned channel. However, in some situations (e.g. no traffic) it would be nicer to scan a few channels in a row to speed up the scan itself. Hence, after scanning a channel, check if we have queued up any tx frames and return to the operating channel in that case. Unfortunately we don't know if the AP has buffered any frames for us. Hence, scan only as many channels in a row as the pm_qos latency and the negotiated listen interval allows us to. Signed-off-by: Helmut Schaa <helmut.schaa@googlemail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/scan.c')
-rw-r--r--net/mac80211/scan.c71
1 files changed, 65 insertions, 6 deletions
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index b822dce97867..75a85978c3b3 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -14,6 +14,8 @@
#include <linux/if_arp.h>
#include <linux/rtnetlink.h>
+#include <linux/pm_qos_params.h>
+#include <net/sch_generic.h>
#include <net/mac80211.h>
#include "ieee80211_i.h"
@@ -321,6 +323,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
ieee80211_offchannel_stop_beaconing(local);
+ local->leave_oper_channel_time = 0;
local->next_scan_state = SCAN_DECISION;
local->scan_channel_idx = 0;
@@ -425,11 +428,28 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
return rc;
}
+static unsigned long
+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)
+ return IEEE80211_PASSIVE_CHANNEL_TIME;
+ return IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME;
+}
+
static int ieee80211_scan_state_decision(struct ieee80211_local *local,
unsigned long *next_delay)
{
bool associated = false;
+ bool tx_empty = true;
+ bool bad_latency;
+ bool listen_int_exceeded;
+ unsigned long min_beacon_int = 0;
struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_channel *next_chan;
/* if no more bands/channels left, complete scan and advance to the idle state */
if (local->scan_channel_idx >= local->scan_req->n_channels) {
@@ -437,7 +457,11 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local,
return 1;
}
- /* check if at least one STA interface is associated */
+ /*
+ * check if at least one STA interface is associated,
+ * check if at least one STA interface has pending tx frames
+ * and grab the lowest used beacon interval
+ */
mutex_lock(&local->iflist_mtx);
list_for_each_entry(sdata, &local->interfaces, list) {
if (!ieee80211_sdata_running(sdata))
@@ -446,7 +470,16 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local,
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
if (sdata->u.mgd.associated) {
associated = true;
- break;
+
+ if (sdata->vif.bss_conf.beacon_int <
+ min_beacon_int || min_beacon_int == 0)
+ min_beacon_int =
+ sdata->vif.bss_conf.beacon_int;
+
+ if (!qdisc_all_tx_empty(sdata->dev)) {
+ tx_empty = false;
+ break;
+ }
}
}
}
@@ -455,11 +488,34 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local,
if (local->scan_channel) {
/*
* we're currently scanning a different channel, let's
- * switch back to the operating channel now if at least
- * one interface is associated. Otherwise just scan the
- * next channel
+ * see if we can scan another channel without interfering
+ * with the current traffic situation.
+ *
+ * Since we don't know if the AP has pending frames for us
+ * we can only check for our tx queues and use the current
+ * pm_qos requirements for rx. Hence, if no tx traffic occurs
+ * at all we will scan as many channels in a row as the pm_qos
+ * latency allows us to. Additionally we also check for the
+ * currently negotiated listen interval to prevent losing
+ * frames unnecessarily.
+ *
+ * Otherwise switch back to the operating channel.
*/
- if (associated)
+ next_chan = local->scan_req->channels[local->scan_channel_idx];
+
+ bad_latency = time_after(jiffies +
+ ieee80211_scan_get_channel_time(next_chan),
+ local->leave_oper_channel_time +
+ usecs_to_jiffies(pm_qos_requirement(PM_QOS_NETWORK_LATENCY)));
+
+ listen_int_exceeded = time_after(jiffies +
+ ieee80211_scan_get_channel_time(next_chan),
+ local->leave_oper_channel_time +
+ usecs_to_jiffies(min_beacon_int * 1024) *
+ local->hw.conf.listen_interval);
+
+ if (associated && ( !tx_empty || bad_latency ||
+ listen_int_exceeded))
local->next_scan_state = SCAN_ENTER_OPER_CHANNEL;
else
local->next_scan_state = SCAN_SET_CHANNEL;
@@ -491,6 +547,9 @@ static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *loca
else
*next_delay = HZ / 10;
+ /* remember when we left the operating channel */
+ local->leave_oper_channel_time = jiffies;
+
/* advance to the next channel to be scanned */
local->next_scan_state = SCAN_SET_CHANNEL;
}