xref: /freebsd/sys/net80211/ieee80211_amrr.c (revision e99cbea414ffa2c439a651e4726896ea8de99c83)
174828f25SSam Leffler /*	$OpenBSD: ieee80211_amrr.c,v 1.1 2006/06/17 19:07:19 damien Exp $	*/
274828f25SSam Leffler 
374828f25SSam Leffler /*-
4b6108616SRui Paulo  * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org>
574828f25SSam Leffler  * Copyright (c) 2006
674828f25SSam Leffler  *	Damien Bergamini <damien.bergamini@free.fr>
774828f25SSam Leffler  *
874828f25SSam Leffler  * Permission to use, copy, modify, and distribute this software for any
974828f25SSam Leffler  * purpose with or without fee is hereby granted, provided that the above
1074828f25SSam Leffler  * copyright notice and this permission notice appear in all copies.
1174828f25SSam Leffler  *
1274828f25SSam Leffler  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1374828f25SSam Leffler  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1474828f25SSam Leffler  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1574828f25SSam Leffler  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1674828f25SSam Leffler  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1774828f25SSam Leffler  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1874828f25SSam Leffler  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1974828f25SSam Leffler  */
2074828f25SSam Leffler 
2174828f25SSam Leffler #include <sys/cdefs.h>
2274828f25SSam Leffler /*-
2374828f25SSam Leffler  * Naive implementation of the Adaptive Multi Rate Retry algorithm:
2474828f25SSam Leffler  *
2574828f25SSam Leffler  * "IEEE 802.11 Rate Adaptation: A Practical Approach"
2674828f25SSam Leffler  *  Mathieu Lacage, Hossein Manshaei, Thierry Turletti
2774828f25SSam Leffler  *  INRIA Sophia - Projet Planete
2874828f25SSam Leffler  *  http://www-sop.inria.fr/rapports/sophia/RR-5208.html
2974828f25SSam Leffler  */
30b032f27cSSam Leffler #include "opt_wlan.h"
31b032f27cSSam Leffler 
3274828f25SSam Leffler #include <sys/param.h>
3374828f25SSam Leffler #include <sys/kernel.h>
348ec07310SGleb Smirnoff #include <sys/malloc.h>
3574828f25SSam Leffler #include <sys/module.h>
3673931706SAdrian Chadd #include <sys/sbuf.h>
3774828f25SSam Leffler #include <sys/socket.h>
3874828f25SSam Leffler #include <sys/sysctl.h>
3974828f25SSam Leffler 
4074828f25SSam Leffler #include <net/if.h>
4176039bc8SGleb Smirnoff #include <net/if_var.h>
4274828f25SSam Leffler #include <net/if_media.h>
43c3322cb9SGleb Smirnoff #include <net/ethernet.h>
4474828f25SSam Leffler 
4574828f25SSam Leffler #ifdef INET
4674828f25SSam Leffler #include <netinet/in.h>
4774828f25SSam Leffler #include <netinet/if_ether.h>
4874828f25SSam Leffler #endif
4974828f25SSam Leffler 
5074828f25SSam Leffler #include <net80211/ieee80211_var.h>
51f8bf74f2SAdrian Chadd #include <net80211/ieee80211_ht.h>
5274828f25SSam Leffler #include <net80211/ieee80211_amrr.h>
53b6108616SRui Paulo #include <net80211/ieee80211_ratectl.h>
5474828f25SSam Leffler 
5574828f25SSam Leffler #define is_success(amn)	\
5674828f25SSam Leffler 	((amn)->amn_retrycnt < (amn)->amn_txcnt / 10)
5774828f25SSam Leffler #define is_failure(amn)	\
5874828f25SSam Leffler 	((amn)->amn_retrycnt > (amn)->amn_txcnt / 3)
5974828f25SSam Leffler #define is_enough(amn)		\
6074828f25SSam Leffler 	((amn)->amn_txcnt > 10)
61b032f27cSSam Leffler 
62b6108616SRui Paulo static void	amrr_setinterval(const struct ieee80211vap *, int);
63b6108616SRui Paulo static void	amrr_init(struct ieee80211vap *);
64b6108616SRui Paulo static void	amrr_deinit(struct ieee80211vap *);
65b6108616SRui Paulo static void	amrr_node_init(struct ieee80211_node *);
66b6108616SRui Paulo static void	amrr_node_deinit(struct ieee80211_node *);
67b6108616SRui Paulo static int	amrr_update(struct ieee80211_amrr *,
68b6108616SRui Paulo     			struct ieee80211_amrr_node *, struct ieee80211_node *);
69b6108616SRui Paulo static int	amrr_rate(struct ieee80211_node *, void *, uint32_t);
70f6930becSAndriy Voskoboinyk static void	amrr_tx_complete(const struct ieee80211_node *,
71f6930becSAndriy Voskoboinyk 			const struct ieee80211_ratectl_tx_status *);
72f6930becSAndriy Voskoboinyk static void	amrr_tx_update_cb(void *, struct ieee80211_node *);
73f6930becSAndriy Voskoboinyk static void	amrr_tx_update(struct ieee80211vap *vap,
74f6930becSAndriy Voskoboinyk 			struct ieee80211_ratectl_tx_stats *);
75b6108616SRui Paulo static void	amrr_sysctlattach(struct ieee80211vap *,
76b6108616SRui Paulo 			struct sysctl_ctx_list *, struct sysctl_oid *);
7773931706SAdrian Chadd static void	amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s);
78b032f27cSSam Leffler 
79b032f27cSSam Leffler /* number of references from net80211 layer */
80b032f27cSSam Leffler static	int nrefs = 0;
81b032f27cSSam Leffler 
82b6108616SRui Paulo static const struct ieee80211_ratectl amrr = {
83b6108616SRui Paulo 	.ir_name	= "amrr",
84b6108616SRui Paulo 	.ir_attach	= NULL,
85b6108616SRui Paulo 	.ir_detach	= NULL,
86b6108616SRui Paulo 	.ir_init	= amrr_init,
87b6108616SRui Paulo 	.ir_deinit	= amrr_deinit,
88b6108616SRui Paulo 	.ir_node_init	= amrr_node_init,
89b6108616SRui Paulo 	.ir_node_deinit	= amrr_node_deinit,
90b6108616SRui Paulo 	.ir_rate	= amrr_rate,
91b6108616SRui Paulo 	.ir_tx_complete	= amrr_tx_complete,
92b6108616SRui Paulo 	.ir_tx_update	= amrr_tx_update,
93b6108616SRui Paulo 	.ir_setinterval	= amrr_setinterval,
9473931706SAdrian Chadd 	.ir_node_stats	= amrr_node_stats,
95b6108616SRui Paulo };
96b6108616SRui Paulo IEEE80211_RATECTL_MODULE(amrr, 1);
97b6108616SRui Paulo IEEE80211_RATECTL_ALG(amrr, IEEE80211_RATECTL_AMRR, amrr);
98b6108616SRui Paulo 
99b6108616SRui Paulo static void
100b6108616SRui Paulo amrr_setinterval(const struct ieee80211vap *vap, int msecs)
101b032f27cSSam Leffler {
102b6108616SRui Paulo 	struct ieee80211_amrr *amrr = vap->iv_rs;
103b032f27cSSam Leffler 
1044367c2d1SAndriy Voskoboinyk 	if (!amrr)
1054367c2d1SAndriy Voskoboinyk 		return;
1064367c2d1SAndriy Voskoboinyk 
107b032f27cSSam Leffler 	if (msecs < 100)
108b032f27cSSam Leffler 		msecs = 100;
1099df9e936SAndriy Voskoboinyk 	amrr->amrr_interval = msecs_to_ticks(msecs);
110b032f27cSSam Leffler }
11174828f25SSam Leffler 
112b6108616SRui Paulo static void
113b6108616SRui Paulo amrr_init(struct ieee80211vap *vap)
11474828f25SSam Leffler {
115b6108616SRui Paulo 	struct ieee80211_amrr *amrr;
116b032f27cSSam Leffler 
117b6108616SRui Paulo 	KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__));
118b6108616SRui Paulo 
119810490a0SAndriy Voskoboinyk 	nrefs++;		/* XXX locking */
120b9b53389SAdrian Chadd 	amrr = vap->iv_rs = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr),
121b9b53389SAdrian Chadd 	    M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
122645fe19aSRui Paulo 	if (amrr == NULL) {
123645fe19aSRui Paulo 		if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n");
124645fe19aSRui Paulo 		return;
125645fe19aSRui Paulo 	}
126b6108616SRui Paulo 	amrr->amrr_min_success_threshold = IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD;
127b6108616SRui Paulo 	amrr->amrr_max_success_threshold = IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD;
128b6108616SRui Paulo 	amrr_setinterval(vap, 500 /* ms */);
129b6108616SRui Paulo 	amrr_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid);
130b032f27cSSam Leffler }
131b032f27cSSam Leffler 
132b6108616SRui Paulo static void
133b6108616SRui Paulo amrr_deinit(struct ieee80211vap *vap)
134b032f27cSSam Leffler {
135810490a0SAndriy Voskoboinyk 	KASSERT(nrefs > 0, ("imbalanced attach/detach"));
136cd9fee3dSBjoern A. Zeeb 	IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL);
137cd9fee3dSBjoern A. Zeeb 	vap->iv_rs = NULL;	/* guard */
138810490a0SAndriy Voskoboinyk 	nrefs--;		/* XXX locking */
13974828f25SSam Leffler }
14074828f25SSam Leffler 
141b6108616SRui Paulo static void
142b6108616SRui Paulo amrr_node_init(struct ieee80211_node *ni)
14374828f25SSam Leffler {
144f8bf74f2SAdrian Chadd 	const struct ieee80211_rateset *rs = NULL;
145b6108616SRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
146b6108616SRui Paulo 	struct ieee80211_amrr *amrr = vap->iv_rs;
147b6108616SRui Paulo 	struct ieee80211_amrr_node *amn;
148f8bf74f2SAdrian Chadd 	uint8_t rate;
149b032f27cSSam Leffler 
1504367c2d1SAndriy Voskoboinyk 	if (!amrr) {
1514367c2d1SAndriy Voskoboinyk 		if_printf(vap->iv_ifp, "ratectl structure was not allocated, "
1524367c2d1SAndriy Voskoboinyk 		    "per-node structure allocation skipped\n");
1534367c2d1SAndriy Voskoboinyk 		return;
1544367c2d1SAndriy Voskoboinyk 	}
1554367c2d1SAndriy Voskoboinyk 
156380fe2dfSRui Paulo 	if (ni->ni_rctls == NULL) {
157b9b53389SAdrian Chadd 		ni->ni_rctls = amn = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr_node),
158b9b53389SAdrian Chadd 		    M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
159645fe19aSRui Paulo 		if (amn == NULL) {
160645fe19aSRui Paulo 			if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl "
161645fe19aSRui Paulo 			    "structure\n");
162645fe19aSRui Paulo 			return;
163645fe19aSRui Paulo 		}
164380fe2dfSRui Paulo 	} else
165380fe2dfSRui Paulo 		amn = ni->ni_rctls;
166b032f27cSSam Leffler 	amn->amn_amrr = amrr;
16774828f25SSam Leffler 	amn->amn_success = 0;
16874828f25SSam Leffler 	amn->amn_recovery = 0;
16974828f25SSam Leffler 	amn->amn_txcnt = amn->amn_retrycnt = 0;
17074828f25SSam Leffler 	amn->amn_success_threshold = amrr->amrr_min_success_threshold;
171b032f27cSSam Leffler 
172f8bf74f2SAdrian Chadd 	/* 11n or not? Pick the right rateset */
1732014462dSAdrian Chadd 	if (ieee80211_ht_check_tx_ht(ni)) {
174f8bf74f2SAdrian Chadd 		/* XXX ew */
175f8bf74f2SAdrian Chadd 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
176f8bf74f2SAdrian Chadd 		    "%s: 11n node", __func__);
177f8bf74f2SAdrian Chadd 		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
178f8bf74f2SAdrian Chadd 	} else {
179f8bf74f2SAdrian Chadd 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
180f8bf74f2SAdrian Chadd 		    "%s: non-11n node", __func__);
181f8bf74f2SAdrian Chadd 		rs = &ni->ni_rates;
182f8bf74f2SAdrian Chadd 	}
183f8bf74f2SAdrian Chadd 
184f8bf74f2SAdrian Chadd 	/* Initial rate - lowest */
185f8bf74f2SAdrian Chadd 	rate = rs->rs_rates[0];
186f8bf74f2SAdrian Chadd 
187f8bf74f2SAdrian Chadd 	/* XXX clear the basic rate flag if it's not 11n */
1882014462dSAdrian Chadd 	if (! ieee80211_ht_check_tx_ht(ni))
189f8bf74f2SAdrian Chadd 		rate &= IEEE80211_RATE_VAL;
190f8bf74f2SAdrian Chadd 
191f8bf74f2SAdrian Chadd 	/* pick initial rate from the rateset - HT or otherwise */
192ebb12408SAdrian Chadd 	/* Pick something low that's likely to succeed */
193f8bf74f2SAdrian Chadd 	for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0;
194f8bf74f2SAdrian Chadd 	    amn->amn_rix--) {
195f8bf74f2SAdrian Chadd 		/* legacy - anything < 36mbit, stop searching */
196ebb12408SAdrian Chadd 		/* 11n - stop at MCS4 */
1972014462dSAdrian Chadd 		if (ieee80211_ht_check_tx_ht(ni)) {
198ebb12408SAdrian Chadd 			if ((rs->rs_rates[amn->amn_rix] & 0x1f) < 4)
199f8bf74f2SAdrian Chadd 				break;
2005140f9e6SAdrian Chadd 		} else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72)
201f8bf74f2SAdrian Chadd 			break;
202f8bf74f2SAdrian Chadd 	}
2035140f9e6SAdrian Chadd 	rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
204f8bf74f2SAdrian Chadd 
205f8bf74f2SAdrian Chadd 	/* if the rate is an 11n rate, ensure the MCS bit is set */
2062014462dSAdrian Chadd 	if (ieee80211_ht_check_tx_ht(ni))
207f8bf74f2SAdrian Chadd 		rate |= IEEE80211_RATE_MCS;
208f8bf74f2SAdrian Chadd 
209f8bf74f2SAdrian Chadd 	/* Assign initial rate from the rateset */
21038075f7dSAdrian Chadd 	ieee80211_node_set_txrate_dot11rate(ni, rate);
211b032f27cSSam Leffler 	amn->amn_ticks = ticks;
212b032f27cSSam Leffler 
213eec88845SAdrian Chadd 	/* XXX TODO: we really need a rate-to-string method */
214eec88845SAdrian Chadd 	/* XXX TODO: non-11n rate should be divided by two.. */
215b032f27cSSam Leffler 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
216eec88845SAdrian Chadd 	    "AMRR: nrates=%d, initial rate %s%d",
217f8bf74f2SAdrian Chadd 	    rs->rs_nrates,
2182014462dSAdrian Chadd 	    ieee80211_ht_check_tx_ht(ni) ? "MCS " : "",
219eec88845SAdrian Chadd 	    rate & IEEE80211_RATE_VAL);
22074828f25SSam Leffler }
22174828f25SSam Leffler 
222b6108616SRui Paulo static void
223b6108616SRui Paulo amrr_node_deinit(struct ieee80211_node *ni)
224b6108616SRui Paulo {
225b9b53389SAdrian Chadd 	IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL);
226b6108616SRui Paulo }
227b6108616SRui Paulo 
228b032f27cSSam Leffler static int
229*e99cbea4SAdrian Chadd amrr_update_ht(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
230b032f27cSSam Leffler     struct ieee80211_node *ni)
23174828f25SSam Leffler {
232b032f27cSSam Leffler 	int rix = amn->amn_rix;
233*e99cbea4SAdrian Chadd 	const struct ieee80211_rateset *rs;
23474828f25SSam Leffler 
235f8bf74f2SAdrian Chadd 	rs = (struct ieee80211_rateset *)&ni->ni_htrates;
236f8bf74f2SAdrian Chadd 
237eec88845SAdrian Chadd 	/* XXX TODO: we really need a rate-to-string method */
238f8bf74f2SAdrian Chadd 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
239*e99cbea4SAdrian Chadd 	    "AMRR: current rate MCS %d, txcnt=%d, retrycnt=%d",
240f8bf74f2SAdrian Chadd 	    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
241f8bf74f2SAdrian Chadd 	    amn->amn_txcnt,
242f8bf74f2SAdrian Chadd 	    amn->amn_retrycnt);
243f8bf74f2SAdrian Chadd 
244f8d390beSAdrian Chadd 	/*
245f8d390beSAdrian Chadd 	 * XXX This is totally bogus for 11n, as although high MCS
246f8d390beSAdrian Chadd 	 * rates for each stream may be failing, the next stream
247f8d390beSAdrian Chadd 	 * should be checked.
248f8d390beSAdrian Chadd 	 *
249f8d390beSAdrian Chadd 	 * Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to
250f8d390beSAdrian Chadd 	 * MCS23, we should skip 6/7 and try 8 onwards.
251f8d390beSAdrian Chadd 	 */
252b032f27cSSam Leffler 	if (is_success(amn)) {
25374828f25SSam Leffler 		amn->amn_success++;
25474828f25SSam Leffler 		if (amn->amn_success >= amn->amn_success_threshold &&
255f8bf74f2SAdrian Chadd 		    rix + 1 < rs->rs_nrates) {
25674828f25SSam Leffler 			amn->amn_recovery = 1;
25774828f25SSam Leffler 			amn->amn_success = 0;
258b032f27cSSam Leffler 			rix++;
259eec88845SAdrian Chadd 			/* XXX TODO: we really need a rate-to-string method */
260b032f27cSSam Leffler 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
261*e99cbea4SAdrian Chadd 			    "AMRR increasing rate MCS %d "
262*e99cbea4SAdrian Chadd 			    "(txcnt=%d retrycnt=%d)",
263f8bf74f2SAdrian Chadd 			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
26474828f25SSam Leffler 			    amn->amn_txcnt, amn->amn_retrycnt);
26574828f25SSam Leffler 		} else {
26674828f25SSam Leffler 			amn->amn_recovery = 0;
26774828f25SSam Leffler 		}
26874828f25SSam Leffler 	} else if (is_failure(amn)) {
26974828f25SSam Leffler 		amn->amn_success = 0;
270b032f27cSSam Leffler 		if (rix > 0) {
27174828f25SSam Leffler 			if (amn->amn_recovery) {
27274828f25SSam Leffler 				amn->amn_success_threshold *= 2;
27374828f25SSam Leffler 				if (amn->amn_success_threshold >
27474828f25SSam Leffler 				    amrr->amrr_max_success_threshold)
27574828f25SSam Leffler 					amn->amn_success_threshold =
27674828f25SSam Leffler 					    amrr->amrr_max_success_threshold;
27774828f25SSam Leffler 			} else {
27874828f25SSam Leffler 				amn->amn_success_threshold =
27974828f25SSam Leffler 				    amrr->amrr_min_success_threshold;
28074828f25SSam Leffler 			}
281b032f27cSSam Leffler 			rix--;
282eec88845SAdrian Chadd 			/* XXX TODO: we really need a rate-to-string method */
283b032f27cSSam Leffler 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
284*e99cbea4SAdrian Chadd 			    "AMRR decreasing rate MCS %d "
285*e99cbea4SAdrian Chadd 			    "(txcnt=%d retrycnt=%d)",
286f8bf74f2SAdrian Chadd 			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
28774828f25SSam Leffler 			    amn->amn_txcnt, amn->amn_retrycnt);
28874828f25SSam Leffler 		}
28974828f25SSam Leffler 		amn->amn_recovery = 0;
29074828f25SSam Leffler 	}
29174828f25SSam Leffler 
292*e99cbea4SAdrian Chadd 	return (rix);
293*e99cbea4SAdrian Chadd }
294*e99cbea4SAdrian Chadd 
295*e99cbea4SAdrian Chadd static int
296*e99cbea4SAdrian Chadd amrr_update_legacy(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
297*e99cbea4SAdrian Chadd     struct ieee80211_node *ni)
298*e99cbea4SAdrian Chadd {
299*e99cbea4SAdrian Chadd 	int rix = amn->amn_rix;
300*e99cbea4SAdrian Chadd 	const struct ieee80211_rateset *rs;
301*e99cbea4SAdrian Chadd 
302*e99cbea4SAdrian Chadd 	rs = &ni->ni_rates;
303*e99cbea4SAdrian Chadd 
304*e99cbea4SAdrian Chadd 	/* XXX TODO: we really need a rate-to-string method */
305*e99cbea4SAdrian Chadd 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
306*e99cbea4SAdrian Chadd 	    "AMRR: current rate %d Mb, txcnt=%d, retrycnt=%d",
307*e99cbea4SAdrian Chadd 	    (rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2,
308*e99cbea4SAdrian Chadd 	    amn->amn_txcnt,
309*e99cbea4SAdrian Chadd 	    amn->amn_retrycnt);
310*e99cbea4SAdrian Chadd 
311*e99cbea4SAdrian Chadd 	if (is_success(amn)) {
312*e99cbea4SAdrian Chadd 		amn->amn_success++;
313*e99cbea4SAdrian Chadd 		if (amn->amn_success >= amn->amn_success_threshold &&
314*e99cbea4SAdrian Chadd 		    rix + 1 < rs->rs_nrates) {
315*e99cbea4SAdrian Chadd 			amn->amn_recovery = 1;
316*e99cbea4SAdrian Chadd 			amn->amn_success = 0;
317*e99cbea4SAdrian Chadd 			rix++;
318*e99cbea4SAdrian Chadd 			/* XXX TODO: we really need a rate-to-string method */
319*e99cbea4SAdrian Chadd 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
320*e99cbea4SAdrian Chadd 			    "AMRR increasing rate %d Mb (txcnt=%d retrycnt=%d)",
321*e99cbea4SAdrian Chadd 			    (rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2,
322*e99cbea4SAdrian Chadd 			    amn->amn_txcnt, amn->amn_retrycnt);
323*e99cbea4SAdrian Chadd 		} else {
324*e99cbea4SAdrian Chadd 			amn->amn_recovery = 0;
325*e99cbea4SAdrian Chadd 		}
326*e99cbea4SAdrian Chadd 	} else if (is_failure(amn)) {
327*e99cbea4SAdrian Chadd 		amn->amn_success = 0;
328*e99cbea4SAdrian Chadd 		if (rix > 0) {
329*e99cbea4SAdrian Chadd 			if (amn->amn_recovery) {
330*e99cbea4SAdrian Chadd 				amn->amn_success_threshold *= 2;
331*e99cbea4SAdrian Chadd 				if (amn->amn_success_threshold >
332*e99cbea4SAdrian Chadd 				    amrr->amrr_max_success_threshold)
333*e99cbea4SAdrian Chadd 					amn->amn_success_threshold =
334*e99cbea4SAdrian Chadd 					    amrr->amrr_max_success_threshold;
335*e99cbea4SAdrian Chadd 			} else {
336*e99cbea4SAdrian Chadd 				amn->amn_success_threshold =
337*e99cbea4SAdrian Chadd 				    amrr->amrr_min_success_threshold;
338*e99cbea4SAdrian Chadd 			}
339*e99cbea4SAdrian Chadd 			rix--;
340*e99cbea4SAdrian Chadd 			/* XXX TODO: we really need a rate-to-string method */
341*e99cbea4SAdrian Chadd 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
342*e99cbea4SAdrian Chadd 			    "AMRR decreasing rate %d Mb (txcnt=%d retrycnt=%d)",
343*e99cbea4SAdrian Chadd 			    (rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2,
344*e99cbea4SAdrian Chadd 			    amn->amn_txcnt, amn->amn_retrycnt);
345*e99cbea4SAdrian Chadd 		}
346*e99cbea4SAdrian Chadd 		amn->amn_recovery = 0;
347*e99cbea4SAdrian Chadd 	}
348*e99cbea4SAdrian Chadd 
349*e99cbea4SAdrian Chadd 	return (rix);
350*e99cbea4SAdrian Chadd }
351*e99cbea4SAdrian Chadd 
352*e99cbea4SAdrian Chadd static int
353*e99cbea4SAdrian Chadd amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
354*e99cbea4SAdrian Chadd     struct ieee80211_node *ni)
355*e99cbea4SAdrian Chadd {
356*e99cbea4SAdrian Chadd 	int rix;
357*e99cbea4SAdrian Chadd 
358*e99cbea4SAdrian Chadd 	KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
359*e99cbea4SAdrian Chadd 
360*e99cbea4SAdrian Chadd 	/* 11n or not? Pick the right rateset */
361*e99cbea4SAdrian Chadd 	if (ieee80211_ht_check_tx_ht(ni))
362*e99cbea4SAdrian Chadd 		rix = amrr_update_ht(amrr, amn, ni);
363*e99cbea4SAdrian Chadd 	else
364*e99cbea4SAdrian Chadd 		rix = amrr_update_legacy(amrr, amn, ni);
365*e99cbea4SAdrian Chadd 
366b032f27cSSam Leffler 	/* reset counters */
367b032f27cSSam Leffler 	amn->amn_txcnt = 0;
368b032f27cSSam Leffler 	amn->amn_retrycnt = 0;
369b032f27cSSam Leffler 
370*e99cbea4SAdrian Chadd 	return (rix);
371b032f27cSSam Leffler }
372b032f27cSSam Leffler 
373b032f27cSSam Leffler /*
374b032f27cSSam Leffler  * Return the rate index to use in sending a data frame.
375b032f27cSSam Leffler  * Update our internal state if it's been long enough.
376b032f27cSSam Leffler  * If the rate changes we also update ni_txrate to match.
377b032f27cSSam Leffler  */
378b6108616SRui Paulo static int
379b6108616SRui Paulo amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
380b032f27cSSam Leffler {
381b6108616SRui Paulo 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
3824367c2d1SAndriy Voskoboinyk 	struct ieee80211_amrr *amrr;
383f8bf74f2SAdrian Chadd 	const struct ieee80211_rateset *rs = NULL;
384b032f27cSSam Leffler 	int rix;
385b032f27cSSam Leffler 
3864367c2d1SAndriy Voskoboinyk 	/* XXX should return -1 here, but drivers may not expect this... */
3874367c2d1SAndriy Voskoboinyk 	if (!amn)
3884367c2d1SAndriy Voskoboinyk 	{
38938075f7dSAdrian Chadd 		ieee80211_node_set_txrate_dot11rate(ni,
39038075f7dSAdrian Chadd 		    ni->ni_rates.rs_rates[0]);
3914367c2d1SAndriy Voskoboinyk 		return 0;
3924367c2d1SAndriy Voskoboinyk 	}
3934367c2d1SAndriy Voskoboinyk 
3944367c2d1SAndriy Voskoboinyk 	amrr = amn->amn_amrr;
3954367c2d1SAndriy Voskoboinyk 
396f8bf74f2SAdrian Chadd 	/* 11n or not? Pick the right rateset */
3972014462dSAdrian Chadd 	if (ieee80211_ht_check_tx_ht(ni)) {
398f8bf74f2SAdrian Chadd 		/* XXX ew */
399f8bf74f2SAdrian Chadd 		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
400f8bf74f2SAdrian Chadd 	} else {
401f8bf74f2SAdrian Chadd 		rs = &ni->ni_rates;
402f8bf74f2SAdrian Chadd 	}
403f8bf74f2SAdrian Chadd 
404b032f27cSSam Leffler 	if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) {
405b032f27cSSam Leffler 		rix = amrr_update(amrr, amn, ni);
406b032f27cSSam Leffler 		if (rix != amn->amn_rix) {
40738075f7dSAdrian Chadd 			uint8_t dot11Rate;
408b032f27cSSam Leffler 			/* update public rate */
40938075f7dSAdrian Chadd 			dot11Rate = rs->rs_rates[rix];
410f8bf74f2SAdrian Chadd 			/* XXX strip basic rate flag from txrate, if non-11n */
4112014462dSAdrian Chadd 			if (ieee80211_ht_check_tx_ht(ni))
41238075f7dSAdrian Chadd 				dot11Rate |= IEEE80211_RATE_MCS;
413f8bf74f2SAdrian Chadd 			else
41438075f7dSAdrian Chadd 				dot11Rate &= IEEE80211_RATE_VAL;
41538075f7dSAdrian Chadd 			ieee80211_node_set_txrate_dot11rate(ni, dot11Rate);
41638075f7dSAdrian Chadd 
417b032f27cSSam Leffler 			amn->amn_rix = rix;
418b032f27cSSam Leffler 		}
419b032f27cSSam Leffler 		amn->amn_ticks = ticks;
420b032f27cSSam Leffler 	} else
421b032f27cSSam Leffler 		rix = amn->amn_rix;
422b032f27cSSam Leffler 	return rix;
423b032f27cSSam Leffler }
424b032f27cSSam Leffler 
425b6108616SRui Paulo /*
426b6108616SRui Paulo  * Update statistics with tx complete status.  Ok is non-zero
427b6108616SRui Paulo  * if the packet is known to be ACK'd.  Retries has the number
428b6108616SRui Paulo  * retransmissions (i.e. xmit attempts - 1).
429b6108616SRui Paulo  */
430b6108616SRui Paulo static void
431f6930becSAndriy Voskoboinyk amrr_tx_complete(const struct ieee80211_node *ni,
432f6930becSAndriy Voskoboinyk     const struct ieee80211_ratectl_tx_status *status)
433b6108616SRui Paulo {
434b6108616SRui Paulo 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
435f6930becSAndriy Voskoboinyk 	int retries;
436f6930becSAndriy Voskoboinyk 
4374367c2d1SAndriy Voskoboinyk 	if (!amn)
4384367c2d1SAndriy Voskoboinyk 		return;
4394367c2d1SAndriy Voskoboinyk 
440f6930becSAndriy Voskoboinyk 	retries = 0;
441f6930becSAndriy Voskoboinyk 	if (status->flags & IEEE80211_RATECTL_STATUS_LONG_RETRY)
442f6930becSAndriy Voskoboinyk 		retries = status->long_retries;
443b6108616SRui Paulo 
444b6108616SRui Paulo 	amn->amn_txcnt++;
445f6930becSAndriy Voskoboinyk 	if (status->status == IEEE80211_RATECTL_TX_SUCCESS)
446b6108616SRui Paulo 		amn->amn_success++;
447b6108616SRui Paulo 	amn->amn_retrycnt += retries;
448b6108616SRui Paulo }
449b6108616SRui Paulo 
450f6930becSAndriy Voskoboinyk static void
451f6930becSAndriy Voskoboinyk amrr_tx_update_cb(void *arg, struct ieee80211_node *ni)
452f6930becSAndriy Voskoboinyk {
453f6930becSAndriy Voskoboinyk 	struct ieee80211_ratectl_tx_stats *stats = arg;
454f6930becSAndriy Voskoboinyk 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
455f6930becSAndriy Voskoboinyk 	int txcnt, success, retrycnt;
456f6930becSAndriy Voskoboinyk 
4574367c2d1SAndriy Voskoboinyk 	if (!amn)
4584367c2d1SAndriy Voskoboinyk 		return;
4594367c2d1SAndriy Voskoboinyk 
460f6930becSAndriy Voskoboinyk 	txcnt = stats->nframes;
461f6930becSAndriy Voskoboinyk 	success = stats->nsuccess;
462f6930becSAndriy Voskoboinyk 	retrycnt = 0;
463f6930becSAndriy Voskoboinyk 	if (stats->flags & IEEE80211_RATECTL_TX_STATS_RETRIES)
464f6930becSAndriy Voskoboinyk 		retrycnt = stats->nretries;
465f6930becSAndriy Voskoboinyk 
466f6930becSAndriy Voskoboinyk 	amn->amn_txcnt += txcnt;
467f6930becSAndriy Voskoboinyk 	amn->amn_success += success;
468f6930becSAndriy Voskoboinyk 	amn->amn_retrycnt += retrycnt;
469f6930becSAndriy Voskoboinyk }
470f6930becSAndriy Voskoboinyk 
471b6108616SRui Paulo /*
472b6108616SRui Paulo  * Set tx count/retry statistics explicitly.  Intended for
473b6108616SRui Paulo  * drivers that poll the device for statistics maintained
474b6108616SRui Paulo  * in the device.
475b6108616SRui Paulo  */
476b6108616SRui Paulo static void
477f6930becSAndriy Voskoboinyk amrr_tx_update(struct ieee80211vap *vap,
478f6930becSAndriy Voskoboinyk     struct ieee80211_ratectl_tx_stats *stats)
479b6108616SRui Paulo {
480b6108616SRui Paulo 
481f6930becSAndriy Voskoboinyk 	if (stats->flags & IEEE80211_RATECTL_TX_STATS_NODE)
482f6930becSAndriy Voskoboinyk 		amrr_tx_update_cb(stats, stats->ni);
483f6930becSAndriy Voskoboinyk 	else {
484f6930becSAndriy Voskoboinyk 		ieee80211_iterate_nodes_vap(&vap->iv_ic->ic_sta, vap,
485f6930becSAndriy Voskoboinyk 		    amrr_tx_update_cb, stats);
486f6930becSAndriy Voskoboinyk 	}
487b6108616SRui Paulo }
488b6108616SRui Paulo 
489b032f27cSSam Leffler static int
490b032f27cSSam Leffler amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)
491b032f27cSSam Leffler {
492b6108616SRui Paulo 	struct ieee80211vap *vap = arg1;
493b6108616SRui Paulo 	struct ieee80211_amrr *amrr = vap->iv_rs;
4944367c2d1SAndriy Voskoboinyk 	int msecs, error;
495b032f27cSSam Leffler 
4964367c2d1SAndriy Voskoboinyk 	if (!amrr)
4974367c2d1SAndriy Voskoboinyk 		return ENOMEM;
4984367c2d1SAndriy Voskoboinyk 
4994367c2d1SAndriy Voskoboinyk 	msecs = ticks_to_msecs(amrr->amrr_interval);
500b032f27cSSam Leffler 	error = sysctl_handle_int(oidp, &msecs, 0, req);
501b032f27cSSam Leffler 	if (error || !req->newptr)
502b032f27cSSam Leffler 		return error;
503b6108616SRui Paulo 	amrr_setinterval(vap, msecs);
504b032f27cSSam Leffler 	return 0;
505b032f27cSSam Leffler }
506b032f27cSSam Leffler 
507b032f27cSSam Leffler static void
508b6108616SRui Paulo amrr_sysctlattach(struct ieee80211vap *vap,
509b032f27cSSam Leffler     struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
510b032f27cSSam Leffler {
511b6108616SRui Paulo 	struct ieee80211_amrr *amrr = vap->iv_rs;
512b032f27cSSam Leffler 
5134367c2d1SAndriy Voskoboinyk 	if (!amrr)
5144367c2d1SAndriy Voskoboinyk 		return;
5154367c2d1SAndriy Voskoboinyk 
516b032f27cSSam Leffler 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
51708f5e6bbSPawel Biernacki 	    "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
51808f5e6bbSPawel Biernacki 	    vap, 0, amrr_sysctl_interval, "I", "amrr operation interval (ms)");
519b032f27cSSam Leffler 	/* XXX bounds check values */
520f88910cdSMatthew D Fleming 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
521b032f27cSSam Leffler 	    "amrr_max_sucess_threshold", CTLFLAG_RW,
522b032f27cSSam Leffler 	    &amrr->amrr_max_success_threshold, 0, "");
523f88910cdSMatthew D Fleming 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
524b032f27cSSam Leffler 	    "amrr_min_sucess_threshold", CTLFLAG_RW,
525b032f27cSSam Leffler 	    &amrr->amrr_min_success_threshold, 0, "");
52674828f25SSam Leffler }
52773931706SAdrian Chadd 
52873931706SAdrian Chadd static void
529c1475a19SConrad Meyer amrr_print_node_rate(struct ieee80211_amrr_node *amn,
530c1475a19SConrad Meyer     struct ieee80211_node *ni, struct sbuf *s)
53173931706SAdrian Chadd {
53273931706SAdrian Chadd 	int rate;
53373931706SAdrian Chadd 	struct ieee80211_rateset *rs;
53473931706SAdrian Chadd 
5352014462dSAdrian Chadd 	if (ieee80211_ht_check_tx_ht(ni)) {
53673931706SAdrian Chadd 		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
53773931706SAdrian Chadd 		rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
53873931706SAdrian Chadd 		sbuf_printf(s, "rate: MCS %d\n", rate);
53973931706SAdrian Chadd 	} else {
54073931706SAdrian Chadd 		rs = &ni->ni_rates;
54173931706SAdrian Chadd 		rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
54273931706SAdrian Chadd 		sbuf_printf(s, "rate: %d Mbit\n", rate / 2);
54373931706SAdrian Chadd 	}
544c1475a19SConrad Meyer }
54573931706SAdrian Chadd 
546c1475a19SConrad Meyer static void
547c1475a19SConrad Meyer amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s)
548c1475a19SConrad Meyer {
549c1475a19SConrad Meyer 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
550c1475a19SConrad Meyer 
551c1475a19SConrad Meyer 	/* XXX TODO: check locking? */
552c1475a19SConrad Meyer 
553c1475a19SConrad Meyer 	if (!amn)
554c1475a19SConrad Meyer 		return;
555c1475a19SConrad Meyer 
556c1475a19SConrad Meyer 	amrr_print_node_rate(amn, ni, s);
55773931706SAdrian Chadd 	sbuf_printf(s, "ticks: %d\n", amn->amn_ticks);
55873931706SAdrian Chadd 	sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt);
55973931706SAdrian Chadd 	sbuf_printf(s, "success: %u\n", amn->amn_success);
56073931706SAdrian Chadd 	sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold);
56173931706SAdrian Chadd 	sbuf_printf(s, "recovery: %u\n", amn->amn_recovery);
56273931706SAdrian Chadd 	sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt);
56373931706SAdrian Chadd }
564