aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/scan.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/scan.c')
-rw-r--r--net/mac80211/scan.c74
1 files changed, 68 insertions, 6 deletions
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index b822dce97867..e1a3defdf581 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -14,6 +14,9 @@
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>
19#include <linux/slab.h>
17#include <net/mac80211.h> 20#include <net/mac80211.h>
18 21
19#include "ieee80211_i.h" 22#include "ieee80211_i.h"
@@ -245,6 +248,8 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
245 struct ieee80211_local *local = hw_to_local(hw); 248 struct ieee80211_local *local = hw_to_local(hw);
246 bool was_hw_scan; 249 bool was_hw_scan;
247 250
251 trace_api_scan_completed(local, aborted);
252
248 mutex_lock(&local->scan_mtx); 253 mutex_lock(&local->scan_mtx);
249 254
250 /* 255 /*
@@ -321,6 +326,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
321 326
322 ieee80211_offchannel_stop_beaconing(local); 327 ieee80211_offchannel_stop_beaconing(local);
323 328
329 local->leave_oper_channel_time = 0;
324 local->next_scan_state = SCAN_DECISION; 330 local->next_scan_state = SCAN_DECISION;
325 local->scan_channel_idx = 0; 331 local->scan_channel_idx = 0;
326 332
@@ -425,11 +431,28 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
425 return rc; 431 return rc;
426} 432}
427 433
434static unsigned long
435ieee80211_scan_get_channel_time(struct ieee80211_channel *chan)
436{
437 /*
438 * TODO: channel switching also consumes quite some time,
439 * add that delay as well to get a better estimation
440 */
441 if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
442 return IEEE80211_PASSIVE_CHANNEL_TIME;
443 return IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME;
444}
445
428static int ieee80211_scan_state_decision(struct ieee80211_local *local, 446static int ieee80211_scan_state_decision(struct ieee80211_local *local,
429 unsigned long *next_delay) 447 unsigned long *next_delay)
430{ 448{
431 bool associated = false; 449 bool associated = false;
450 bool tx_empty = true;
451 bool bad_latency;
452 bool listen_int_exceeded;
453 unsigned long min_beacon_int = 0;
432 struct ieee80211_sub_if_data *sdata; 454 struct ieee80211_sub_if_data *sdata;
455 struct ieee80211_channel *next_chan;
433 456
434 /* if no more bands/channels left, complete scan and advance to the idle state */ 457 /* 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) { 458 if (local->scan_channel_idx >= local->scan_req->n_channels) {
@@ -437,7 +460,11 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local,
437 return 1; 460 return 1;
438 } 461 }
439 462
440 /* check if at least one STA interface is associated */ 463 /*
464 * check if at least one STA interface is associated,
465 * check if at least one STA interface has pending tx frames
466 * and grab the lowest used beacon interval
467 */
441 mutex_lock(&local->iflist_mtx); 468 mutex_lock(&local->iflist_mtx);
442 list_for_each_entry(sdata, &local->interfaces, list) { 469 list_for_each_entry(sdata, &local->interfaces, list) {
443 if (!ieee80211_sdata_running(sdata)) 470 if (!ieee80211_sdata_running(sdata))
@@ -446,7 +473,16 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local,
446 if (sdata->vif.type == NL80211_IFTYPE_STATION) { 473 if (sdata->vif.type == NL80211_IFTYPE_STATION) {
447 if (sdata->u.mgd.associated) { 474 if (sdata->u.mgd.associated) {
448 associated = true; 475 associated = true;
449 break; 476
477 if (sdata->vif.bss_conf.beacon_int <
478 min_beacon_int || min_beacon_int == 0)
479 min_beacon_int =
480 sdata->vif.bss_conf.beacon_int;
481
482 if (!qdisc_all_tx_empty(sdata->dev)) {
483 tx_empty = false;
484 break;
485 }
450 } 486 }
451 } 487 }
452 } 488 }
@@ -455,11 +491,34 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local,
455 if (local->scan_channel) { 491 if (local->scan_channel) {
456 /* 492 /*
457 * we're currently scanning a different channel, let's 493 * we're currently scanning a different channel, let's
458 * switch back to the operating channel now if at least 494 * see if we can scan another channel without interfering
459 * one interface is associated. Otherwise just scan the 495 * with the current traffic situation.
460 * next channel 496 *
497 * Since we don't know if the AP has pending frames for us
498 * we can only check for our tx queues and use the current
499 * pm_qos requirements for rx. Hence, if no tx traffic occurs
500 * at all we will scan as many channels in a row as the pm_qos
501 * latency allows us to. Additionally we also check for the
502 * currently negotiated listen interval to prevent losing
503 * frames unnecessarily.
504 *
505 * Otherwise switch back to the operating channel.
461 */ 506 */
462 if (associated) 507 next_chan = local->scan_req->channels[local->scan_channel_idx];
508
509 bad_latency = time_after(jiffies +
510 ieee80211_scan_get_channel_time(next_chan),
511 local->leave_oper_channel_time +
512 usecs_to_jiffies(pm_qos_requirement(PM_QOS_NETWORK_LATENCY)));
513
514 listen_int_exceeded = time_after(jiffies +
515 ieee80211_scan_get_channel_time(next_chan),
516 local->leave_oper_channel_time +
517 usecs_to_jiffies(min_beacon_int * 1024) *
518 local->hw.conf.listen_interval);
519
520 if (associated && ( !tx_empty || bad_latency ||
521 listen_int_exceeded))
463 local->next_scan_state = SCAN_ENTER_OPER_CHANNEL; 522 local->next_scan_state = SCAN_ENTER_OPER_CHANNEL;
464 else 523 else
465 local->next_scan_state = SCAN_SET_CHANNEL; 524 local->next_scan_state = SCAN_SET_CHANNEL;
@@ -491,6 +550,9 @@ static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *loca
491 else 550 else
492 *next_delay = HZ / 10; 551 *next_delay = HZ / 10;
493 552
553 /* remember when we left the operating channel */
554 local->leave_oper_channel_time = jiffies;
555
494 /* advance to the next channel to be scanned */ 556 /* advance to the next channel to be scanned */
495 local->next_scan_state = SCAN_SET_CHANNEL; 557 local->next_scan_state = SCAN_SET_CHANNEL;
496} 558}