aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/ieee80211_i.h1
-rw-r--r--net/mac80211/scan.c71
2 files changed, 66 insertions, 6 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 241533e1bc03..b84126491ab1 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -745,6 +745,7 @@ struct ieee80211_local {
745 int scan_channel_idx; 745 int scan_channel_idx;
746 int scan_ies_len; 746 int scan_ies_len;
747 747
748 unsigned long leave_oper_channel_time;
748 enum mac80211_scan_state next_scan_state; 749 enum mac80211_scan_state next_scan_state;
749 struct delayed_work scan_work; 750 struct delayed_work scan_work;
750 struct ieee80211_sub_if_data *scan_sdata; 751 struct ieee80211_sub_if_data *scan_sdata;
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
431static unsigned long
432ieee80211_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
428static int ieee80211_scan_state_decision(struct ieee80211_local *local, 443static 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}