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
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 sta_info *si)
+usteer_roam_trigger_sm(struct usteer_local_node *ln, struct sta_info *si)
{
+ struct sta_info *candidate;
struct uevent ev = {
.si_cur = si,
};
- 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 (config.roam_scan_tries && si->roam_tries >= config.roam_scan_tries) {
if (!config.roam_scan_timeout) {
/* Prepare to kick client */
- usteer_roam_set_state(si, ROAM_TRIGGER_WAIT_KICK, &ev);
+ usteer_roam_set_state(si, ROAM_TRIGGER_SCAN_DONE, &ev);
} else {
/* Kick in scan timeout */
si->roam_scan_timeout_start = current_time;
break;
case ROAM_TRIGGER_SCAN_DONE:
- if (usteer_roam_sm_found_better_node(si, &ev, ROAM_TRIGGER_WAIT_KICK))
- break;
-
- /* Kick back to SCAN state if candidate expired */
- usteer_roam_sm_start_scan(si, &ev);
- break;
-
- case ROAM_TRIGGER_WAIT_KICK:
- if (si->signal > min_signal)
- break;
-
- usteer_roam_set_state(si, ROAM_TRIGGER_NOTIFY_KICK, &ev);
- usteer_ubus_notify_client_disassoc(si);
- break;
- case ROAM_TRIGGER_NOTIFY_KICK:
- if (current_time - si->roam_event < config.roam_kick_delay * 100)
+ 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_roam_set_state(si, ROAM_TRIGGER_KICK, &ev);
- break;
- case ROAM_TRIGGER_KICK:
- usteer_ubus_kick_client(si);
+ 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);
- return true;
+ break;
}
return false;
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) {
usteer_roam_set_state(si, ROAM_TRIGGER_IDLE, ev);
continue;
* If the state machine kicked a client, other clients should wait
* until the next turn
*/
- if (usteer_roam_trigger_sm(si))
+ if (usteer_roam_trigger_sm(ln, si))
return;
}
}
}
}
-void
-usteer_local_node_kick(struct usteer_local_node *ln)
+static void
+usteer_local_node_load_kick(struct usteer_local_node *ln)
{
struct usteer_node *node = &ln->node;
struct sta_info *kick1 = NULL, *kick2 = NULL;
};
unsigned int min_count = DIV_ROUND_UP(config.load_kick_delay, config.local_sta_update);
- usteer_local_node_roam_check(ln, &ev);
- usteer_local_node_snr_kick(ln);
-
if (!config.load_kick_enabled || !config.load_kick_threshold ||
!config.load_kick_delay)
return;
out:
usteer_event(&ev);
}
+
+static void
+usteer_local_node_perform_kick(struct usteer_local_node *ln)
+{
+ struct sta_info *si;
+
+ list_for_each_entry(si, &ln->node.sta_info, node_list) {
+ if (!si->kick_time || si->kick_time > current_time)
+ continue;
+
+ usteer_ubus_kick_client(si);
+ }
+}
+
+void
+usteer_local_node_kick(struct usteer_local_node *ln)
+{
+ struct uevent ev = {
+ .node_local = &ln->node,
+ };
+
+ usteer_local_node_perform_kick(ln);
+
+ usteer_local_node_snr_kick(ln);
+ usteer_local_node_load_kick(ln);
+ usteer_local_node_roam_check(ln, &ev);
+}