1 | /* $NetBSD: ieee80211_rssadapt.c,v 1.21 2016/09/27 20:20:06 christos Exp $ */ |
2 | /*- |
3 | * Copyright (c) 2003, 2004 David Young. All rights reserved. |
4 | * |
5 | * Redistribution and use in source and binary forms, with or |
6 | * without modification, are permitted provided that the following |
7 | * conditions are met: |
8 | * 1. Redistributions of source code must retain the above copyright |
9 | * notice, this list of conditions and the following disclaimer. |
10 | * 2. Redistributions in binary form must reproduce the above |
11 | * copyright notice, this list of conditions and the following |
12 | * disclaimer in the documentation and/or other materials provided |
13 | * with the distribution. |
14 | * |
15 | * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY |
16 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
17 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A |
18 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David |
19 | * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
20 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
22 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
26 | * OF SUCH DAMAGE. |
27 | */ |
28 | |
29 | #include <sys/cdefs.h> |
30 | #ifdef __NetBSD__ |
31 | __KERNEL_RCSID(0, "$NetBSD: ieee80211_rssadapt.c,v 1.21 2016/09/27 20:20:06 christos Exp $" ); |
32 | #endif |
33 | |
34 | #include <sys/param.h> |
35 | #include <sys/types.h> |
36 | #include <sys/kernel.h> /* for hz */ |
37 | #include <sys/sysctl.h> |
38 | |
39 | #include <net/if.h> |
40 | #include <net/if_media.h> |
41 | #include <net/if_ether.h> |
42 | |
43 | #include <net80211/ieee80211_netbsd.h> |
44 | #include <net80211/ieee80211_var.h> |
45 | #include <net80211/ieee80211.h> |
46 | #include <net80211/ieee80211_rssadapt.h> |
47 | |
48 | #ifdef interpolate |
49 | #undef interpolate |
50 | #endif |
51 | #define interpolate(parm, old, new) ((parm##_old * (old) + \ |
52 | (parm##_denom - parm##_old) * (new)) / \ |
53 | parm##_denom) |
54 | |
55 | #ifdef IEEE80211_DEBUG |
56 | static struct timeval lastrateadapt; /* time of last rate adaptation msg */ |
57 | static int = 0; /* rate-adaptation msgs this second */ |
58 | static int ieee80211_adaptrate = 4; /* rate-adaptation max msgs/sec */ |
59 | |
60 | #define () \ |
61 | ((ieee80211_rssadapt_debug > 0) && \ |
62 | ppsratecheck(&lastrateadapt, &currssadaptps, ieee80211_adaptrate)) |
63 | #define (X) \ |
64 | if (RSSADAPT_DO_PRINT()) \ |
65 | printf X |
66 | |
67 | int = 0; |
68 | |
69 | #else |
70 | #define RSSADAPT_DO_PRINT() (0) |
71 | #define RSSADAPT_PRINTF(X) |
72 | #endif |
73 | |
74 | static struct ieee80211_rssadapt_expavgctl master_expavgctl = { |
75 | .rc_decay_denom = 16, |
76 | .rc_decay_old = 15, |
77 | .rc_thresh_denom = 8, |
78 | .rc_thresh_old = 4, |
79 | .rc_avgrssi_denom = 8, |
80 | .rc_avgrssi_old = 4 |
81 | }; |
82 | |
83 | #ifdef __NetBSD__ |
84 | #ifdef IEEE80211_DEBUG |
85 | /* TBD factor with sysctl_ath_verify, sysctl_ieee80211_verify. */ |
86 | static int |
87 | (SYSCTLFN_ARGS) |
88 | { |
89 | int error, t; |
90 | struct sysctlnode node; |
91 | |
92 | node = *rnode; |
93 | t = *(int*)rnode->sysctl_data; |
94 | node.sysctl_data = &t; |
95 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
96 | if (error || newp == NULL) |
97 | return (error); |
98 | |
99 | if (t < 0 || t > 2) |
100 | return (EINVAL); |
101 | *(int*)rnode->sysctl_data = t; |
102 | |
103 | return (0); |
104 | } |
105 | #endif /* IEEE80211_DEBUG */ |
106 | |
107 | /* TBD factor with sysctl_ath_verify, sysctl_ieee80211_verify. */ |
108 | static int |
109 | (SYSCTLFN_ARGS) |
110 | { |
111 | struct ieee80211_rssadapt_expavgctl rc; |
112 | int error; |
113 | struct sysctlnode node; |
114 | |
115 | node = *rnode; |
116 | rc = *(struct ieee80211_rssadapt_expavgctl *)rnode->sysctl_data; |
117 | node.sysctl_data = &rc; |
118 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
119 | if (error || newp == NULL) |
120 | return (error); |
121 | |
122 | if (/* rc.rc_decay_old < 0 || */ |
123 | rc.rc_decay_denom < rc.rc_decay_old) |
124 | return (EINVAL); |
125 | |
126 | if (/* rc.rc_thresh_old < 0 || */ |
127 | rc.rc_thresh_denom < rc.rc_thresh_old) |
128 | return (EINVAL); |
129 | |
130 | if (/* rc.rc_avgrssi_old < 0 || */ |
131 | rc.rc_avgrssi_denom < rc.rc_avgrssi_old) |
132 | return (EINVAL); |
133 | |
134 | *(struct ieee80211_rssadapt_expavgctl *)rnode->sysctl_data = rc; |
135 | |
136 | return (0); |
137 | } |
138 | |
139 | /* |
140 | * Setup sysctl(3) MIB, net.ieee80211.* |
141 | * |
142 | * TBD condition CTLFLAG_PERMANENT on being a module or not |
143 | */ |
144 | void |
145 | (struct sysctllog **clog) |
146 | { |
147 | int rc; |
148 | const struct sysctlnode *node; |
149 | |
150 | if ((rc = sysctl_createv(clog, 0, NULL, &node, |
151 | CTLFLAG_PERMANENT, CTLTYPE_NODE, "link" , NULL, |
152 | NULL, 0, NULL, 0, CTL_NET, PF_LINK, CTL_EOL)) != 0) |
153 | goto err; |
154 | |
155 | if ((rc = sysctl_createv(clog, 0, &node, &node, |
156 | CTLFLAG_PERMANENT, CTLTYPE_NODE, "ieee80211" , NULL, |
157 | NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0) |
158 | goto err; |
159 | |
160 | if ((rc = sysctl_createv(clog, 0, &node, &node, |
161 | CTLFLAG_PERMANENT, CTLTYPE_NODE, "rssadapt" , |
162 | SYSCTL_DESCR("Received Signal Strength adaptation controls" ), |
163 | NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0) |
164 | goto err; |
165 | |
166 | #ifdef IEEE80211_DEBUG |
167 | /* control debugging printfs */ |
168 | if ((rc = sysctl_createv(clog, 0, &node, NULL, |
169 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "debug" , |
170 | SYSCTL_DESCR("Enable RSS adaptation debugging output" ), |
171 | sysctl_ieee80211_rssadapt_debug, 0, &ieee80211_rssadapt_debug, 0, |
172 | CTL_CREATE, CTL_EOL)) != 0) |
173 | goto err; |
174 | #endif /* IEEE80211_DEBUG */ |
175 | |
176 | /* control rate of decay for exponential averages */ |
177 | if ((rc = sysctl_createv(clog, 0, &node, NULL, |
178 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_STRUCT, |
179 | "expavgctl" , SYSCTL_DESCR("RSS exponential averaging control" ), |
180 | sysctl_ieee80211_rssadapt_expavgctl, 0, |
181 | &master_expavgctl, sizeof(master_expavgctl), CTL_CREATE, |
182 | CTL_EOL)) != 0) |
183 | goto err; |
184 | |
185 | return; |
186 | err: |
187 | printf("%s: sysctl_createv failed (rc = %d)\n" , __func__, rc); |
188 | } |
189 | #endif /* __NetBSD__ */ |
190 | |
191 | int |
192 | (struct ieee80211_rssadapt *ra, |
193 | struct ieee80211_rateset *rs, struct ieee80211_frame *wh, u_int len, |
194 | int fixed_rate, const char *dvname, int do_not_adapt) |
195 | { |
196 | u_int16_t (*thrs)[IEEE80211_RATE_SIZE]; |
197 | int flags = 0, i, rateidx = 0, thridx, top; |
198 | |
199 | if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) |
200 | flags |= IEEE80211_RATE_BASIC; |
201 | |
202 | for (i = 0, top = IEEE80211_RSSADAPT_BKT0; |
203 | i < IEEE80211_RSSADAPT_BKTS; |
204 | i++, top <<= IEEE80211_RSSADAPT_BKTPOWER) { |
205 | thridx = i; |
206 | if (len <= top) |
207 | break; |
208 | } |
209 | |
210 | thrs = &ra->ra_rate_thresh[thridx]; |
211 | |
212 | if (fixed_rate != -1) { |
213 | if ((rs->rs_rates[fixed_rate] & flags) == flags) { |
214 | rateidx = fixed_rate; |
215 | goto out; |
216 | } |
217 | flags |= IEEE80211_RATE_BASIC; |
218 | i = fixed_rate; |
219 | } else |
220 | i = rs->rs_nrates; |
221 | |
222 | while (--i >= 0) { |
223 | rateidx = i; |
224 | if ((rs->rs_rates[i] & flags) != flags) |
225 | continue; |
226 | if (do_not_adapt) |
227 | break; |
228 | if ((*thrs)[i] < ra->ra_avg_rssi) |
229 | break; |
230 | } |
231 | |
232 | out: |
233 | #ifdef IEEE80211_DEBUG |
234 | if (ieee80211_rssadapt_debug && dvname != NULL) { |
235 | printf("%s: dst %s threshold[%d, %d.%d] %d < %d\n" , |
236 | dvname, ether_sprintf(wh->i_addr1), len, |
237 | (rs->rs_rates[rateidx] & IEEE80211_RATE_VAL) / 2, |
238 | (rs->rs_rates[rateidx] & IEEE80211_RATE_VAL) * 5 % 10, |
239 | (*thrs)[rateidx], ra->ra_avg_rssi); |
240 | } |
241 | #endif /* IEEE80211_DEBUG */ |
242 | return rateidx; |
243 | } |
244 | |
245 | void |
246 | (struct ieee80211_rssadapt *ra) |
247 | { |
248 | long interval; |
249 | |
250 | ra->ra_pktrate = |
251 | (ra->ra_pktrate + 10 * (ra->ra_nfail + ra->ra_nok)) / 2; |
252 | ra->ra_nfail = ra->ra_nok = 0; |
253 | |
254 | /* a node is eligible for its rate to be raised every 1/10 to 10 |
255 | * seconds, more eligible in proportion to recent packet rates. |
256 | */ |
257 | interval = MAX(100000, 10000000 / MAX(1, 10 * ra->ra_pktrate)); |
258 | ra->ra_raise_interval.tv_sec = interval / (1000 * 1000); |
259 | ra->ra_raise_interval.tv_usec = interval % (1000 * 1000); |
260 | } |
261 | |
262 | void |
263 | (struct ieee80211com *ic, struct ieee80211_node *ni, |
264 | struct ieee80211_rssadapt *ra, int ) |
265 | { |
266 | #ifdef IEEE80211_DEBUG |
267 | int = ra->ra_avg_rssi; |
268 | #endif |
269 | |
270 | ra->ra_avg_rssi = interpolate(master_expavgctl.rc_avgrssi, |
271 | ra->ra_avg_rssi, (rssi << 8)); |
272 | |
273 | RSSADAPT_PRINTF(("%s: src %s rssi %d avg %d -> %d\n" , |
274 | ic->ic_ifp->if_xname, ether_sprintf(ni->ni_macaddr), |
275 | rssi, last_avg_rssi, ra->ra_avg_rssi)); |
276 | } |
277 | |
278 | /* |
279 | * Adapt the data rate to suit the conditions. When a transmitted |
280 | * packet is dropped after IEEE80211_RSSADAPT_RETRY_LIMIT retransmissions, |
281 | * raise the RSS threshold for transmitting packets of similar length at |
282 | * the same data rate. |
283 | */ |
284 | void |
285 | (struct ieee80211com *ic, |
286 | struct ieee80211_node *ni, struct ieee80211_rssadapt *ra, |
287 | struct ieee80211_rssdesc *id) |
288 | { |
289 | struct ieee80211_rateset *rs = &ni->ni_rates; |
290 | u_int16_t last_thr; |
291 | u_int i, thridx, top; |
292 | |
293 | ra->ra_nfail++; |
294 | |
295 | if (id->id_rateidx >= rs->rs_nrates) { |
296 | RSSADAPT_PRINTF(("ieee80211_rssadapt_lower_rate: " |
297 | "%s rate #%d > #%d out of bounds\n" , |
298 | ether_sprintf(ni->ni_macaddr), id->id_rateidx, |
299 | rs->rs_nrates - 1)); |
300 | return; |
301 | } |
302 | |
303 | for (i = 0, top = IEEE80211_RSSADAPT_BKT0; |
304 | i < IEEE80211_RSSADAPT_BKTS; |
305 | i++, top <<= IEEE80211_RSSADAPT_BKTPOWER) { |
306 | thridx = i; |
307 | if (id->id_len <= top) |
308 | break; |
309 | } |
310 | |
311 | last_thr = ra->ra_rate_thresh[thridx][id->id_rateidx]; |
312 | ra->ra_rate_thresh[thridx][id->id_rateidx] = |
313 | interpolate(master_expavgctl.rc_thresh, last_thr, |
314 | (id->id_rssi << 8)); |
315 | |
316 | RSSADAPT_PRINTF(("%s: dst %s rssi %d threshold[%d, %d.%d] %d -> %d\n" , |
317 | ic->ic_ifp->if_xname, ether_sprintf(ni->ni_macaddr), |
318 | id->id_rssi, id->id_len, |
319 | (rs->rs_rates[id->id_rateidx] & IEEE80211_RATE_VAL) / 2, |
320 | (rs->rs_rates[id->id_rateidx] & IEEE80211_RATE_VAL) * 5 % 10, |
321 | last_thr, ra->ra_rate_thresh[thridx][id->id_rateidx])); |
322 | } |
323 | |
324 | void |
325 | (struct ieee80211com *ic, |
326 | struct ieee80211_rssadapt *ra, struct ieee80211_rssdesc *id) |
327 | { |
328 | u_int16_t (*thrs)[IEEE80211_RATE_SIZE], newthr, oldthr; |
329 | struct ieee80211_node *ni = id->id_node; |
330 | struct ieee80211_rateset *rs = &ni->ni_rates; |
331 | int i, rate, top; |
332 | #ifdef IEEE80211_DEBUG |
333 | int j; |
334 | #endif |
335 | |
336 | ra->ra_nok++; |
337 | |
338 | if (!ratecheck(&ra->ra_last_raise, &ra->ra_raise_interval)) |
339 | return; |
340 | |
341 | for (i = 0, top = IEEE80211_RSSADAPT_BKT0; |
342 | i < IEEE80211_RSSADAPT_BKTS; |
343 | i++, top <<= IEEE80211_RSSADAPT_BKTPOWER) { |
344 | thrs = &ra->ra_rate_thresh[i]; |
345 | if (id->id_len <= top) |
346 | break; |
347 | } |
348 | |
349 | if (id->id_rateidx + 1 < rs->rs_nrates && |
350 | (*thrs)[id->id_rateidx + 1] > (*thrs)[id->id_rateidx]) { |
351 | rate = (rs->rs_rates[id->id_rateidx + 1] & IEEE80211_RATE_VAL); |
352 | |
353 | __USE(rate); |
354 | RSSADAPT_PRINTF(("%s: threshold[%d, %d.%d] decay %d " , |
355 | ic->ic_ifp->if_xname, |
356 | IEEE80211_RSSADAPT_BKT0 << (IEEE80211_RSSADAPT_BKTPOWER* i), |
357 | rate / 2, rate * 5 % 10, (*thrs)[id->id_rateidx + 1])); |
358 | oldthr = (*thrs)[id->id_rateidx + 1]; |
359 | if ((*thrs)[id->id_rateidx] == 0) |
360 | newthr = ra->ra_avg_rssi; |
361 | else |
362 | newthr = (*thrs)[id->id_rateidx]; |
363 | (*thrs)[id->id_rateidx + 1] = |
364 | interpolate(master_expavgctl.rc_decay, oldthr, newthr); |
365 | |
366 | RSSADAPT_PRINTF(("-> %d\n" , (*thrs)[id->id_rateidx + 1])); |
367 | } |
368 | |
369 | #ifdef IEEE80211_DEBUG |
370 | if (RSSADAPT_DO_PRINT()) { |
371 | printf("%s: dst %s thresholds\n" , ic->ic_ifp->if_xname, |
372 | ether_sprintf(ni->ni_macaddr)); |
373 | for (i = 0; i < IEEE80211_RSSADAPT_BKTS; i++) { |
374 | printf("%d-byte" , IEEE80211_RSSADAPT_BKT0 << (IEEE80211_RSSADAPT_BKTPOWER * i)); |
375 | for (j = 0; j < rs->rs_nrates; j++) { |
376 | rate = (rs->rs_rates[j] & IEEE80211_RATE_VAL); |
377 | printf(", T[%d.%d] = %d" , rate / 2, |
378 | rate * 5 % 10, ra->ra_rate_thresh[i][j]); |
379 | } |
380 | printf("\n" ); |
381 | } |
382 | } |
383 | #endif /* IEEE80211_DEBUG */ |
384 | } |
385 | |