diff options
Diffstat (limited to 'net/mac80211/scan.c')
-rw-r--r-- | net/mac80211/scan.c | 74 |
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 | ||
434 | static unsigned long | ||
435 | ieee80211_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 | |||
428 | static int ieee80211_scan_state_decision(struct ieee80211_local *local, | 446 | static 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 | } |