bool ref_5g = node_cur->freq > 4000;
bool node_5g = node_new->freq > 4000;
+ if (!config.load_balancing_threshold)
+ return false;
+
if (ref_5g && !node_5g)
n_assoc_new += config.band_steering_threshold;
else if (!ref_5g && node_5g)
static struct sta_info *
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_info *si, *candidate = NULL;
struct sta *sta = si_ref->sta;
uint32_t reasons;
ev->select_reasons = reasons;
}
- return si;
+ if (!candidate || si->signal > candidate->signal)
+ candidate = si;
}
- return NULL;
+ return candidate;
}
int
int min_signal;
bool ret = true;
+ if (type == EVENT_TYPE_PROBE && !config.probe_steering)
+ goto out;
+
if (type == EVENT_TYPE_AUTH)
goto out;
out:
switch (type) {
case EVENT_TYPE_PROBE:
- ev.type = UEV_PROBE_REQ_ACCEPT;
+ ev.type = ret ? UEV_PROBE_REQ_ACCEPT : UEV_PROBE_REQ_DENY;
break;
case EVENT_TYPE_ASSOC:
- ev.type = UEV_ASSOC_REQ_ACCEPT;
+ ev.type = ret ? UEV_ASSOC_REQ_ACCEPT : UEV_ASSOC_REQ_DENY;
break;
case EVENT_TYPE_AUTH:
- ev.type = UEV_AUTH_REQ_ACCEPT;
+ ev.type = ret ? UEV_AUTH_REQ_ACCEPT : UEV_AUTH_REQ_DENY;
break;
default:
break;
}
- if (!ret)
- ev.type++;
-
if (!ret && si->stats[type].blocked_cur >= config.max_retry_band) {
ev.reason = UEV_REASON_RETRY_EXCEEDED;
ev.threshold.cur = si->stats[type].blocked_cur;
usteer_roam_set_state(struct sta_info *si, enum roam_trigger_state state,
struct uevent *ev)
{
+ /* NOP in case we remain idle */
+ if (si->roam_state == state && si->roam_state == ROAM_TRIGGER_IDLE) {
+ si->roam_tries = 0;
+ return;
+ }
+
si->roam_event = current_time;
if (si->roam_state == state) {
- if (si->roam_state == ROAM_TRIGGER_IDLE) {
- si->roam_tries = 0;
- return;
- }
-
si->roam_tries++;
} else {
si->roam_tries = 0;
usteer_roam_set_state(si, ROAM_TRIGGER_IDLE, ev);
}
-static bool
+static struct sta_info *
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;
+ struct sta_info *candidate;
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)) {
+ candidate = find_better_candidate(si, ev, (1 << UEV_SELECT_REASON_SIGNAL), max_age);
+ if (candidate)
usteer_roam_set_state(si, next_state, ev);
- return true;
- }
- return false;
+ return candidate;
}
static bool
usteer_roam_trigger_sm(struct usteer_local_node *ln, struct sta_info *si)
{
+ struct sta_info *candidate;
struct uevent ev = {
.si_cur = si,
};
break;
case ROAM_TRIGGER_SCAN_DONE:
- usteer_ubus_bss_transition_request(si, 1, false, false, 100);
- si->kick_time = current_time;
+ candidate = usteer_roam_sm_found_better_node(si, &ev, ROAM_TRIGGER_SCAN_DONE);
+ /* Kick back in case no better node is found */
+ if (!candidate) {
+ usteer_roam_set_state(si, ROAM_TRIGGER_IDLE, &ev);
+ break;
+ }
+
+ usteer_ubus_bss_transition_request(si, 1, false, false, 100, candidate->node);
+ si->kick_time = current_time + config.roam_kick_delay;
usteer_roam_set_state(si, ROAM_TRIGGER_IDLE, &ev);
break;
}
return false;
}
+bool usteer_policy_can_perform_roam(struct sta_info *si)
+{
+ /* Only trigger for connected STAs */
+ if (si->connected != STA_CONNECTED)
+ return false;
+
+ /* Skip on pending kick */
+ if (si->kick_time)
+ return false;
+
+ /* Skip on rejected transition */
+ if (si->bss_transition_response.status_code && current_time - si->bss_transition_response.timestamp < config.steer_reject_timeout)
+ return false;
+
+ /* Skip on previous kick attempt */
+ if (current_time - si->roam_kick < config.roam_trigger_interval)
+ return false;
+
+ /* Skip if connection is established shorter than the trigger-interval */
+ if (current_time - si->connected_since < config.roam_trigger_interval)
+ return false;
+
+ return true;
+}
+
+static bool
+usteer_local_node_roam_sm_active(struct sta_info *si, int min_signal)
+{
+ if (!usteer_policy_can_perform_roam(si))
+ return false;
+
+ /* Signal has to be below scan / roam threshold */
+ if (si->signal >= min_signal)
+ return false;
+
+ return true;
+}
+
static void
usteer_local_node_roam_check(struct usteer_local_node *ln, struct uevent *ev)
{
min_signal = usteer_snr_to_signal(&ln->node, min_signal);
list_for_each_entry(si, &ln->node.sta_info, node_list) {
- if (si->connected != STA_CONNECTED || si->signal >= min_signal ||
- si->kick_time ||
- (si->bss_transition_response.status_code && current_time - si->bss_transition_response.timestamp < config.steer_reject_timeout) ||
- current_time - si->roam_kick < config.roam_trigger_interval) {
+ if (!usteer_local_node_roam_sm_active(si, min_signal)) {
usteer_roam_set_state(si, ROAM_TRIGGER_IDLE, ev);
continue;
}
if (si->below_min_snr <= min_count)
continue;
- si->kick_count++;
-
ev.type = UEV_SIGNAL_KICK;
ev.threshold.cur = si->signal;
ev.count = si->kick_count;