1 /* 2 * Airtime policy configuration 3 * Copyright (c) 2018-2019, Toke Høiland-Jørgensen <toke@toke.dk> 4 * 5 * This software may be distributed under the terms of the BSD license. 6 * See README for more details. 7 */ 8 9 #include "utils/includes.h" 10 11 #include "utils/common.h" 12 #include "utils/eloop.h" 13 #include "hostapd.h" 14 #include "ap_drv_ops.h" 15 #include "sta_info.h" 16 #include "airtime_policy.h" 17 18 /* Idea: 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. 23 * 24 * - Periodic per-station callback to update queue status. 25 * 26 * Copy accounting_sta_update_stats() to get TXQ info and airtime weights and 27 * keep them updated in sta_info. 28 * 29 * - Separate periodic per-bss (or per-iface?) callback to update weights. 30 * 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 33 * weights. 34 */ 35 36 static int get_airtime_policy_update_timeout(struct hostapd_iface *iface, 37 unsigned int *sec, 38 unsigned int *usec) 39 { 40 unsigned int update_int = iface->conf->airtime_update_interval; 41 42 if (!update_int) { 43 wpa_printf(MSG_ERROR, 44 "Airtime policy: Invalid airtime policy update interval %u", 45 update_int); 46 return -1; 47 } 48 49 *sec = update_int / 1000; 50 *usec = (update_int % 1000) * 1000; 51 52 return 0; 53 } 54 55 56 static void set_new_backlog_time(struct hostapd_data *hapd, 57 struct sta_info *sta, 58 struct os_reltime *now) 59 { 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; 66 } 67 } 68 69 70 static void count_backlogged_sta(struct hostapd_data *hapd) 71 { 72 struct sta_info *sta; 73 struct hostap_sta_driver_data data = {}; 74 unsigned int num_backlogged = 0; 75 struct os_reltime now; 76 77 os_get_reltime(&now); 78 79 for (sta = hapd->sta_list; sta; sta = sta->next) { 80 if (hostapd_drv_read_sta_data(hapd, &data, sta->addr)) 81 continue; 82 #ifdef CONFIG_TESTING_OPTIONS 83 if (hapd->force_backlog_bytes) 84 data.backlog_bytes = 1; 85 #endif /* CONFIG_TESTING_OPTIONS */ 86 87 if (data.backlog_bytes > 0) 88 set_new_backlog_time(hapd, sta, &now); 89 if (os_reltime_before(&now, &sta->backlogged_until)) 90 num_backlogged++; 91 } 92 hapd->num_backlogged_sta = num_backlogged; 93 } 94 95 96 static int sta_set_airtime_weight(struct hostapd_data *hapd, 97 struct sta_info *sta, 98 unsigned int weight) 99 { 100 int ret = 0; 101 102 if (weight != sta->airtime_weight && 103 (ret = hostapd_sta_set_airtime_weight(hapd, sta->addr, weight))) 104 return ret; 105 106 sta->airtime_weight = weight; 107 return ret; 108 } 109 110 111 static void set_sta_weights(struct hostapd_data *hapd, unsigned int weight) 112 { 113 struct sta_info *sta; 114 115 for (sta = hapd->sta_list; sta; sta = sta->next) 116 sta_set_airtime_weight(hapd, sta, weight); 117 } 118 119 120 static unsigned int get_airtime_quantum(unsigned int max_wt) 121 { 122 unsigned int quantum = AIRTIME_QUANTUM_TARGET / max_wt; 123 124 if (quantum < AIRTIME_QUANTUM_MIN) 125 quantum = AIRTIME_QUANTUM_MIN; 126 else if (quantum > AIRTIME_QUANTUM_MAX) 127 quantum = AIRTIME_QUANTUM_MAX; 128 129 return quantum; 130 } 131 132 133 static void update_airtime_weights(void *eloop_data, void *user_data) 134 { 135 struct hostapd_iface *iface = eloop_data; 136 struct hostapd_data *bss; 137 unsigned int sec, usec; 138 unsigned int num_sta_min = 0, num_sta_prod = 1, num_sta_sum = 0, 139 wt_sum = 0; 140 unsigned int quantum; 141 bool all_div_min = true; 142 bool apply_limit = iface->conf->airtime_mode == AIRTIME_MODE_DYNAMIC; 143 int wt, num_bss = 0, max_wt = 0; 144 size_t i; 145 146 for (i = 0; i < iface->num_bss; i++) { 147 bss = iface->bss[i]; 148 if (!bss->started || !bss->conf->airtime_weight) 149 continue; 150 151 count_backlogged_sta(bss); 152 if (!bss->num_backlogged_sta) 153 continue; 154 155 if (!num_sta_min || bss->num_backlogged_sta < num_sta_min) 156 num_sta_min = bss->num_backlogged_sta; 157 158 num_sta_prod *= bss->num_backlogged_sta; 159 num_sta_sum += bss->num_backlogged_sta; 160 wt_sum += bss->conf->airtime_weight; 161 num_bss++; 162 } 163 164 if (num_sta_min) { 165 for (i = 0; i < iface->num_bss; i++) { 166 bss = iface->bss[i]; 167 if (!bss->started || !bss->conf->airtime_weight) 168 continue; 169 170 /* Check if we can divide all sta numbers by the 171 * smallest number to keep weights as small as possible. 172 * This is a lazy way to avoid having to factor 173 * integers. */ 174 if (bss->num_backlogged_sta && 175 bss->num_backlogged_sta % num_sta_min > 0) 176 all_div_min = false; 177 178 /* If we're in LIMIT mode, we only apply the weight 179 * scaling when the BSS(es) marked as limited would a 180 * larger share than the relative BSS weights indicates 181 * it should. */ 182 if (!apply_limit && bss->conf->airtime_limit) { 183 if (bss->num_backlogged_sta * wt_sum > 184 bss->conf->airtime_weight * num_sta_sum) 185 apply_limit = true; 186 } 187 } 188 if (all_div_min) 189 num_sta_prod /= num_sta_min; 190 } 191 192 for (i = 0; i < iface->num_bss; i++) { 193 bss = iface->bss[i]; 194 if (!bss->started || !bss->conf->airtime_weight) 195 continue; 196 197 /* We only set the calculated weight if the BSS has active 198 * stations and there are other active interfaces as well - 199 * otherwise we just set a unit weight. This ensures that 200 * the weights are set reasonably when stations transition from 201 * inactive to active. */ 202 if (apply_limit && bss->num_backlogged_sta && num_bss > 1) 203 wt = bss->conf->airtime_weight * num_sta_prod / 204 bss->num_backlogged_sta; 205 else 206 wt = 1; 207 208 bss->airtime_weight = wt; 209 if (wt > max_wt) 210 max_wt = wt; 211 } 212 213 quantum = get_airtime_quantum(max_wt); 214 215 for (i = 0; i < iface->num_bss; i++) { 216 bss = iface->bss[i]; 217 if (!bss->started || !bss->conf->airtime_weight) 218 continue; 219 set_sta_weights(bss, bss->airtime_weight * quantum); 220 } 221 222 if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0) 223 return; 224 225 eloop_register_timeout(sec, usec, update_airtime_weights, iface, 226 NULL); 227 } 228 229 230 static int get_weight_for_sta(struct hostapd_data *hapd, const u8 *sta) 231 { 232 struct airtime_sta_weight *wt; 233 234 wt = hapd->conf->airtime_weight_list; 235 while (wt && os_memcmp(wt->addr, sta, ETH_ALEN) != 0) 236 wt = wt->next; 237 238 return wt ? wt->weight : hapd->conf->airtime_weight; 239 } 240 241 242 int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta) 243 { 244 unsigned int weight; 245 246 if (hapd->iconf->airtime_mode == AIRTIME_MODE_STATIC) { 247 weight = get_weight_for_sta(hapd, sta->addr); 248 if (weight) 249 return sta_set_airtime_weight(hapd, sta, weight); 250 } 251 return 0; 252 } 253 254 255 int airtime_policy_update_init(struct hostapd_iface *iface) 256 { 257 unsigned int sec, usec; 258 259 if (iface->conf->airtime_mode < AIRTIME_MODE_DYNAMIC) 260 return 0; 261 262 if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0) 263 return -1; 264 265 eloop_register_timeout(sec, usec, update_airtime_weights, iface, NULL); 266 return 0; 267 } 268 269 270 void airtime_policy_update_deinit(struct hostapd_iface *iface) 271 { 272 eloop_cancel_timeout(update_airtime_weights, iface, NULL); 273 } 274