diff options
author | Helmut Schaa <Helmut.Schaa@gmx.de> | 2010-02-24 08:19:21 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-03-09 15:03:07 -0500 |
commit | df13cce53a7b28a81460e6bfc4857e9df4956141 (patch) | |
tree | 5c7be2b33088f457b0510e7de660a88b8dc9c397 /net/mac80211/scan.c | |
parent | 74bad5cb497080514c4a945f38589bdb574fdfb7 (diff) |
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.c | 71 |
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 @@ | |||
14 | 14 | ||
15 | #include <linux/if_arp.h> | 15 | #include <linux/if_arp.h> |
16 | #include <linux/rtnetlink.h> | 16 | #include <linux/rtnetlink.h> |
17 | #include <linux/pm_qos_params.h> | ||
18 | #include <net/sch_generic.h> | ||
17 | #include <net/mac80211.h> | 19 | #include <net/mac80211.h> |
18 | 20 | ||
19 | #include "ieee80211_i.h" | 21 | #include "ieee80211_i.h" |
@@ -321,6 +323,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) | |||
321 | 323 | ||
322 | ieee80211_offchannel_stop_beaconing(local); | 324 | ieee80211_offchannel_stop_beaconing(local); |
323 | 325 | ||
326 | local->leave_oper_channel_time = 0; | ||
324 | local->next_scan_state = SCAN_DECISION; | 327 | local->next_scan_state = SCAN_DECISION; |
325 | local->scan_channel_idx = 0; | 328 | local->scan_channel_idx = 0; |
326 | 329 | ||
@@ -425,11 +428,28 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, | |||
425 | return rc; | 428 | return rc; |
426 | } | 429 | } |
427 | 430 | ||
431 | static unsigned long | ||
432 | ieee80211_scan_get_channel_time(struct ieee80211_channel *chan) | ||
433 | { | ||
434 | /* | ||
435 | * TODO: channel switching also consumes quite some time, | ||
436 | * add that delay as well to get a better estimation | ||
437 | */ | ||
438 | if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) | ||
439 | return IEEE80211_PASSIVE_CHANNEL_TIME; | ||
440 | return IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME; | ||
441 | } | ||
442 | |||
428 | static int ieee80211_scan_state_decision(struct ieee80211_local *local, | 443 | static int ieee80211_scan_state_decision(struct ieee80211_local *local, |
429 | unsigned long *next_delay) | 444 | unsigned long *next_delay) |
430 | { | 445 | { |
431 | bool associated = false; | 446 | bool associated = false; |
447 | bool tx_empty = true; | ||
448 | bool bad_latency; | ||
449 | bool listen_int_exceeded; | ||
450 | unsigned long min_beacon_int = 0; | ||
432 | struct ieee80211_sub_if_data *sdata; | 451 | struct ieee80211_sub_if_data *sdata; |
452 | struct ieee80211_channel *next_chan; | ||
433 | 453 | ||
434 | /* if no more bands/channels left, complete scan and advance to the idle state */ | 454 | /* if no more bands/channels left, complete scan and advance to the idle state */ |
435 | if (local->scan_channel_idx >= local->scan_req->n_channels) { | 455 | if (local->scan_channel_idx >= local->scan_req->n_channels) { |
@@ -437,7 +457,11 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
437 | return 1; | 457 | return 1; |
438 | } | 458 | } |
439 | 459 | ||
440 | /* check if at least one STA interface is associated */ | 460 | /* |
461 | * check if at least one STA interface is associated, | ||
462 | * check if at least one STA interface has pending tx frames | ||
463 | * and grab the lowest used beacon interval | ||
464 | */ | ||
441 | mutex_lock(&local->iflist_mtx); | 465 | mutex_lock(&local->iflist_mtx); |
442 | list_for_each_entry(sdata, &local->interfaces, list) { | 466 | list_for_each_entry(sdata, &local->interfaces, list) { |
443 | if (!ieee80211_sdata_running(sdata)) | 467 | if (!ieee80211_sdata_running(sdata)) |
@@ -446,7 +470,16 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
446 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | 470 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
447 | if (sdata->u.mgd.associated) { | 471 | if (sdata->u.mgd.associated) { |
448 | associated = true; | 472 | associated = true; |
449 | break; | 473 | |
474 | if (sdata->vif.bss_conf.beacon_int < | ||
475 | min_beacon_int || min_beacon_int == 0) | ||
476 | min_beacon_int = | ||
477 | sdata->vif.bss_conf.beacon_int; | ||
478 | |||
479 | if (!qdisc_all_tx_empty(sdata->dev)) { | ||
480 | tx_empty = false; | ||
481 | break; | ||
482 | } | ||
450 | } | 483 | } |
451 | } | 484 | } |
452 | } | 485 | } |
@@ -455,11 +488,34 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
455 | if (local->scan_channel) { | 488 | if (local->scan_channel) { |
456 | /* | 489 | /* |
457 | * we're currently scanning a different channel, let's | 490 | * we're currently scanning a different channel, let's |
458 | * switch back to the operating channel now if at least | 491 | * see if we can scan another channel without interfering |
459 | * one interface is associated. Otherwise just scan the | 492 | * with the current traffic situation. |
460 | * next channel | 493 | * |
494 | * Since we don't know if the AP has pending frames for us | ||
495 | * we can only check for our tx queues and use the current | ||
496 | * pm_qos requirements for rx. Hence, if no tx traffic occurs | ||
497 | * at all we will scan as many channels in a row as the pm_qos | ||
498 | * latency allows us to. Additionally we also check for the | ||
499 | * currently negotiated listen interval to prevent losing | ||
500 | * frames unnecessarily. | ||
501 | * | ||
502 | * Otherwise switch back to the operating channel. | ||
461 | */ | 503 | */ |
462 | if (associated) | 504 | next_chan = local->scan_req->channels[local->scan_channel_idx]; |
505 | |||
506 | bad_latency = time_after(jiffies + | ||
507 | ieee80211_scan_get_channel_time(next_chan), | ||
508 | local->leave_oper_channel_time + | ||
509 | usecs_to_jiffies(pm_qos_requirement(PM_QOS_NETWORK_LATENCY))); | ||
510 | |||
511 | listen_int_exceeded = time_after(jiffies + | ||
512 | ieee80211_scan_get_channel_time(next_chan), | ||
513 | local->leave_oper_channel_time + | ||
514 | usecs_to_jiffies(min_beacon_int * 1024) * | ||
515 | local->hw.conf.listen_interval); | ||
516 | |||
517 | if (associated && ( !tx_empty || bad_latency || | ||
518 | listen_int_exceeded)) | ||
463 | local->next_scan_state = SCAN_ENTER_OPER_CHANNEL; | 519 | local->next_scan_state = SCAN_ENTER_OPER_CHANNEL; |
464 | else | 520 | else |
465 | local->next_scan_state = SCAN_SET_CHANNEL; | 521 | local->next_scan_state = SCAN_SET_CHANNEL; |
@@ -491,6 +547,9 @@ static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *loca | |||
491 | else | 547 | else |
492 | *next_delay = HZ / 10; | 548 | *next_delay = HZ / 10; |
493 | 549 | ||
550 | /* remember when we left the operating channel */ | ||
551 | local->leave_oper_channel_time = jiffies; | ||
552 | |||
494 | /* advance to the next channel to be scanned */ | 553 | /* advance to the next channel to be scanned */ |
495 | local->next_scan_state = SCAN_SET_CHANNEL; | 554 | local->next_scan_state = SCAN_SET_CHANNEL; |
496 | } | 555 | } |