diff options
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/ieee80211_i.h | 1 | ||||
-rw-r--r-- | net/mac80211/scan.c | 71 |
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 | ||
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 | } |