xref: /freebsd/sys/net80211/ieee80211_amrr.c (revision 1a3c03d88aaf380e9b8bcb5efd3c8c3e84f721b1)
1 /*	$OpenBSD: ieee80211_amrr.c,v 1.1 2006/06/17 19:07:19 damien Exp $	*/
2 
3 /*-
4  * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org>
5  * Copyright (c) 2006
6  *	Damien Bergamini <damien.bergamini@free.fr>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <sys/cdefs.h>
22 /*-
23  * Naive implementation of the Adaptive Multi Rate Retry algorithm:
24  *
25  * "IEEE 802.11 Rate Adaptation: A Practical Approach"
26  *  Mathieu Lacage, Hossein Manshaei, Thierry Turletti
27  *  INRIA Sophia - Projet Planete
28  *  http://www-sop.inria.fr/rapports/sophia/RR-5208.html
29  */
30 #include "opt_wlan.h"
31 
32 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/malloc.h>
35 #include <sys/module.h>
36 #include <sys/sbuf.h>
37 #include <sys/socket.h>
38 #include <sys/sysctl.h>
39 
40 #include <net/if.h>
41 #include <net/if_var.h>
42 #include <net/if_media.h>
43 #include <net/ethernet.h>
44 
45 #ifdef INET
46 #include <netinet/in.h>
47 #include <netinet/if_ether.h>
48 #endif
49 
50 #include <net80211/ieee80211_var.h>
51 #include <net80211/ieee80211_ht.h>
52 #include <net80211/ieee80211_vht.h>
53 #include <net80211/ieee80211_amrr.h>
54 #include <net80211/ieee80211_ratectl.h>
55 
56 #define is_success(amn)	\
57 	((amn)->amn_retrycnt < (amn)->amn_txcnt / 10)
58 #define is_failure(amn)	\
59 	((amn)->amn_retrycnt > (amn)->amn_txcnt / 3)
60 #define is_enough(amn)		\
61 	((amn)->amn_txcnt > 10)
62 
63 static void	amrr_setinterval(const struct ieee80211vap *, int);
64 static void	amrr_init(struct ieee80211vap *);
65 static void	amrr_deinit(struct ieee80211vap *);
66 static void	amrr_node_init(struct ieee80211_node *);
67 static void	amrr_node_deinit(struct ieee80211_node *);
68 static int	amrr_update(struct ieee80211_amrr *,
69     			struct ieee80211_amrr_node *, struct ieee80211_node *);
70 static int	amrr_rate(struct ieee80211_node *, void *, uint32_t);
71 static void	amrr_tx_complete(const struct ieee80211_node *,
72 			const struct ieee80211_ratectl_tx_status *);
73 static void	amrr_tx_update_cb(void *, struct ieee80211_node *);
74 static void	amrr_tx_update(struct ieee80211vap *vap,
75 			struct ieee80211_ratectl_tx_stats *);
76 static void	amrr_sysctlattach(struct ieee80211vap *,
77 			struct sysctl_ctx_list *, struct sysctl_oid *);
78 static void	amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s);
79 
80 /* number of references from net80211 layer */
81 static	int nrefs = 0;
82 
83 static const struct ieee80211_ratectl amrr = {
84 	.ir_name	= "amrr",
85 	.ir_attach	= NULL,
86 	.ir_detach	= NULL,
87 	.ir_init	= amrr_init,
88 	.ir_deinit	= amrr_deinit,
89 	.ir_node_init	= amrr_node_init,
90 	.ir_node_deinit	= amrr_node_deinit,
91 	.ir_rate	= amrr_rate,
92 	.ir_tx_complete	= amrr_tx_complete,
93 	.ir_tx_update	= amrr_tx_update,
94 	.ir_setinterval	= amrr_setinterval,
95 	.ir_node_stats	= amrr_node_stats,
96 };
97 IEEE80211_RATECTL_MODULE(amrr, 1);
98 IEEE80211_RATECTL_ALG(amrr, IEEE80211_RATECTL_AMRR, amrr);
99 
100 static void
amrr_setinterval(const struct ieee80211vap * vap,int msecs)101 amrr_setinterval(const struct ieee80211vap *vap, int msecs)
102 {
103 	struct ieee80211_amrr *amrr = vap->iv_rs;
104 
105 	if (!amrr)
106 		return;
107 
108 	if (msecs < 100)
109 		msecs = 100;
110 	amrr->amrr_interval = msecs_to_ticks(msecs);
111 }
112 
113 static void
amrr_init(struct ieee80211vap * vap)114 amrr_init(struct ieee80211vap *vap)
115 {
116 	struct ieee80211_amrr *amrr;
117 
118 	KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__));
119 
120 	nrefs++;		/* XXX locking */
121 	amrr = vap->iv_rs = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr),
122 	    M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
123 	if (amrr == NULL) {
124 		net80211_vap_printf(vap, "couldn't alloc ratectl structure\n");
125 		return;
126 	}
127 	amrr->amrr_min_success_threshold = IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD;
128 	amrr->amrr_max_success_threshold = IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD;
129 	amrr_setinterval(vap, 500 /* ms */);
130 	amrr_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid);
131 }
132 
133 static void
amrr_deinit(struct ieee80211vap * vap)134 amrr_deinit(struct ieee80211vap *vap)
135 {
136 	KASSERT(nrefs > 0, ("imbalanced attach/detach"));
137 	IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL);
138 	vap->iv_rs = NULL;	/* guard */
139 	nrefs--;		/* XXX locking */
140 }
141 
142 static void
amrr_node_init_vht(struct ieee80211_node * ni)143 amrr_node_init_vht(struct ieee80211_node *ni)
144 {
145 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
146 
147 	/* Default to VHT NSS 1 MCS 2; should be reliable! */
148 	amn->amn_vht_mcs = 2;
149 	amn->amn_vht_nss = 1;
150 	ieee80211_node_set_txrate_vht_rate(ni, amn->amn_vht_nss,
151 	    amn->amn_vht_mcs);
152 
153 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
154 	    "AMRR: VHT: initial rate NSS %d MCS %d",
155 	    amn->amn_vht_nss,
156 	    amn->amn_vht_mcs);
157 }
158 
159 static void
amrr_node_init_ht(struct ieee80211_node * ni)160 amrr_node_init_ht(struct ieee80211_node *ni)
161 {
162 	const struct ieee80211_rateset *rs;
163 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
164 	uint8_t rate;	/* dot11rate */
165 
166 	rs = (struct ieee80211_rateset *) &ni->ni_htrates;
167 	/* Initial rate - lowest */
168 	rate = rs->rs_rates[0];
169 
170 	/* Pick something low that's likely to succeed */
171 	for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0;
172 	    amn->amn_rix--) {
173 		/* 11n - stop at MCS4 */
174 		if ((rs->rs_rates[amn->amn_rix] & 0x1f) < 4)
175 			break;
176 	}
177 	rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
178 
179 	/* Ensure the MCS bit is set */
180 	rate |= IEEE80211_RATE_MCS;
181 
182 	/* Assign initial rate from the rateset */
183 	ieee80211_node_set_txrate_dot11rate(ni, rate);
184 
185 	/* XXX TODO: we really need a rate-to-string method */
186 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
187 	    "AMRR: nrates=%d, initial rate MCS %d",
188 	    rs->rs_nrates,
189 	    (rate & IEEE80211_RATE_VAL));
190 }
191 
192 static void
amrr_node_init_legacy(struct ieee80211_node * ni)193 amrr_node_init_legacy(struct ieee80211_node *ni)
194 {
195 	const struct ieee80211_rateset *rs;
196 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
197 	uint8_t rate;	/* dot11rate */
198 
199 	rs = &ni->ni_rates;
200 	/* Initial rate - lowest */
201 	rate = rs->rs_rates[0];
202 
203 	/* Clear the basic rate flag if it's not 11n */
204 	rate &= IEEE80211_RATE_VAL;
205 
206 	/* Pick something low that's likely to succeed */
207 	for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0;
208 	    amn->amn_rix--) {
209 		/* legacy - anything < 36mbit, stop searching */
210 		if ((rs->rs_rates[amn->amn_rix] &
211 		    IEEE80211_RATE_VAL) <= 72)
212 			break;
213 	}
214 	rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
215 
216 	/* Assign initial rate from the rateset */
217 	ieee80211_node_set_txrate_dot11rate(ni, rate);
218 
219 	/* XXX TODO: we really need a rate-to-string method */
220 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
221 	    "AMRR: nrates=%d, initial rate %d Mb",
222 	    rs->rs_nrates,
223 	    (rate & IEEE80211_RATE_VAL) / 2);
224 }
225 
226 static void
amrr_node_init(struct ieee80211_node * ni)227 amrr_node_init(struct ieee80211_node *ni)
228 {
229 	struct ieee80211vap *vap = ni->ni_vap;
230 	struct ieee80211_amrr *amrr = vap->iv_rs;
231 	struct ieee80211_amrr_node *amn;
232 
233 	if (!amrr) {
234 		net80211_vap_printf(vap,
235 		    "ratectl structure was not allocated, "
236 		    "per-node structure allocation skipped\n");
237 		return;
238 	}
239 
240 	if (ni->ni_rctls == NULL) {
241 		ni->ni_rctls = amn = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr_node),
242 		    M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
243 		if (amn == NULL) {
244 			net80211_vap_printf(vap,
245 			    "couldn't alloc per-node ratectl structure\n");
246 			return;
247 		}
248 	} else
249 		amn = ni->ni_rctls;
250 
251 	/* Common state */
252 	amn->amn_amrr = amrr;
253 	amn->amn_success = 0;
254 	amn->amn_recovery = 0;
255 	amn->amn_txcnt = amn->amn_retrycnt = 0;
256 	amn->amn_success_threshold = amrr->amrr_min_success_threshold;
257 	amn->amn_ticks = ticks;
258 
259 	/* Pick the right rateset */
260 	if (ieee80211_vht_check_tx_vht(ni))
261 		amrr_node_init_vht(ni);
262 	else if (ieee80211_ht_check_tx_ht(ni))
263 		amrr_node_init_ht(ni);
264 	else
265 		amrr_node_init_legacy(ni);
266 }
267 
268 static void
amrr_node_deinit(struct ieee80211_node * ni)269 amrr_node_deinit(struct ieee80211_node *ni)
270 {
271 	IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL);
272 }
273 
274 static void
amrr_update_vht_inc(struct ieee80211_node * ni)275 amrr_update_vht_inc(struct ieee80211_node *ni)
276 {
277 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
278 	uint8_t nss, mcs;
279 
280 	/*
281 	 * For now just keep looping over MCS to 9, then NSS up, checking if
282 	 * it's valid via ieee80211_vht_node_check_tx_valid_mcs(),
283 	 * until we hit max.  This at least tests the VHT MCS rates,
284 	 * but definitely is suboptimal (in the same way the 11n MCS selection
285 	 * is suboptimal.)
286 	 */
287 	nss = amn->amn_vht_nss;
288 	mcs = amn->amn_vht_mcs;
289 
290 	while (nss <= 8 && mcs <= 9) {
291 		/* Increment MCS 0..9, NSS 1..8 */
292 		if (mcs == 9) {
293 			mcs = 0;
294 			nss++;
295 		} else
296 			mcs++;
297 		if (nss > 8)
298 			break;
299 
300 		if (ieee80211_vht_node_check_tx_valid_mcs(ni, ni->ni_chw, nss,
301 		    mcs)) {
302 			amn->amn_vht_nss = nss;
303 			amn->amn_vht_mcs = mcs;
304 			break;
305 		}
306 	}
307 }
308 
309 static void
amrr_update_vht_dec(struct ieee80211_node * ni)310 amrr_update_vht_dec(struct ieee80211_node *ni)
311 {
312 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
313 	uint8_t nss, mcs;
314 
315 	/*
316 	 * For now just keep looping over MCS 9 .. 0 then NSS down, checking if
317 	 * it's valid via ieee80211_vht_node_check_tx_valid_mcs(),
318 	 * until we hit min.  This at least tests the VHT MCS rates,
319 	 * but definitely is suboptimal (in the same way the 11n MCS selection
320 	 * is suboptimal.
321 	 */
322 	nss = amn->amn_vht_nss;
323 	mcs = amn->amn_vht_mcs;
324 
325 	while (nss >= 1 && mcs >= 0) {
326 
327 		if (mcs == 0) {
328 			mcs = 9;
329 			nss--;
330 		} else
331 			mcs--;
332 		if (nss < 1)
333 			break;
334 
335 		if (ieee80211_vht_node_check_tx_valid_mcs(ni, ni->ni_chw, nss,
336 		    mcs)) {
337 			amn->amn_vht_nss = nss;
338 			amn->amn_vht_mcs = mcs;
339 			break;
340 		}
341 	}
342 }
343 
344 /*
345  * A placeholder / temporary hack VHT rate control.
346  *
347  * Use the available MCS rates at the current node bandwidth
348  * and configured / negotiated MCS rates.
349  */
350 static int
amrr_update_vht(struct ieee80211_node * ni)351 amrr_update_vht(struct ieee80211_node *ni)
352 {
353 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
354 	struct ieee80211_amrr *amrr = ni->ni_vap->iv_rs;
355 
356 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
357 	    "AMRR: VHT: current rate NSS %d MCS %d, txcnt=%d, retrycnt=%d",
358 	    amn->amn_vht_nss, amn->amn_vht_mcs, amn->amn_txcnt,
359 	    amn->amn_retrycnt);
360 
361 	if (is_success(amn)) {
362 		amn->amn_success++;
363 		if (amn->amn_success >= amn->amn_success_threshold) {
364 			amn->amn_recovery = 1;
365 			amn->amn_success = 0;
366 
367 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
368 			    "AMRR: VHT: increase rate (txcnt=%d retrycnt=%d)",
369 			    amn->amn_txcnt, amn->amn_retrycnt);
370 
371 			amrr_update_vht_inc(ni);
372 		} else {
373 			amn->amn_recovery = 0;
374 		}
375 	} else if (is_failure(amn)) {
376 		amn->amn_success = 0;
377 
378 		if (amn->amn_recovery) {
379 			amn->amn_success_threshold *= 2;
380 			if (amn->amn_success_threshold >
381 			    amrr->amrr_max_success_threshold)
382 				amn->amn_success_threshold =
383 				    amrr->amrr_max_success_threshold;
384 		} else {
385 			amn->amn_success_threshold =
386 			    amrr->amrr_min_success_threshold;
387 		}
388 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
389 		    "AMRR: VHT: decreasing rate (txcnt=%d retrycnt=%d)",
390 		    amn->amn_txcnt, amn->amn_retrycnt);
391 
392 		amrr_update_vht_dec(ni);
393 
394 		amn->amn_recovery = 0;
395 	}
396 
397 	/* Reset counters */
398 	amn->amn_txcnt = 0;
399 	amn->amn_retrycnt = 0;
400 
401 	/* Return 0, not useful anymore */
402 	return (0);
403 }
404 
405 static int
amrr_update_ht(struct ieee80211_amrr * amrr,struct ieee80211_amrr_node * amn,struct ieee80211_node * ni)406 amrr_update_ht(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
407     struct ieee80211_node *ni)
408 {
409 	int rix = amn->amn_rix;
410 	const struct ieee80211_rateset *rs;
411 
412 	rs = (struct ieee80211_rateset *)&ni->ni_htrates;
413 
414 	/* XXX TODO: we really need a rate-to-string method */
415 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
416 	    "AMRR: current rate MCS %d, txcnt=%d, retrycnt=%d",
417 	    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
418 	    amn->amn_txcnt,
419 	    amn->amn_retrycnt);
420 
421 	/*
422 	 * XXX This is totally bogus for 11n, as although high MCS
423 	 * rates for each stream may be failing, the next stream
424 	 * should be checked.
425 	 *
426 	 * Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to
427 	 * MCS23, we should skip 6/7 and try 8 onwards.
428 	 */
429 	if (is_success(amn)) {
430 		amn->amn_success++;
431 		if (amn->amn_success >= amn->amn_success_threshold &&
432 		    rix + 1 < rs->rs_nrates) {
433 			amn->amn_recovery = 1;
434 			amn->amn_success = 0;
435 			rix++;
436 			/* XXX TODO: we really need a rate-to-string method */
437 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
438 			    "AMRR increasing rate MCS %d "
439 			    "(txcnt=%d retrycnt=%d)",
440 			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
441 			    amn->amn_txcnt, amn->amn_retrycnt);
442 		} else {
443 			amn->amn_recovery = 0;
444 		}
445 	} else if (is_failure(amn)) {
446 		amn->amn_success = 0;
447 		if (rix > 0) {
448 			if (amn->amn_recovery) {
449 				amn->amn_success_threshold *= 2;
450 				if (amn->amn_success_threshold >
451 				    amrr->amrr_max_success_threshold)
452 					amn->amn_success_threshold =
453 					    amrr->amrr_max_success_threshold;
454 			} else {
455 				amn->amn_success_threshold =
456 				    amrr->amrr_min_success_threshold;
457 			}
458 			rix--;
459 			/* XXX TODO: we really need a rate-to-string method */
460 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
461 			    "AMRR decreasing rate MCS %d "
462 			    "(txcnt=%d retrycnt=%d)",
463 			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
464 			    amn->amn_txcnt, amn->amn_retrycnt);
465 		}
466 		amn->amn_recovery = 0;
467 	}
468 
469 	return (rix);
470 }
471 
472 static int
amrr_update_legacy(struct ieee80211_amrr * amrr,struct ieee80211_amrr_node * amn,struct ieee80211_node * ni)473 amrr_update_legacy(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
474     struct ieee80211_node *ni)
475 {
476 	int rix = amn->amn_rix;
477 	const struct ieee80211_rateset *rs;
478 
479 	rs = &ni->ni_rates;
480 
481 	/* XXX TODO: we really need a rate-to-string method */
482 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
483 	    "AMRR: current rate %d Mb, txcnt=%d, retrycnt=%d",
484 	    (rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2,
485 	    amn->amn_txcnt,
486 	    amn->amn_retrycnt);
487 
488 	if (is_success(amn)) {
489 		amn->amn_success++;
490 		if (amn->amn_success >= amn->amn_success_threshold &&
491 		    rix + 1 < rs->rs_nrates) {
492 			amn->amn_recovery = 1;
493 			amn->amn_success = 0;
494 			rix++;
495 			/* XXX TODO: we really need a rate-to-string method */
496 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
497 			    "AMRR increasing rate %d Mb (txcnt=%d retrycnt=%d)",
498 			    (rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2,
499 			    amn->amn_txcnt, amn->amn_retrycnt);
500 		} else {
501 			amn->amn_recovery = 0;
502 		}
503 	} else if (is_failure(amn)) {
504 		amn->amn_success = 0;
505 		if (rix > 0) {
506 			if (amn->amn_recovery) {
507 				amn->amn_success_threshold *= 2;
508 				if (amn->amn_success_threshold >
509 				    amrr->amrr_max_success_threshold)
510 					amn->amn_success_threshold =
511 					    amrr->amrr_max_success_threshold;
512 			} else {
513 				amn->amn_success_threshold =
514 				    amrr->amrr_min_success_threshold;
515 			}
516 			rix--;
517 			/* XXX TODO: we really need a rate-to-string method */
518 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
519 			    "AMRR decreasing rate %d Mb (txcnt=%d retrycnt=%d)",
520 			    (rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2,
521 			    amn->amn_txcnt, amn->amn_retrycnt);
522 		}
523 		amn->amn_recovery = 0;
524 	}
525 
526 	return (rix);
527 }
528 
529 static int
amrr_update(struct ieee80211_amrr * amrr,struct ieee80211_amrr_node * amn,struct ieee80211_node * ni)530 amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
531     struct ieee80211_node *ni)
532 {
533 	int rix;
534 
535 	KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
536 
537 	/* Pick the right rateset */
538 	if (ieee80211_vht_check_tx_vht(ni))
539 		rix = amrr_update_vht(ni);
540 	else if (ieee80211_ht_check_tx_ht(ni))
541 		rix = amrr_update_ht(amrr, amn, ni);
542 	else
543 		rix = amrr_update_legacy(amrr, amn, ni);
544 
545 	/* reset counters */
546 	amn->amn_txcnt = 0;
547 	amn->amn_retrycnt = 0;
548 
549 	return (rix);
550 }
551 
552 static int
amrr_rate_vht(struct ieee80211_node * ni)553 amrr_rate_vht(struct ieee80211_node *ni)
554 {
555 	struct ieee80211_amrr *amrr = ni->ni_vap->iv_rs;
556 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
557 
558 	if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval)
559 		amrr_update_vht(ni);
560 
561 	ieee80211_node_set_txrate_vht_rate(ni, amn->amn_vht_nss,
562 	    amn->amn_vht_mcs);
563 
564 	/* Note: There's no vht rs_rates, and the API doesn't use it anymore */
565 	return (0);
566 }
567 
568 /*
569  * Return the rate index to use in sending a data frame.
570  * Update our internal state if it's been long enough.
571  * If the rate changes we also update ni_txrate to match.
572  */
573 static int
amrr_rate(struct ieee80211_node * ni,void * arg __unused,uint32_t iarg __unused)574 amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
575 {
576 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
577 	struct ieee80211_amrr *amrr;
578 	const struct ieee80211_rateset *rs = NULL;
579 	int rix;
580 
581 	/* XXX should return -1 here, but drivers may not expect this... */
582 	if (!amn)
583 	{
584 		ieee80211_node_set_txrate_dot11rate(ni,
585 		    ni->ni_rates.rs_rates[0]);
586 		return 0;
587 	}
588 
589 	if (ieee80211_vht_check_tx_vht(ni))
590 		return (amrr_rate_vht(ni));
591 
592 	/* Pick the right rateset */
593 	if (ieee80211_ht_check_tx_ht(ni)) {
594 		/* XXX ew */
595 		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
596 	} else {
597 		rs = &ni->ni_rates;
598 	}
599 
600 	amrr = amn->amn_amrr;
601 	if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) {
602 		rix = amrr_update(amrr, amn, ni);
603 		if (rix != amn->amn_rix) {
604 			uint8_t dot11Rate;
605 			/* update public rate */
606 			dot11Rate = rs->rs_rates[rix];
607 			/* XXX strip basic rate flag from txrate, if non-11n */
608 			if (ieee80211_ht_check_tx_ht(ni))
609 				dot11Rate |= IEEE80211_RATE_MCS;
610 			else
611 				dot11Rate &= IEEE80211_RATE_VAL;
612 			ieee80211_node_set_txrate_dot11rate(ni, dot11Rate);
613 
614 			amn->amn_rix = rix;
615 		}
616 		amn->amn_ticks = ticks;
617 	} else
618 		rix = amn->amn_rix;
619 	return rix;
620 }
621 
622 /*
623  * Update statistics with tx complete status.  Ok is non-zero
624  * if the packet is known to be ACK'd.  Retries has the number
625  * retransmissions (i.e. xmit attempts - 1).
626  */
627 static void
amrr_tx_complete(const struct ieee80211_node * ni,const struct ieee80211_ratectl_tx_status * status)628 amrr_tx_complete(const struct ieee80211_node *ni,
629     const struct ieee80211_ratectl_tx_status *status)
630 {
631 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
632 	int retries;
633 
634 	if (!amn)
635 		return;
636 
637 	retries = 0;
638 	if (status->flags & IEEE80211_RATECTL_STATUS_LONG_RETRY)
639 		retries = status->long_retries;
640 
641 	amn->amn_txcnt++;
642 	if (status->status == IEEE80211_RATECTL_TX_SUCCESS)
643 		amn->amn_success++;
644 	amn->amn_retrycnt += retries;
645 }
646 
647 static void
amrr_tx_update_cb(void * arg,struct ieee80211_node * ni)648 amrr_tx_update_cb(void *arg, struct ieee80211_node *ni)
649 {
650 	struct ieee80211_ratectl_tx_stats *stats = arg;
651 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
652 	int txcnt, success, retrycnt;
653 
654 	if (!amn)
655 		return;
656 
657 	txcnt = stats->nframes;
658 	success = stats->nsuccess;
659 	retrycnt = 0;
660 	if (stats->flags & IEEE80211_RATECTL_TX_STATS_RETRIES)
661 		retrycnt = stats->nretries;
662 
663 	amn->amn_txcnt += txcnt;
664 	amn->amn_success += success;
665 	amn->amn_retrycnt += retrycnt;
666 }
667 
668 /*
669  * Set tx count/retry statistics explicitly.  Intended for
670  * drivers that poll the device for statistics maintained
671  * in the device.
672  */
673 static void
amrr_tx_update(struct ieee80211vap * vap,struct ieee80211_ratectl_tx_stats * stats)674 amrr_tx_update(struct ieee80211vap *vap,
675     struct ieee80211_ratectl_tx_stats *stats)
676 {
677 
678 	if (stats->flags & IEEE80211_RATECTL_TX_STATS_NODE)
679 		amrr_tx_update_cb(stats, stats->ni);
680 	else {
681 		ieee80211_iterate_nodes_vap(&vap->iv_ic->ic_sta, vap,
682 		    amrr_tx_update_cb, stats);
683 	}
684 }
685 
686 static int
amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)687 amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)
688 {
689 	struct ieee80211vap *vap = arg1;
690 	struct ieee80211_amrr *amrr = vap->iv_rs;
691 	int msecs, error;
692 
693 	if (!amrr)
694 		return ENOMEM;
695 
696 	msecs = ticks_to_msecs(amrr->amrr_interval);
697 	error = sysctl_handle_int(oidp, &msecs, 0, req);
698 	if (error || !req->newptr)
699 		return error;
700 	amrr_setinterval(vap, msecs);
701 	return 0;
702 }
703 
704 static void
amrr_sysctlattach(struct ieee80211vap * vap,struct sysctl_ctx_list * ctx,struct sysctl_oid * tree)705 amrr_sysctlattach(struct ieee80211vap *vap,
706     struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
707 {
708 	struct ieee80211_amrr *amrr = vap->iv_rs;
709 
710 	if (!amrr)
711 		return;
712 
713 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
714 	    "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
715 	    vap, 0, amrr_sysctl_interval, "I", "amrr operation interval (ms)");
716 	/* XXX bounds check values */
717 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
718 	    "amrr_max_sucess_threshold", CTLFLAG_RW,
719 	    &amrr->amrr_max_success_threshold, 0, "");
720 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
721 	    "amrr_min_sucess_threshold", CTLFLAG_RW,
722 	    &amrr->amrr_min_success_threshold, 0, "");
723 }
724 
725 static void
amrr_print_node_rate(struct ieee80211_amrr_node * amn,struct ieee80211_node * ni,struct sbuf * s)726 amrr_print_node_rate(struct ieee80211_amrr_node *amn,
727     struct ieee80211_node *ni, struct sbuf *s)
728 {
729 	int rate;
730 	struct ieee80211_rateset *rs;
731 
732 	if (ieee80211_ht_check_tx_ht(ni)) {
733 		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
734 		rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
735 		sbuf_printf(s, "rate: MCS %d\n", rate);
736 	} else {
737 		rs = &ni->ni_rates;
738 		rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
739 		sbuf_printf(s, "rate: %d Mbit\n", rate / 2);
740 	}
741 }
742 
743 static void
amrr_node_stats(struct ieee80211_node * ni,struct sbuf * s)744 amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s)
745 {
746 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
747 
748 	/* XXX TODO: check locking? */
749 
750 	if (!amn)
751 		return;
752 
753 	amrr_print_node_rate(amn, ni, s);
754 	sbuf_printf(s, "ticks: %d\n", amn->amn_ticks);
755 	sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt);
756 	sbuf_printf(s, "success: %u\n", amn->amn_success);
757 	sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold);
758 	sbuf_printf(s, "recovery: %u\n", amn->amn_recovery);
759 	sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt);
760 }
761