policy: don't track roam_scan_done
authorDavid Bauer <mail@david-bauer.net>
Wed, 22 Dec 2021 21:28:49 +0000 (22:28 +0100)
committerDavid Bauer <mail@david-bauer.net>
Sun, 23 Jan 2022 19:55:41 +0000 (20:55 +0100)
Don't determine a finished client scan based on whether the current node
has seen a beacon or not.

This works surprisingly bad on 5 GHz nodes, as clients may refuse to
perform active scanning on this frequency band. In case the clients does
refuse to scan the 5 GHz band but scans the 2.4 GHz band actively, we
might have a good indication about a better node on this band at least.

However, as the roam state-machine requires to have seen a probe request
from the client to direct him to a better node, thi process will not
continue, which either ends the node in being kicked due to exceeding
the number of max tries or the scan cooldown to kick in.

The solution to this is fairly simple: Don't track the roam_scan_done
state but instead just determine a better candidate after the scan
interval finished.

To ensure the indicated signal levels are as recent as 2 *
scan-interval, add a max_age parameter to find_better_candidate.

Signed-off-by: David Bauer <mail@david-bauer.net>
policy.c
sta.c
usteer.h

index 67c9c75be2b255302b8cb445fea09dc10d998c4e..9d03b30b9ba23ea6e32f44ccf939647ae82d3d00 100644 (file)
--- a/policy.c
+++ b/policy.c
@@ -110,7 +110,7 @@ is_better_candidate(struct sta_info *si_cur, struct sta_info *si_new)
 }
 
 static struct sta_info *
-find_better_candidate(struct sta_info *si_ref, struct uevent *ev, uint32_t required_criteria)
+find_better_candidate(struct sta_info *si_ref, struct uevent *ev, uint32_t required_criteria, uint64_t max_age)
 {
        struct sta_info *si;
        struct sta *sta = si_ref->sta;
@@ -126,6 +126,9 @@ find_better_candidate(struct sta_info *si_ref, struct uevent *ev, uint32_t requi
                if (strcmp(si->node->ssid, si_ref->node->ssid) != 0)
                        continue;
 
+               if (max_age && max_age < current_time - si->seen)
+                       continue;
+
                reasons = is_better_candidate(si_ref, si);
                if (!reasons)
                        continue;
@@ -204,7 +207,7 @@ usteer_check_request(struct sta_info *si, enum usteer_event_type type)
                goto out;
        }
 
-       if (!find_better_candidate(si, &ev, UEV_SELECT_REASON_ALL))
+       if (!find_better_candidate(si, &ev, UEV_SELECT_REASON_ALL, 0))
                goto out;
 
        ev.reason = UEV_REASON_BETTER_CANDIDATE;
@@ -283,7 +286,7 @@ usteer_roam_sm_start_scan(struct sta_info *si, struct uevent *ev)
        }
 
        /* We are currently in scan timeout / cooldown.
-        * Check if we are in ROAM_TRIGGER_IDLE state and enter this stateif not.
+        * Check if we are in ROAM_TRIGGER_IDLE state. Enter this state if not.
         */
        if (si->roam_state == ROAM_TRIGGER_IDLE)
                return;
@@ -292,27 +295,47 @@ usteer_roam_sm_start_scan(struct sta_info *si, struct uevent *ev)
        usteer_roam_set_state(si, ROAM_TRIGGER_IDLE, ev);
 }
 
+static bool
+usteer_roam_sm_found_better_node(struct sta_info *si, struct uevent *ev, enum roam_trigger_state next_state)
+{
+       uint64_t max_age = 2 * config.roam_scan_interval;
+
+       if (max_age > current_time - si->roam_scan_start)
+               max_age = current_time - si->roam_scan_start;
+
+       if (find_better_candidate(si, ev, (1 << UEV_SELECT_REASON_SIGNAL), max_age)) {
+               usteer_roam_set_state(si, next_state, ev);
+               return true;
+       }
+
+       return false;
+}
+
 static bool
 usteer_roam_trigger_sm(struct sta_info *si)
 {
        struct uevent ev = {
                .si_cur = si,
        };
-       int min_signal;
+       uint64_t min_signal;
 
        min_signal = usteer_snr_to_signal(si->node, config.roam_trigger_snr);
 
        switch (si->roam_state) {
        case ROAM_TRIGGER_SCAN:
-               if (current_time - si->roam_event < config.roam_scan_interval)
+               if (!si->roam_tries) {
+                       si->roam_scan_start = current_time;
+               }
+
+               /* Check if we've found a better node regardless of the scan-interval */
+               if (usteer_roam_sm_found_better_node(si, &ev, ROAM_TRIGGER_SCAN_DONE))
                        break;
 
-               if (find_better_candidate(si, &ev, (1 << UEV_SELECT_REASON_SIGNAL)) ||
-                   si->roam_scan_done > si->roam_event) {
-                       usteer_roam_set_state(si, ROAM_TRIGGER_SCAN_DONE, &ev);
+               /* Only scan every scan-interval */
+               if (current_time - si->roam_event < config.roam_scan_interval)
                        break;
-               }
 
+               /* Check if no node was found within roam_scan_tries tries */
                if (config.roam_scan_tries && si->roam_tries >= config.roam_scan_tries) {
                        if (!config.roam_scan_timeout) {
                                /* Prepare to kick client */
@@ -325,29 +348,21 @@ usteer_roam_trigger_sm(struct sta_info *si)
                        break;
                }
 
+               /* Send beacon-request to client */
                usteer_ubus_trigger_client_scan(si);
                usteer_roam_sm_start_scan(si, &ev);
                break;
 
        case ROAM_TRIGGER_IDLE:
-               if (find_better_candidate(si, &ev, (1 << UEV_SELECT_REASON_SIGNAL))) {
-                       usteer_roam_set_state(si, ROAM_TRIGGER_SCAN_DONE, &ev);
-                       break;
-               }
-
                usteer_roam_sm_start_scan(si, &ev);
                break;
 
        case ROAM_TRIGGER_SCAN_DONE:
-               /* Check for stale scan results, kick back to SCAN state if necessary */
-               if (current_time - si->roam_scan_done > 2 * config.roam_scan_interval) {
-                       usteer_roam_sm_start_scan(si, &ev);
+               if (usteer_roam_sm_found_better_node(si, &ev, ROAM_TRIGGER_WAIT_KICK))
                        break;
-               }
-
-               if (find_better_candidate(si, &ev, (1 << UEV_SELECT_REASON_SIGNAL)))
-                       usteer_roam_set_state(si, ROAM_TRIGGER_WAIT_KICK, &ev);
 
+               /* Kick back to SCAN state if candidate expired */
+               usteer_roam_sm_start_scan(si, &ev);
                break;
 
        case ROAM_TRIGGER_WAIT_KICK:
@@ -495,7 +510,7 @@ usteer_local_node_kick(struct usteer_local_node *ln)
                if (is_more_kickable(kick1, si))
                        kick1 = si;
 
-               tmp = find_better_candidate(si, NULL, (1 << UEV_SELECT_REASON_LOAD));
+               tmp = find_better_candidate(si, NULL, (1 << UEV_SELECT_REASON_LOAD), 0);
                if (!tmp)
                        continue;
 
diff --git a/sta.c b/sta.c
index 0ec6a8a4d9c4670547c15bd9d42ae4bc31a02a61..2868a67652cdf1dca805d7c4909aca6ce42d0ebc 100644 (file)
--- a/sta.c
+++ b/sta.c
@@ -186,7 +186,6 @@ usteer_handle_sta_event(struct usteer_node *node, const uint8_t *addr,
 
        si = usteer_sta_info_get(sta, node, &create);
        usteer_sta_info_update(si, signal, false);
-       si->roam_scan_done = current_time;
        si->stats[type].requests++;
 
        diff = si->stats[type].blocked_last_time - current_time;
index 280fdb2e4fa1e658bf008ebdea417ac7e3c35e41..f47f53efb8f457609f62999b5bf89071a0a71d1f 100644 (file)
--- a/usteer.h
+++ b/usteer.h
@@ -229,7 +229,7 @@ struct sta_info {
        uint8_t roam_tries;
        uint64_t roam_event;
        uint64_t roam_kick;
-       uint64_t roam_scan_done;
+       uint64_t roam_scan_start;
        uint64_t roam_scan_timeout_start;
 
        int kick_count;