1/*	$FreeBSD: stable/11/sys/net80211/ieee80211_rssadapt.c 343035 2019-01-15 02:16:23Z avos $	*/
2/* $NetBSD: ieee80211_rssadapt.c,v 1.9 2005/02/26 22:45:09 perry Exp $ */
3/*-
4 * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org>
5 * Copyright (c) 2003, 2004 David Young.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or
8 * without modification, are permitted provided that the following
9 * conditions are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above
13 *    copyright notice, this list of conditions and the following
14 *    disclaimer in the documentation and/or other materials provided
15 *    with the distribution.
16 * 3. The name of David Young may not be used to endorse or promote
17 *    products derived from this software without specific prior
18 *    written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY
21 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23 * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL David
24 * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
26 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
31 * OF SUCH DAMAGE.
32 */
33#include "opt_wlan.h"
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/kernel.h>
38#include <sys/malloc.h>
39#include <sys/module.h>
40#include <sys/socket.h>
41#include <sys/sysctl.h>
42
43#include <net/if.h>
44#include <net/if_var.h>
45#include <net/if_media.h>
46#include <net/ethernet.h>
47
48#include <net80211/ieee80211_var.h>
49#include <net80211/ieee80211_rssadapt.h>
50#include <net80211/ieee80211_ratectl.h>
51
52struct rssadapt_expavgctl {
53	/* RSS threshold decay. */
54	u_int rc_decay_denom;
55	u_int rc_decay_old;
56	/* RSS threshold update. */
57	u_int rc_thresh_denom;
58	u_int rc_thresh_old;
59	/* RSS average update. */
60	u_int rc_avgrssi_denom;
61	u_int rc_avgrssi_old;
62};
63
64static struct rssadapt_expavgctl master_expavgctl = {
65	.rc_decay_denom = 16,
66	.rc_decay_old = 15,
67	.rc_thresh_denom = 8,
68	.rc_thresh_old = 4,
69	.rc_avgrssi_denom = 8,
70	.rc_avgrssi_old = 4
71};
72
73#ifdef interpolate
74#undef interpolate
75#endif
76#define interpolate(parm, old, new) ((parm##_old * (old) + \
77                                     (parm##_denom - parm##_old) * (new)) / \
78				    parm##_denom)
79
80static void	rssadapt_setinterval(const struct ieee80211vap *, int);
81static void	rssadapt_init(struct ieee80211vap *);
82static void	rssadapt_deinit(struct ieee80211vap *);
83static void	rssadapt_updatestats(struct ieee80211_rssadapt_node *);
84static void	rssadapt_node_init(struct ieee80211_node *);
85static void	rssadapt_node_deinit(struct ieee80211_node *);
86static int	rssadapt_rate(struct ieee80211_node *, void *, uint32_t);
87static void	rssadapt_lower_rate(struct ieee80211_rssadapt_node *, int, int);
88static void	rssadapt_raise_rate(struct ieee80211_rssadapt_node *,
89			int, int);
90static void	rssadapt_tx_complete(const struct ieee80211vap *,
91    			const struct ieee80211_node *, int,
92			void *, void *);
93static void	rssadapt_sysctlattach(struct ieee80211vap *,
94			struct sysctl_ctx_list *, struct sysctl_oid *);
95
96/* number of references from net80211 layer */
97static	int nrefs = 0;
98
99static const struct ieee80211_ratectl rssadapt = {
100	.ir_name	= "rssadapt",
101	.ir_attach	= NULL,
102	.ir_detach	= NULL,
103	.ir_init	= rssadapt_init,
104	.ir_deinit	= rssadapt_deinit,
105	.ir_node_init	= rssadapt_node_init,
106	.ir_node_deinit	= rssadapt_node_deinit,
107	.ir_rate	= rssadapt_rate,
108	.ir_tx_complete	= rssadapt_tx_complete,
109	.ir_tx_update	= NULL,
110	.ir_setinterval	= rssadapt_setinterval,
111};
112IEEE80211_RATECTL_MODULE(rssadapt, 1);
113IEEE80211_RATECTL_ALG(rssadapt, IEEE80211_RATECTL_RSSADAPT, rssadapt);
114
115static void
116rssadapt_setinterval(const struct ieee80211vap *vap, int msecs)
117{
118	struct ieee80211_rssadapt *rs = vap->iv_rs;
119	int t;
120
121	if (!rs)
122		return;
123
124	if (msecs < 100)
125		msecs = 100;
126	t = msecs_to_ticks(msecs);
127	rs->interval = (t < 1) ? 1 : t;
128}
129
130static void
131rssadapt_init(struct ieee80211vap *vap)
132{
133	struct ieee80211_rssadapt *rs;
134
135	KASSERT(vap->iv_rs == NULL, ("%s: iv_rs already initialized",
136	    __func__));
137
138	nrefs++;		/* XXX locking */
139	vap->iv_rs = rs = IEEE80211_MALLOC(sizeof(struct ieee80211_rssadapt),
140	    M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
141	if (rs == NULL) {
142		if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n");
143		return;
144	}
145	rs->vap = vap;
146	rssadapt_setinterval(vap, 500 /* msecs */);
147	rssadapt_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid);
148}
149
150static void
151rssadapt_deinit(struct ieee80211vap *vap)
152{
153	IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL);
154	KASSERT(nrefs > 0, ("imbalanced attach/detach"));
155	nrefs--;		/* XXX locking */
156}
157
158static void
159rssadapt_updatestats(struct ieee80211_rssadapt_node *ra)
160{
161	long interval;
162
163	ra->ra_pktrate = (ra->ra_pktrate + 10*(ra->ra_nfail + ra->ra_nok))/2;
164	ra->ra_nfail = ra->ra_nok = 0;
165
166	/*
167	 * A node is eligible for its rate to be raised every 1/10 to 10
168	 * seconds, more eligible in proportion to recent packet rates.
169	 */
170	interval = MAX(10*1000, 10*1000 / MAX(1, 10 * ra->ra_pktrate));
171	ra->ra_raise_interval = msecs_to_ticks(interval);
172}
173
174static void
175rssadapt_node_init(struct ieee80211_node *ni)
176{
177	struct ieee80211_rssadapt_node *ra;
178	struct ieee80211vap *vap = ni->ni_vap;
179	struct ieee80211_rssadapt *rsa = vap->iv_rs;
180	const struct ieee80211_rateset *rs = &ni->ni_rates;
181
182	if (!rsa) {
183		if_printf(vap->iv_ifp, "ratectl structure was not allocated, "
184		    "per-node structure allocation skipped\n");
185		return;
186	}
187
188	if (ni->ni_rctls == NULL) {
189		ni->ni_rctls = ra =
190		    IEEE80211_MALLOC(sizeof(struct ieee80211_rssadapt_node),
191		        M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
192		if (ra == NULL) {
193			if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl "
194			    "structure\n");
195			return;
196		}
197	} else
198		ra = ni->ni_rctls;
199	ra->ra_rs = rsa;
200	ra->ra_rates = *rs;
201	rssadapt_updatestats(ra);
202
203	/* pick initial rate */
204	for (ra->ra_rix = rs->rs_nrates - 1;
205	     ra->ra_rix > 0 && (rs->rs_rates[ra->ra_rix] & IEEE80211_RATE_VAL) > 72;
206	     ra->ra_rix--)
207		;
208	ni->ni_txrate = rs->rs_rates[ra->ra_rix] & IEEE80211_RATE_VAL;
209	ra->ra_ticks = ticks;
210
211	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
212	    "RSSADAPT initial rate %d", ni->ni_txrate);
213}
214
215static void
216rssadapt_node_deinit(struct ieee80211_node *ni)
217{
218
219	IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL);
220}
221
222static __inline int
223bucket(int pktlen)
224{
225	int i, top, thridx;
226
227	for (i = 0, top = IEEE80211_RSSADAPT_BKT0;
228	     i < IEEE80211_RSSADAPT_BKTS;
229	     i++, top <<= IEEE80211_RSSADAPT_BKTPOWER) {
230		thridx = i;
231		if (pktlen <= top)
232			break;
233	}
234	return thridx;
235}
236
237static int
238rssadapt_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg)
239{
240	struct ieee80211_rssadapt_node *ra = ni->ni_rctls;
241	u_int pktlen = iarg;
242	const struct ieee80211_rateset *rs;
243	uint16_t (*thrs)[IEEE80211_RATE_SIZE];
244	int rix, rssi;
245
246	/* XXX should return -1 here, but drivers may not expect this... */
247	if (!ra)
248	{
249		ni->ni_txrate = ni->ni_rates.rs_rates[0];
250		return 0;
251	}
252
253	rs = &ra->ra_rates;
254	if ((ticks - ra->ra_ticks) > ra->ra_rs->interval) {
255		rssadapt_updatestats(ra);
256		ra->ra_ticks = ticks;
257	}
258
259	thrs = &ra->ra_rate_thresh[bucket(pktlen)];
260
261	/* XXX this is average rssi, should be using last value */
262	rssi = ni->ni_ic->ic_node_getrssi(ni);
263	for (rix = rs->rs_nrates-1; rix >= 0; rix--)
264		if ((*thrs)[rix] < (rssi << 8))
265			break;
266	if (rix != ra->ra_rix) {
267		/* update public rate */
268		ni->ni_txrate = ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL;
269		ra->ra_rix = rix;
270
271		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
272		    "RSSADAPT new rate %d (pktlen %d rssi %d)",
273		    ni->ni_txrate, pktlen, rssi);
274	}
275	return rix;
276}
277
278/*
279 * Adapt the data rate to suit the conditions.  When a transmitted
280 * packet is dropped after RAL_RSSADAPT_RETRY_LIMIT retransmissions,
281 * raise the RSS threshold for transmitting packets of similar length at
282 * the same data rate.
283 */
284static void
285rssadapt_lower_rate(struct ieee80211_rssadapt_node *ra, int pktlen, int rssi)
286{
287	uint16_t last_thr;
288	uint16_t (*thrs)[IEEE80211_RATE_SIZE];
289	u_int rix;
290
291	thrs = &ra->ra_rate_thresh[bucket(pktlen)];
292
293	rix = ra->ra_rix;
294	last_thr = (*thrs)[rix];
295	(*thrs)[rix] = interpolate(master_expavgctl.rc_thresh,
296	    last_thr, (rssi << 8));
297
298	IEEE80211_DPRINTF(ra->ra_rs->vap, IEEE80211_MSG_RATECTL,
299	    "RSSADAPT lower threshold for rate %d (last_thr %d new thr %d rssi %d)\n",
300	    ra->ra_rates.rs_rates[rix + 1] & IEEE80211_RATE_VAL,
301	    last_thr, (*thrs)[rix], rssi);
302}
303
304static void
305rssadapt_raise_rate(struct ieee80211_rssadapt_node *ra, int pktlen, int rssi)
306{
307	uint16_t (*thrs)[IEEE80211_RATE_SIZE];
308	uint16_t newthr, oldthr;
309	int rix;
310
311	thrs = &ra->ra_rate_thresh[bucket(pktlen)];
312
313	rix = ra->ra_rix;
314	if ((*thrs)[rix + 1] > (*thrs)[rix]) {
315		oldthr = (*thrs)[rix + 1];
316		if ((*thrs)[rix] == 0)
317			newthr = (rssi << 8);
318		else
319			newthr = (*thrs)[rix];
320		(*thrs)[rix + 1] = interpolate(master_expavgctl.rc_decay,
321		    oldthr, newthr);
322
323		IEEE80211_DPRINTF(ra->ra_rs->vap, IEEE80211_MSG_RATECTL,
324		    "RSSADAPT raise threshold for rate %d (oldthr %d newthr %d rssi %d)\n",
325		    ra->ra_rates.rs_rates[rix + 1] & IEEE80211_RATE_VAL,
326		    oldthr, newthr, rssi);
327
328		ra->ra_last_raise = ticks;
329	}
330}
331
332static void
333rssadapt_tx_complete(const struct ieee80211vap *vap,
334    const struct ieee80211_node *ni, int success, void *arg1, void *arg2)
335{
336	struct ieee80211_rssadapt_node *ra = ni->ni_rctls;
337	int pktlen = *(int *)arg1, rssi = *(int *)arg2;
338
339	if (!ra)
340		return;
341
342	if (success) {
343		ra->ra_nok++;
344		if ((ra->ra_rix + 1) < ra->ra_rates.rs_nrates &&
345		    (ticks - ra->ra_last_raise) >= ra->ra_raise_interval)
346			rssadapt_raise_rate(ra, pktlen, rssi);
347	} else {
348		ra->ra_nfail++;
349		rssadapt_lower_rate(ra, pktlen, rssi);
350	}
351}
352
353static int
354rssadapt_sysctl_interval(SYSCTL_HANDLER_ARGS)
355{
356	struct ieee80211vap *vap = arg1;
357	struct ieee80211_rssadapt *rs = vap->iv_rs;
358	int msecs, error;
359
360	if (!rs)
361		return ENOMEM;
362
363	msecs = ticks_to_msecs(rs->interval);
364	error = sysctl_handle_int(oidp, &msecs, 0, req);
365	if (error || !req->newptr)
366		return error;
367	rssadapt_setinterval(vap, msecs);
368	return 0;
369}
370
371static void
372rssadapt_sysctlattach(struct ieee80211vap *vap,
373    struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
374{
375
376	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
377	    "rssadapt_rate_interval", CTLTYPE_INT | CTLFLAG_RW, vap,
378	    0, rssadapt_sysctl_interval, "I", "rssadapt operation interval (ms)");
379}
380