diff --git a/src/scan.c b/src/scan.c index 64de57d2..aeab6516 100644 --- a/src/scan.c +++ b/src/scan.c @@ -56,6 +56,8 @@ static double RANK_2G_FACTOR; static double RANK_5G_FACTOR; static double RANK_6G_FACTOR; +static uint32_t RANK_HIGH_UTILIZATION; +static uint32_t RANK_HIGH_STATION_COUNT; static uint32_t SCAN_MAX_INTERVAL; static uint32_t SCAN_INIT_INTERVAL; @@ -1681,10 +1683,77 @@ static struct scan_bss *scan_parse_result(struct l_genl_msg *msg, return bss; } -static void scan_bss_compute_rank(struct scan_bss *bss) +static double scan_legacy_utilization_factor(uint8_t utilization) { static const double RANK_HIGH_UTILIZATION_FACTOR = 0.8; static const double RANK_LOW_UTILIZATION_FACTOR = 1.2; + + if (utilization >= 192) + return RANK_HIGH_UTILIZATION_FACTOR; + else if (utilization <= 63) + return RANK_LOW_UTILIZATION_FACTOR; + + return 1.0; +} + +static double scan_get_load_factors(uint8_t utilization, uint16_t sta_count) +{ + double n; + double factor = 1.0; + /* + * The exponential decay table has 64 entries (0 <= n <= 63) which range + * from 1.0 to 0.28. For the utilization and station count factors we + * likely don't want to adjust the rank so drastically (potentially a + * 78% reduction in the worse case) so cap the index at 30 which equates + * to ~64% at the worst case. + */ + static const uint8_t LOAD_DECAY_OFFSET = 30; + + if (!RANK_HIGH_UTILIZATION) { + /* + * To maintain existing behavior, if the utilization factor is + * unset (default) fall back to the static thresholds and + * factor weights. + */ + factor = scan_legacy_utilization_factor(utilization); + goto sta_count; + } else if (utilization < RANK_HIGH_UTILIZATION) + goto sta_count; + + /* Map the utilization threshold -> 255 to rankmod_table indexes */ + if (L_WARN_ON(!util_linear_map(utilization, RANK_HIGH_UTILIZATION, + 255, 0, LOAD_DECAY_OFFSET, &n))) + goto sta_count; + + factor = util_exponential_decay(n); + +sta_count: + if (!RANK_HIGH_STATION_COUNT || sta_count < RANK_HIGH_STATION_COUNT) + return factor; + + /* + * The station count is a uint16 so in theory it could be excessively + * large. In practice APs generally can't handle anywhere near this so + * put a cap at 255. If at some time in the future APs begin to handle + * this level of capacity we could increase this. + * + * TODO: A warning is used here to make this visible. If we see cases + * where this is happening we may need to give this another look. + */ + if (L_WARN_ON(sta_count > 255)) + sta_count = 255; + + if (L_WARN_ON(!util_linear_map(sta_count, RANK_HIGH_STATION_COUNT, + 255, 0, LOAD_DECAY_OFFSET, &n))) + return factor; + + factor *= util_exponential_decay(n); + + return factor; +} + +static void scan_bss_compute_rank(struct scan_bss *bss) +{ static const double RANK_HIGH_SNR_FACTOR = 1.2; static const double RANK_LOW_SNR_FACTOR = 0.8; double rank; @@ -1708,12 +1777,8 @@ static void scan_bss_compute_rank(struct scan_bss *bss) rank *= RANK_6G_FACTOR; /* Rank loaded APs lower and lightly loaded APs higher */ - if (bss->have_utilization) { - if (bss->utilization >= 192) - rank *= RANK_HIGH_UTILIZATION_FACTOR; - else if (bss->utilization <= 63) - rank *= RANK_LOW_UTILIZATION_FACTOR; - } + if (bss->have_utilization) + rank *= scan_get_load_factors(bss->utilization, bss->sta_count); if (bss->have_snr) { if (bss->snr <= 15) @@ -2599,6 +2664,20 @@ static int scan_init(void) if (SCAN_MAX_INTERVAL > UINT16_MAX) SCAN_MAX_INTERVAL = UINT16_MAX; + if (!l_settings_get_uint(config, "Rank", "HighUtilizationThreshold", + &RANK_HIGH_UTILIZATION)) + RANK_HIGH_UTILIZATION = 0; + + if (L_WARN_ON(RANK_HIGH_UTILIZATION > 255)) + RANK_HIGH_UTILIZATION = 255; + + if (!l_settings_get_uint(config, "Rank", "HighStationCountThreshold", + &RANK_HIGH_STATION_COUNT)) + RANK_HIGH_STATION_COUNT = 0; + + if (L_WARN_ON(RANK_HIGH_STATION_COUNT > 255)) + RANK_HIGH_STATION_COUNT = 255; + return 0; }