2 /* $NetBSD: ieee80211_rssadapt.c,v 1.9 2005/02/26 22:45:09 perry Exp $ */
4 * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org>
5 * Copyright (c) 2003, 2004 David Young. All rights reserved.
7 * Redistribution and use in source and binary forms, with or
8 * without modification, are permitted provided that the following
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
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
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>
44 #include <net/if_var.h>
45 #include <net/if_media.h>
46 #include <net/ethernet.h>
48 #include <netproto/802_11/ieee80211_var.h>
49 #include <netproto/802_11/ieee80211_rssadapt.h>
50 #include <netproto/802_11/ieee80211_ratectl.h>
52 struct rssadapt_expavgctl
{
53 /* RSS threshold decay. */
56 /* RSS threshold update. */
57 u_int rc_thresh_denom
;
59 /* RSS average update. */
60 u_int rc_avgrssi_denom
;
64 static struct rssadapt_expavgctl master_expavgctl
= {
69 .rc_avgrssi_denom
= 8,
76 #define interpolate(parm, old, new) ((parm##_old * (old) + \
77 (parm##_denom - parm##_old) * (new)) / \
80 static void rssadapt_setinterval(const struct ieee80211vap
*, int);
81 static void rssadapt_init(struct ieee80211vap
*);
82 static void rssadapt_deinit(struct ieee80211vap
*);
83 static void rssadapt_updatestats(struct ieee80211_rssadapt_node
*);
84 static void rssadapt_node_init(struct ieee80211_node
*);
85 static void rssadapt_node_deinit(struct ieee80211_node
*);
86 static int rssadapt_rate(struct ieee80211_node
*, void *, uint32_t);
87 static void rssadapt_lower_rate(struct ieee80211_rssadapt_node
*, int, int);
88 static void rssadapt_raise_rate(struct ieee80211_rssadapt_node
*,
90 static void rssadapt_tx_complete(const struct ieee80211vap
*,
91 const struct ieee80211_node
*, int,
93 static void rssadapt_sysctlattach(struct ieee80211vap
*,
94 struct sysctl_ctx_list
*, struct sysctl_oid
*);
96 /* number of references from net80211 layer */
99 static const struct ieee80211_ratectl rssadapt
= {
100 .ir_name
= "rssadapt",
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
,
112 IEEE80211_RATECTL_MODULE(rssadapt
, 1);
113 IEEE80211_RATECTL_ALG(rssadapt
, IEEE80211_RATECTL_RSSADAPT
, rssadapt
);
116 rssadapt_setinterval(const struct ieee80211vap
*vap
, int msecs
)
118 struct ieee80211_rssadapt
*rs
= vap
->iv_rs
;
123 t
= msecs_to_ticks(msecs
);
124 rs
->interval
= (t
< 1) ? 1 : t
;
128 rssadapt_init(struct ieee80211vap
*vap
)
130 struct ieee80211_rssadapt
*rs
;
132 KASSERT(vap
->iv_rs
== NULL
, ("%s: iv_rs already initialized",
135 #if defined(__DragonFly__)
136 vap
->iv_rs
= rs
= kmalloc(sizeof(struct ieee80211_rssadapt
),
137 M_80211_RATECTL
, M_INTWAIT
|M_ZERO
);
139 vap
->iv_rs
= rs
= IEEE80211_MALLOC(sizeof(struct ieee80211_rssadapt
),
140 M_80211_RATECTL
, IEEE80211_M_NOWAIT
| IEEE80211_M_ZERO
);
143 if_printf(vap
->iv_ifp
, "couldn't alloc ratectl structure\n");
147 rssadapt_setinterval(vap
, 500 /* msecs */);
148 rssadapt_sysctlattach(vap
, vap
->iv_sysctl
, vap
->iv_oid
);
152 rssadapt_deinit(struct ieee80211vap
*vap
)
154 IEEE80211_FREE(vap
->iv_rs
, M_80211_RATECTL
);
158 rssadapt_updatestats(struct ieee80211_rssadapt_node
*ra
)
162 ra
->ra_pktrate
= (ra
->ra_pktrate
+ 10*(ra
->ra_nfail
+ ra
->ra_nok
))/2;
163 ra
->ra_nfail
= ra
->ra_nok
= 0;
166 * A node is eligible for its rate to be raised every 1/10 to 10
167 * seconds, more eligible in proportion to recent packet rates.
169 interval
= MAX(10*1000, 10*1000 / MAX(1, 10 * ra
->ra_pktrate
));
170 ra
->ra_raise_interval
= msecs_to_ticks(interval
);
174 rssadapt_node_init(struct ieee80211_node
*ni
)
176 struct ieee80211_rssadapt_node
*ra
;
177 struct ieee80211vap
*vap
= ni
->ni_vap
;
178 struct ieee80211_rssadapt
*rsa
= vap
->iv_rs
;
179 const struct ieee80211_rateset
*rs
= &ni
->ni_rates
;
181 if (ni
->ni_rctls
== NULL
) {
183 #if defined(__DragonFly__)
184 kmalloc(sizeof(struct ieee80211_rssadapt_node
),
185 M_80211_RATECTL
, M_INTWAIT
|M_ZERO
);
187 IEEE80211_MALLOC(sizeof(struct ieee80211_rssadapt_node
),
188 M_80211_RATECTL
, IEEE80211_M_NOWAIT
| IEEE80211_M_ZERO
);
191 if_printf(vap
->iv_ifp
, "couldn't alloc per-node ratectl "
199 rssadapt_updatestats(ra
);
201 /* pick initial rate */
202 for (ra
->ra_rix
= rs
->rs_nrates
- 1;
203 ra
->ra_rix
> 0 && (rs
->rs_rates
[ra
->ra_rix
] & IEEE80211_RATE_VAL
) > 72;
206 ni
->ni_txrate
= rs
->rs_rates
[ra
->ra_rix
] & IEEE80211_RATE_VAL
;
207 ra
->ra_ticks
= ticks
;
209 IEEE80211_NOTE(ni
->ni_vap
, IEEE80211_MSG_RATECTL
, ni
,
210 "RSSADAPT initial rate %d", ni
->ni_txrate
);
214 rssadapt_node_deinit(struct ieee80211_node
*ni
)
217 IEEE80211_FREE(ni
->ni_rctls
, M_80211_RATECTL
);
225 for (i
= 0, top
= IEEE80211_RSSADAPT_BKT0
;
226 i
< IEEE80211_RSSADAPT_BKTS
;
227 i
++, top
<<= IEEE80211_RSSADAPT_BKTPOWER
) {
236 rssadapt_rate(struct ieee80211_node
*ni
, void *arg __unused
, uint32_t iarg
)
238 struct ieee80211_rssadapt_node
*ra
= ni
->ni_rctls
;
240 const struct ieee80211_rateset
*rs
= &ra
->ra_rates
;
241 uint16_t (*thrs
)[IEEE80211_RATE_SIZE
];
244 if ((ticks
- ra
->ra_ticks
) > ra
->ra_rs
->interval
) {
245 rssadapt_updatestats(ra
);
246 ra
->ra_ticks
= ticks
;
249 thrs
= &ra
->ra_rate_thresh
[bucket(pktlen
)];
251 /* XXX this is average rssi, should be using last value */
252 rssi
= ni
->ni_ic
->ic_node_getrssi(ni
);
253 for (rix
= rs
->rs_nrates
-1; rix
>= 0; rix
--)
254 if ((*thrs
)[rix
] < (rssi
<< 8))
256 if (rix
!= ra
->ra_rix
) {
257 /* update public rate */
258 ni
->ni_txrate
= ni
->ni_rates
.rs_rates
[rix
] & IEEE80211_RATE_VAL
;
261 IEEE80211_NOTE(ni
->ni_vap
, IEEE80211_MSG_RATECTL
, ni
,
262 "RSSADAPT new rate %d (pktlen %d rssi %d)",
263 ni
->ni_txrate
, pktlen
, rssi
);
269 * Adapt the data rate to suit the conditions. When a transmitted
270 * packet is dropped after RAL_RSSADAPT_RETRY_LIMIT retransmissions,
271 * raise the RSS threshold for transmitting packets of similar length at
272 * the same data rate.
275 rssadapt_lower_rate(struct ieee80211_rssadapt_node
*ra
, int pktlen
, int rssi
)
278 uint16_t (*thrs
)[IEEE80211_RATE_SIZE
];
281 thrs
= &ra
->ra_rate_thresh
[bucket(pktlen
)];
284 last_thr
= (*thrs
)[rix
];
285 (*thrs
)[rix
] = interpolate(master_expavgctl
.rc_thresh
,
286 last_thr
, (rssi
<< 8));
288 IEEE80211_DPRINTF(ra
->ra_rs
->vap
, IEEE80211_MSG_RATECTL
,
289 "RSSADAPT lower threshold for rate %d (last_thr %d new thr %d rssi %d)\n",
290 ra
->ra_rates
.rs_rates
[rix
+ 1] & IEEE80211_RATE_VAL
,
291 last_thr
, (*thrs
)[rix
], rssi
);
295 rssadapt_raise_rate(struct ieee80211_rssadapt_node
*ra
, int pktlen
, int rssi
)
297 uint16_t (*thrs
)[IEEE80211_RATE_SIZE
];
298 uint16_t newthr
, oldthr
;
301 thrs
= &ra
->ra_rate_thresh
[bucket(pktlen
)];
304 if ((*thrs
)[rix
+ 1] > (*thrs
)[rix
]) {
305 oldthr
= (*thrs
)[rix
+ 1];
306 if ((*thrs
)[rix
] == 0)
307 newthr
= (rssi
<< 8);
309 newthr
= (*thrs
)[rix
];
310 (*thrs
)[rix
+ 1] = interpolate(master_expavgctl
.rc_decay
,
313 IEEE80211_DPRINTF(ra
->ra_rs
->vap
, IEEE80211_MSG_RATECTL
,
314 "RSSADAPT raise threshold for rate %d (oldthr %d newthr %d rssi %d)\n",
315 ra
->ra_rates
.rs_rates
[rix
+ 1] & IEEE80211_RATE_VAL
,
316 oldthr
, newthr
, rssi
);
318 ra
->ra_last_raise
= ticks
;
323 rssadapt_tx_complete(const struct ieee80211vap
*vap
,
324 const struct ieee80211_node
*ni
, int success
, void *arg1
, void *arg2
)
326 struct ieee80211_rssadapt_node
*ra
= ni
->ni_rctls
;
327 int pktlen
= *(int *)arg1
, rssi
= *(int *)arg2
;
331 if ((ra
->ra_rix
+ 1) < ra
->ra_rates
.rs_nrates
&&
332 (ticks
- ra
->ra_last_raise
) >= ra
->ra_raise_interval
)
333 rssadapt_raise_rate(ra
, pktlen
, rssi
);
336 rssadapt_lower_rate(ra
, pktlen
, rssi
);
341 rssadapt_sysctl_interval(SYSCTL_HANDLER_ARGS
)
343 struct ieee80211vap
*vap
= arg1
;
344 struct ieee80211_rssadapt
*rs
= vap
->iv_rs
;
345 int msecs
= ticks_to_msecs(rs
->interval
);
348 error
= sysctl_handle_int(oidp
, &msecs
, 0, req
);
349 if (error
|| !req
->newptr
)
351 rssadapt_setinterval(vap
, msecs
);
356 rssadapt_sysctlattach(struct ieee80211vap
*vap
,
357 struct sysctl_ctx_list
*ctx
, struct sysctl_oid
*tree
)
360 SYSCTL_ADD_PROC(ctx
, SYSCTL_CHILDREN(tree
), OID_AUTO
,
361 "rssadapt_rate_interval", CTLTYPE_INT
| CTLFLAG_RW
, vap
,
362 0, rssadapt_sysctl_interval
, "I", "rssadapt operation interval (ms)");