2 * Airtime policy configuration
3 * Copyright (c) 2018-2019, Toke Høiland-Jørgensen <toke@toke.dk>
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
9 #include "utils/includes.h"
11 #include "utils/common.h"
12 #include "utils/eloop.h"
14 #include "ap_drv_ops.h"
16 #include "airtime_policy.h"
19 * Two modes of airtime enforcement:
20 * 1. Static weights: specify weights per MAC address with a per-BSS default
21 * 2. Per-BSS limits: Dynamically calculate weights of backlogged stations to
22 * enforce relative total shares between BSSes.
24 * - Periodic per-station callback to update queue status.
26 * Copy accounting_sta_update_stats() to get TXQ info and airtime weights and
27 * keep them updated in sta_info.
29 * - Separate periodic per-bss (or per-iface?) callback to update weights.
31 * Just need to loop through all interfaces, count sum the active stations (or
32 * should the per-STA callback just adjust that for the BSS?) and calculate new
36 static int get_airtime_policy_update_timeout(struct hostapd_iface *iface,
40 unsigned int update_int = iface->conf->airtime_update_interval;
44 "Airtime policy: Invalid airtime policy update interval %u",
49 *sec = update_int / 1000;
50 *usec = (update_int % 1000) * 1000;
56 static void set_new_backlog_time(struct hostapd_data *hapd,
58 struct os_reltime *now)
60 sta->backlogged_until = *now;
61 sta->backlogged_until.usec += hapd->iconf->airtime_update_interval *
62 AIRTIME_BACKLOG_EXPIRY_FACTOR;
63 while (sta->backlogged_until.usec >= 1000000) {
64 sta->backlogged_until.sec++;
65 sta->backlogged_until.usec -= 1000000;
70 static void count_backlogged_sta(struct hostapd_data *hapd)
73 struct hostap_sta_driver_data data = {};
74 unsigned int num_backlogged = 0;
75 struct os_reltime now;
79 for (sta = hapd->sta_list; sta; sta = sta->next) {
80 if (hostapd_drv_read_sta_data(hapd, &data, sta->addr))
83 if (data.backlog_bytes > 0)
84 set_new_backlog_time(hapd, sta, &now);
85 if (os_reltime_before(&now, &sta->backlogged_until))
88 hapd->num_backlogged_sta = num_backlogged;
92 static int sta_set_airtime_weight(struct hostapd_data *hapd,
98 if (weight != sta->airtime_weight &&
99 (ret = hostapd_sta_set_airtime_weight(hapd, sta->addr, weight)))
102 sta->airtime_weight = weight;
107 static void set_sta_weights(struct hostapd_data *hapd, unsigned int weight)
109 struct sta_info *sta;
111 for (sta = hapd->sta_list; sta; sta = sta->next)
112 sta_set_airtime_weight(hapd, sta, weight);
116 static unsigned int get_airtime_quantum(unsigned int max_wt)
118 unsigned int quantum = AIRTIME_QUANTUM_TARGET / max_wt;
120 if (quantum < AIRTIME_QUANTUM_MIN)
121 quantum = AIRTIME_QUANTUM_MIN;
122 else if (quantum > AIRTIME_QUANTUM_MAX)
123 quantum = AIRTIME_QUANTUM_MAX;
129 static void update_airtime_weights(void *eloop_data, void *user_data)
131 struct hostapd_iface *iface = eloop_data;
132 struct hostapd_data *bss;
133 unsigned int sec, usec;
134 unsigned int num_sta_min = 0, num_sta_prod = 1, num_sta_sum = 0,
136 unsigned int quantum;
137 Boolean all_div_min = TRUE;
138 Boolean apply_limit = iface->conf->airtime_mode == AIRTIME_MODE_DYNAMIC;
139 int wt, num_bss = 0, max_wt = 0;
142 for (i = 0; i < iface->num_bss; i++) {
144 if (!bss->started || !bss->conf->airtime_weight)
147 count_backlogged_sta(bss);
148 if (!bss->num_backlogged_sta)
151 if (!num_sta_min || bss->num_backlogged_sta < num_sta_min)
152 num_sta_min = bss->num_backlogged_sta;
154 num_sta_prod *= bss->num_backlogged_sta;
155 num_sta_sum += bss->num_backlogged_sta;
156 wt_sum += bss->conf->airtime_weight;
161 for (i = 0; i < iface->num_bss; i++) {
163 if (!bss->started || !bss->conf->airtime_weight)
166 /* Check if we can divide all sta numbers by the
167 * smallest number to keep weights as small as possible.
168 * This is a lazy way to avoid having to factor
170 if (bss->num_backlogged_sta &&
171 bss->num_backlogged_sta % num_sta_min > 0)
174 /* If we're in LIMIT mode, we only apply the weight
175 * scaling when the BSS(es) marked as limited would a
176 * larger share than the relative BSS weights indicates
178 if (!apply_limit && bss->conf->airtime_limit) {
179 if (bss->num_backlogged_sta * wt_sum >
180 bss->conf->airtime_weight * num_sta_sum)
185 num_sta_prod /= num_sta_min;
188 for (i = 0; i < iface->num_bss; i++) {
190 if (!bss->started || !bss->conf->airtime_weight)
193 /* We only set the calculated weight if the BSS has active
194 * stations and there are other active interfaces as well -
195 * otherwise we just set a unit weight. This ensures that
196 * the weights are set reasonably when stations transition from
197 * inactive to active. */
198 if (apply_limit && bss->num_backlogged_sta && num_bss > 1)
199 wt = bss->conf->airtime_weight * num_sta_prod /
200 bss->num_backlogged_sta;
204 bss->airtime_weight = wt;
209 quantum = get_airtime_quantum(max_wt);
211 for (i = 0; i < iface->num_bss; i++) {
213 if (!bss->started || !bss->conf->airtime_weight)
215 set_sta_weights(bss, bss->airtime_weight * quantum);
218 if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0)
221 eloop_register_timeout(sec, usec, update_airtime_weights, iface,
226 static int get_weight_for_sta(struct hostapd_data *hapd, const u8 *sta)
228 struct airtime_sta_weight *wt;
230 wt = hapd->conf->airtime_weight_list;
231 while (wt && os_memcmp(wt->addr, sta, ETH_ALEN) != 0)
234 return wt ? wt->weight : hapd->conf->airtime_weight;
238 int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta)
242 if (hapd->iconf->airtime_mode == AIRTIME_MODE_STATIC) {
243 weight = get_weight_for_sta(hapd, sta->addr);
245 return sta_set_airtime_weight(hapd, sta, weight);
251 int airtime_policy_update_init(struct hostapd_iface *iface)
253 unsigned int sec, usec;
255 if (iface->conf->airtime_mode < AIRTIME_MODE_DYNAMIC)
258 if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0)
261 eloop_register_timeout(sec, usec, update_airtime_weights, iface, NULL);
266 void airtime_policy_update_deinit(struct hostapd_iface *iface)
268 eloop_cancel_timeout(update_airtime_weights, iface, NULL);