xref: /freebsd/sys/net80211/ieee80211_amrr.c (revision 4b2c7dfe768d341336f58db3cced9b097e1a6d2d)
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 		if_printf(vap->iv_ifp, "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 		if_printf(vap->iv_ifp, "ratectl structure was not allocated, "
235 		    "per-node structure allocation skipped\n");
236 		return;
237 	}
238 
239 	if (ni->ni_rctls == NULL) {
240 		ni->ni_rctls = amn = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr_node),
241 		    M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
242 		if (amn == NULL) {
243 			if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl "
244 			    "structure\n");
245 			return;
246 		}
247 	} else
248 		amn = ni->ni_rctls;
249 
250 	/* Common state */
251 	amn->amn_amrr = amrr;
252 	amn->amn_success = 0;
253 	amn->amn_recovery = 0;
254 	amn->amn_txcnt = amn->amn_retrycnt = 0;
255 	amn->amn_success_threshold = amrr->amrr_min_success_threshold;
256 	amn->amn_ticks = ticks;
257 
258 	/* Pick the right rateset */
259 	if (ieee80211_vht_check_tx_vht(ni))
260 		amrr_node_init_vht(ni);
261 	else if (ieee80211_ht_check_tx_ht(ni))
262 		amrr_node_init_ht(ni);
263 	else
264 		amrr_node_init_legacy(ni);
265 }
266 
267 static void
amrr_node_deinit(struct ieee80211_node * ni)268 amrr_node_deinit(struct ieee80211_node *ni)
269 {
270 	IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL);
271 }
272 
273 static void
amrr_update_vht_inc(struct ieee80211_node * ni)274 amrr_update_vht_inc(struct ieee80211_node *ni)
275 {
276 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
277 	uint8_t nss, mcs;
278 
279 	/*
280 	 * For now just keep looping over MCS to 9, then NSS up, checking if
281 	 * it's valid via ieee80211_vht_node_check_tx_valid_mcs(),
282 	 * until we hit max.  This at least tests the VHT MCS rates,
283 	 * but definitely is suboptimal (in the same way the 11n MCS selection
284 	 * is suboptimal.)
285 	 */
286 	nss = amn->amn_vht_nss;
287 	mcs = amn->amn_vht_mcs;
288 
289 	while (nss <= 8 && mcs <= 9) {
290 		/* Increment MCS 0..9, NSS 1..8 */
291 		if (mcs == 9) {
292 			mcs = 0;
293 			nss++;
294 		} else
295 			mcs++;
296 		if (nss > 8)
297 			break;
298 
299 		if (ieee80211_vht_node_check_tx_valid_mcs(ni, ni->ni_chw, nss,
300 		    mcs)) {
301 			amn->amn_vht_nss = nss;
302 			amn->amn_vht_mcs = mcs;
303 			break;
304 		}
305 	}
306 }
307 
308 static void
amrr_update_vht_dec(struct ieee80211_node * ni)309 amrr_update_vht_dec(struct ieee80211_node *ni)
310 {
311 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
312 	uint8_t nss, mcs;
313 
314 	/*
315 	 * For now just keep looping over MCS 9 .. 0 then NSS down, checking if
316 	 * it's valid via ieee80211_vht_node_check_tx_valid_mcs(),
317 	 * until we hit min.  This at least tests the VHT MCS rates,
318 	 * but definitely is suboptimal (in the same way the 11n MCS selection
319 	 * is suboptimal.
320 	 */
321 	nss = amn->amn_vht_nss;
322 	mcs = amn->amn_vht_mcs;
323 
324 	while (nss >= 1 && mcs >= 0) {
325 
326 		if (mcs == 0) {
327 			mcs = 9;
328 			nss--;
329 		} else
330 			mcs--;
331 		if (nss < 1)
332 			break;
333 
334 		if (ieee80211_vht_node_check_tx_valid_mcs(ni, ni->ni_chw, nss,
335 		    mcs)) {
336 			amn->amn_vht_nss = nss;
337 			amn->amn_vht_mcs = mcs;
338 			break;
339 		}
340 	}
341 }
342 
343 /*
344  * A placeholder / temporary hack VHT rate control.
345  *
346  * Use the available MCS rates at the current node bandwidth
347  * and configured / negotiated MCS rates.
348  */
349 static int
amrr_update_vht(struct ieee80211_node * ni)350 amrr_update_vht(struct ieee80211_node *ni)
351 {
352 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
353 	struct ieee80211_amrr *amrr = ni->ni_vap->iv_rs;
354 
355 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
356 	    "AMRR: VHT: current rate NSS %d MCS %d, txcnt=%d, retrycnt=%d",
357 	    amn->amn_vht_nss, amn->amn_vht_mcs, amn->amn_txcnt,
358 	    amn->amn_retrycnt);
359 
360 	if (is_success(amn)) {
361 		amn->amn_success++;
362 		if (amn->amn_success >= amn->amn_success_threshold) {
363 			amn->amn_recovery = 1;
364 			amn->amn_success = 0;
365 
366 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
367 			    "AMRR: VHT: increase rate (txcnt=%d retrycnt=%d)",
368 			    amn->amn_txcnt, amn->amn_retrycnt);
369 
370 			amrr_update_vht_inc(ni);
371 		} else {
372 			amn->amn_recovery = 0;
373 		}
374 	} else if (is_failure(amn)) {
375 		amn->amn_success = 0;
376 
377 		if (amn->amn_recovery) {
378 			amn->amn_success_threshold *= 2;
379 			if (amn->amn_success_threshold >
380 			    amrr->amrr_max_success_threshold)
381 				amn->amn_success_threshold =
382 				    amrr->amrr_max_success_threshold;
383 		} else {
384 			amn->amn_success_threshold =
385 			    amrr->amrr_min_success_threshold;
386 		}
387 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
388 		    "AMRR: VHT: decreasing rate (txcnt=%d retrycnt=%d)",
389 		    amn->amn_txcnt, amn->amn_retrycnt);
390 
391 		amrr_update_vht_dec(ni);
392 
393 		amn->amn_recovery = 0;
394 	}
395 
396 	/* Reset counters */
397 	amn->amn_txcnt = 0;
398 	amn->amn_retrycnt = 0;
399 
400 	/* Return 0, not useful anymore */
401 	return (0);
402 }
403 
404 static int
amrr_update_ht(struct ieee80211_amrr * amrr,struct ieee80211_amrr_node * amn,struct ieee80211_node * ni)405 amrr_update_ht(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
406     struct ieee80211_node *ni)
407 {
408 	int rix = amn->amn_rix;
409 	const struct ieee80211_rateset *rs;
410 
411 	rs = (struct ieee80211_rateset *)&ni->ni_htrates;
412 
413 	/* XXX TODO: we really need a rate-to-string method */
414 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
415 	    "AMRR: current rate MCS %d, txcnt=%d, retrycnt=%d",
416 	    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
417 	    amn->amn_txcnt,
418 	    amn->amn_retrycnt);
419 
420 	/*
421 	 * XXX This is totally bogus for 11n, as although high MCS
422 	 * rates for each stream may be failing, the next stream
423 	 * should be checked.
424 	 *
425 	 * Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to
426 	 * MCS23, we should skip 6/7 and try 8 onwards.
427 	 */
428 	if (is_success(amn)) {
429 		amn->amn_success++;
430 		if (amn->amn_success >= amn->amn_success_threshold &&
431 		    rix + 1 < rs->rs_nrates) {
432 			amn->amn_recovery = 1;
433 			amn->amn_success = 0;
434 			rix++;
435 			/* XXX TODO: we really need a rate-to-string method */
436 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
437 			    "AMRR increasing rate MCS %d "
438 			    "(txcnt=%d retrycnt=%d)",
439 			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
440 			    amn->amn_txcnt, amn->amn_retrycnt);
441 		} else {
442 			amn->amn_recovery = 0;
443 		}
444 	} else if (is_failure(amn)) {
445 		amn->amn_success = 0;
446 		if (rix > 0) {
447 			if (amn->amn_recovery) {
448 				amn->amn_success_threshold *= 2;
449 				if (amn->amn_success_threshold >
450 				    amrr->amrr_max_success_threshold)
451 					amn->amn_success_threshold =
452 					    amrr->amrr_max_success_threshold;
453 			} else {
454 				amn->amn_success_threshold =
455 				    amrr->amrr_min_success_threshold;
456 			}
457 			rix--;
458 			/* XXX TODO: we really need a rate-to-string method */
459 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
460 			    "AMRR decreasing rate MCS %d "
461 			    "(txcnt=%d retrycnt=%d)",
462 			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
463 			    amn->amn_txcnt, amn->amn_retrycnt);
464 		}
465 		amn->amn_recovery = 0;
466 	}
467 
468 	return (rix);
469 }
470 
471 static int
amrr_update_legacy(struct ieee80211_amrr * amrr,struct ieee80211_amrr_node * amn,struct ieee80211_node * ni)472 amrr_update_legacy(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
473     struct ieee80211_node *ni)
474 {
475 	int rix = amn->amn_rix;
476 	const struct ieee80211_rateset *rs;
477 
478 	rs = &ni->ni_rates;
479 
480 	/* XXX TODO: we really need a rate-to-string method */
481 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
482 	    "AMRR: current rate %d Mb, txcnt=%d, retrycnt=%d",
483 	    (rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2,
484 	    amn->amn_txcnt,
485 	    amn->amn_retrycnt);
486 
487 	if (is_success(amn)) {
488 		amn->amn_success++;
489 		if (amn->amn_success >= amn->amn_success_threshold &&
490 		    rix + 1 < rs->rs_nrates) {
491 			amn->amn_recovery = 1;
492 			amn->amn_success = 0;
493 			rix++;
494 			/* XXX TODO: we really need a rate-to-string method */
495 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
496 			    "AMRR increasing rate %d Mb (txcnt=%d retrycnt=%d)",
497 			    (rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2,
498 			    amn->amn_txcnt, amn->amn_retrycnt);
499 		} else {
500 			amn->amn_recovery = 0;
501 		}
502 	} else if (is_failure(amn)) {
503 		amn->amn_success = 0;
504 		if (rix > 0) {
505 			if (amn->amn_recovery) {
506 				amn->amn_success_threshold *= 2;
507 				if (amn->amn_success_threshold >
508 				    amrr->amrr_max_success_threshold)
509 					amn->amn_success_threshold =
510 					    amrr->amrr_max_success_threshold;
511 			} else {
512 				amn->amn_success_threshold =
513 				    amrr->amrr_min_success_threshold;
514 			}
515 			rix--;
516 			/* XXX TODO: we really need a rate-to-string method */
517 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
518 			    "AMRR decreasing rate %d Mb (txcnt=%d retrycnt=%d)",
519 			    (rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2,
520 			    amn->amn_txcnt, amn->amn_retrycnt);
521 		}
522 		amn->amn_recovery = 0;
523 	}
524 
525 	return (rix);
526 }
527 
528 static int
amrr_update(struct ieee80211_amrr * amrr,struct ieee80211_amrr_node * amn,struct ieee80211_node * ni)529 amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
530     struct ieee80211_node *ni)
531 {
532 	int rix;
533 
534 	KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
535 
536 	/* Pick the right rateset */
537 	if (ieee80211_vht_check_tx_vht(ni))
538 		rix = amrr_update_vht(ni);
539 	else if (ieee80211_ht_check_tx_ht(ni))
540 		rix = amrr_update_ht(amrr, amn, ni);
541 	else
542 		rix = amrr_update_legacy(amrr, amn, ni);
543 
544 	/* reset counters */
545 	amn->amn_txcnt = 0;
546 	amn->amn_retrycnt = 0;
547 
548 	return (rix);
549 }
550 
551 static int
amrr_rate_vht(struct ieee80211_node * ni)552 amrr_rate_vht(struct ieee80211_node *ni)
553 {
554 	struct ieee80211_amrr *amrr = ni->ni_vap->iv_rs;
555 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
556 
557 	if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval)
558 		amrr_update_vht(ni);
559 
560 	ieee80211_node_set_txrate_vht_rate(ni, amn->amn_vht_nss,
561 	    amn->amn_vht_mcs);
562 
563 	/* Note: There's no vht rs_rates, and the API doesn't use it anymore */
564 	return (0);
565 }
566 
567 /*
568  * Return the rate index to use in sending a data frame.
569  * Update our internal state if it's been long enough.
570  * If the rate changes we also update ni_txrate to match.
571  */
572 static int
amrr_rate(struct ieee80211_node * ni,void * arg __unused,uint32_t iarg __unused)573 amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
574 {
575 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
576 	struct ieee80211_amrr *amrr;
577 	const struct ieee80211_rateset *rs = NULL;
578 	int rix;
579 
580 	/* XXX should return -1 here, but drivers may not expect this... */
581 	if (!amn)
582 	{
583 		ieee80211_node_set_txrate_dot11rate(ni,
584 		    ni->ni_rates.rs_rates[0]);
585 		return 0;
586 	}
587 
588 	if (ieee80211_vht_check_tx_vht(ni))
589 		return (amrr_rate_vht(ni));
590 
591 	/* Pick the right rateset */
592 	if (ieee80211_ht_check_tx_ht(ni)) {
593 		/* XXX ew */
594 		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
595 	} else {
596 		rs = &ni->ni_rates;
597 	}
598 
599 	amrr = amn->amn_amrr;
600 	if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) {
601 		rix = amrr_update(amrr, amn, ni);
602 		if (rix != amn->amn_rix) {
603 			uint8_t dot11Rate;
604 			/* update public rate */
605 			dot11Rate = rs->rs_rates[rix];
606 			/* XXX strip basic rate flag from txrate, if non-11n */
607 			if (ieee80211_ht_check_tx_ht(ni))
608 				dot11Rate |= IEEE80211_RATE_MCS;
609 			else
610 				dot11Rate &= IEEE80211_RATE_VAL;
611 			ieee80211_node_set_txrate_dot11rate(ni, dot11Rate);
612 
613 			amn->amn_rix = rix;
614 		}
615 		amn->amn_ticks = ticks;
616 	} else
617 		rix = amn->amn_rix;
618 	return rix;
619 }
620 
621 /*
622  * Update statistics with tx complete status.  Ok is non-zero
623  * if the packet is known to be ACK'd.  Retries has the number
624  * retransmissions (i.e. xmit attempts - 1).
625  */
626 static void
amrr_tx_complete(const struct ieee80211_node * ni,const struct ieee80211_ratectl_tx_status * status)627 amrr_tx_complete(const struct ieee80211_node *ni,
628     const struct ieee80211_ratectl_tx_status *status)
629 {
630 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
631 	int retries;
632 
633 	if (!amn)
634 		return;
635 
636 	retries = 0;
637 	if (status->flags & IEEE80211_RATECTL_STATUS_LONG_RETRY)
638 		retries = status->long_retries;
639 
640 	amn->amn_txcnt++;
641 	if (status->status == IEEE80211_RATECTL_TX_SUCCESS)
642 		amn->amn_success++;
643 	amn->amn_retrycnt += retries;
644 }
645 
646 static void
amrr_tx_update_cb(void * arg,struct ieee80211_node * ni)647 amrr_tx_update_cb(void *arg, struct ieee80211_node *ni)
648 {
649 	struct ieee80211_ratectl_tx_stats *stats = arg;
650 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
651 	int txcnt, success, retrycnt;
652 
653 	if (!amn)
654 		return;
655 
656 	txcnt = stats->nframes;
657 	success = stats->nsuccess;
658 	retrycnt = 0;
659 	if (stats->flags & IEEE80211_RATECTL_TX_STATS_RETRIES)
660 		retrycnt = stats->nretries;
661 
662 	amn->amn_txcnt += txcnt;
663 	amn->amn_success += success;
664 	amn->amn_retrycnt += retrycnt;
665 }
666 
667 /*
668  * Set tx count/retry statistics explicitly.  Intended for
669  * drivers that poll the device for statistics maintained
670  * in the device.
671  */
672 static void
amrr_tx_update(struct ieee80211vap * vap,struct ieee80211_ratectl_tx_stats * stats)673 amrr_tx_update(struct ieee80211vap *vap,
674     struct ieee80211_ratectl_tx_stats *stats)
675 {
676 
677 	if (stats->flags & IEEE80211_RATECTL_TX_STATS_NODE)
678 		amrr_tx_update_cb(stats, stats->ni);
679 	else {
680 		ieee80211_iterate_nodes_vap(&vap->iv_ic->ic_sta, vap,
681 		    amrr_tx_update_cb, stats);
682 	}
683 }
684 
685 static int
amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)686 amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)
687 {
688 	struct ieee80211vap *vap = arg1;
689 	struct ieee80211_amrr *amrr = vap->iv_rs;
690 	int msecs, error;
691 
692 	if (!amrr)
693 		return ENOMEM;
694 
695 	msecs = ticks_to_msecs(amrr->amrr_interval);
696 	error = sysctl_handle_int(oidp, &msecs, 0, req);
697 	if (error || !req->newptr)
698 		return error;
699 	amrr_setinterval(vap, msecs);
700 	return 0;
701 }
702 
703 static void
amrr_sysctlattach(struct ieee80211vap * vap,struct sysctl_ctx_list * ctx,struct sysctl_oid * tree)704 amrr_sysctlattach(struct ieee80211vap *vap,
705     struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
706 {
707 	struct ieee80211_amrr *amrr = vap->iv_rs;
708 
709 	if (!amrr)
710 		return;
711 
712 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
713 	    "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
714 	    vap, 0, amrr_sysctl_interval, "I", "amrr operation interval (ms)");
715 	/* XXX bounds check values */
716 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
717 	    "amrr_max_sucess_threshold", CTLFLAG_RW,
718 	    &amrr->amrr_max_success_threshold, 0, "");
719 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
720 	    "amrr_min_sucess_threshold", CTLFLAG_RW,
721 	    &amrr->amrr_min_success_threshold, 0, "");
722 }
723 
724 static void
amrr_print_node_rate(struct ieee80211_amrr_node * amn,struct ieee80211_node * ni,struct sbuf * s)725 amrr_print_node_rate(struct ieee80211_amrr_node *amn,
726     struct ieee80211_node *ni, struct sbuf *s)
727 {
728 	int rate;
729 	struct ieee80211_rateset *rs;
730 
731 	if (ieee80211_ht_check_tx_ht(ni)) {
732 		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
733 		rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
734 		sbuf_printf(s, "rate: MCS %d\n", rate);
735 	} else {
736 		rs = &ni->ni_rates;
737 		rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
738 		sbuf_printf(s, "rate: %d Mbit\n", rate / 2);
739 	}
740 }
741 
742 static void
amrr_node_stats(struct ieee80211_node * ni,struct sbuf * s)743 amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s)
744 {
745 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
746 
747 	/* XXX TODO: check locking? */
748 
749 	if (!amn)
750 		return;
751 
752 	amrr_print_node_rate(amn, ni, s);
753 	sbuf_printf(s, "ticks: %d\n", amn->amn_ticks);
754 	sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt);
755 	sbuf_printf(s, "success: %u\n", amn->amn_success);
756 	sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold);
757 	sbuf_printf(s, "recovery: %u\n", amn->amn_recovery);
758 	sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt);
759 }
760