aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/rc80211_pid_algo.c
diff options
context:
space:
mode:
authorMattias Nissler <mattias.nissler@gmx.de>2007-12-18 19:27:18 -0500
committerDavid S. Miller <davem@davemloft.net>2008-01-28 17:59:44 -0500
commit12446c67fea1e5bc74c58e43ef53eea308cdda61 (patch)
tree597ab96b5a7827d53ba676e78530646f842b4bde /net/mac80211/rc80211_pid_algo.c
parent1dc4d1e6a1e81fee0d488cec4fcd39269ec51318 (diff)
rc80211-pid: add debugging
This adds a new debugfs file from which rate control relevant events can be read one event per line. The output includes the current time, so graphs can be created showing the rate control parameters. This helps in evaluating and tuning rate control parameters. While at it, we split headers and code for better readability. Signed-off-by: Mattias Nissler <mattias.nissler@gmx.de> Signed-off-by: Stefano Brivio <stefano.brivio@polimi.it> Signed-off-by: John W. Linville <linville@tuxdriver.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/mac80211/rc80211_pid_algo.c')
-rw-r--r--net/mac80211/rc80211_pid_algo.c444
1 files changed, 444 insertions, 0 deletions
diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c
new file mode 100644
index 000000000000..3fac3a5d7e00
--- /dev/null
+++ b/net/mac80211/rc80211_pid_algo.c
@@ -0,0 +1,444 @@
1/*
2 * Copyright 2002-2005, Instant802 Networks, Inc.
3 * Copyright 2005, Devicescape Software, Inc.
4 * Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de>
5 * Copyright 2007, Stefano Brivio <stefano.brivio@polimi.it>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/netdevice.h>
13#include <linux/types.h>
14#include <linux/skbuff.h>
15
16#include <net/mac80211.h>
17#include "ieee80211_rate.h"
18
19#include "rc80211_pid.h"
20
21
22/* This is an implementation of a TX rate control algorithm that uses a PID
23 * controller. Given a target failed frames rate, the controller decides about
24 * TX rate changes to meet the target failed frames rate.
25 *
26 * The controller basically computes the following:
27 *
28 * adj = CP * err + CI * err_avg + CD * (err - last_err) * (1 + sharpening)
29 *
30 * where
31 * adj adjustment value that is used to switch TX rate (see below)
32 * err current error: target vs. current failed frames percentage
33 * last_err last error
34 * err_avg average (i.e. poor man's integral) of recent errors
35 * sharpening non-zero when fast response is needed (i.e. right after
36 * association or no frames sent for a long time), heading
37 * to zero over time
38 * CP Proportional coefficient
39 * CI Integral coefficient
40 * CD Derivative coefficient
41 *
42 * CP, CI, CD are subject to careful tuning.
43 *
44 * The integral component uses a exponential moving average approach instead of
45 * an actual sliding window. The advantage is that we don't need to keep an
46 * array of the last N error values and computation is easier.
47 *
48 * Once we have the adj value, we map it to a rate by means of a learning
49 * algorithm. This algorithm keeps the state of the percentual failed frames
50 * difference between rates. The behaviour of the lowest available rate is kept
51 * as a reference value, and every time we switch between two rates, we compute
52 * the difference between the failed frames each rate exhibited. By doing so,
53 * we compare behaviours which different rates exhibited in adjacent timeslices,
54 * thus the comparison is minimally affected by external conditions. This
55 * difference gets propagated to the whole set of measurements, so that the
56 * reference is always the same. Periodically, we normalize this set so that
57 * recent events weigh the most. By comparing the adj value with this set, we
58 * avoid pejorative switches to lower rates and allow for switches to higher
59 * rates if they behaved well.
60 *
61 * Note that for the computations we use a fixed-point representation to avoid
62 * floating point arithmetic. Hence, all values are shifted left by
63 * RC_PID_ARITH_SHIFT.
64 */
65
66
67/* Shift the adjustment so that we won't switch to a lower rate if it exhibited
68 * a worse failed frames behaviour and we'll choose the highest rate whose
69 * failed frames behaviour is not worse than the one of the original rate
70 * target. While at it, check that the adjustment is within the ranges. Then,
71 * provide the new rate index. */
72static int rate_control_pid_shift_adjust(struct rc_pid_rateinfo *r,
73 int adj, int cur, int l)
74{
75 int i, j, k, tmp;
76
77 if (cur + adj < 0)
78 return 0;
79 if (cur + adj >= l)
80 return l - 1;
81
82 i = r[cur + adj].rev_index;
83
84 j = r[cur].rev_index;
85
86 if (adj < 0) {
87 tmp = i;
88 for (k = j; k >= i; k--)
89 if (r[k].diff <= r[j].diff)
90 tmp = k;
91 return r[tmp].index;
92 } else if (adj > 0) {
93 tmp = i;
94 for (k = i + 1; k + i < l; k++)
95 if (r[k].diff <= r[i].diff)
96 tmp = k;
97 return r[tmp].index;
98 }
99 return cur + adj;
100}
101
102static void rate_control_pid_adjust_rate(struct ieee80211_local *local,
103 struct sta_info *sta, int adj,
104 struct rc_pid_rateinfo *rinfo)
105{
106 struct ieee80211_sub_if_data *sdata;
107 struct ieee80211_hw_mode *mode;
108 int newidx;
109 int maxrate;
110 int back = (adj > 0) ? 1 : -1;
111
112 sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
113 if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) {
114 /* forced unicast rate - do not change STA rate */
115 return;
116 }
117
118 mode = local->oper_hw_mode;
119 maxrate = sdata->bss ? sdata->bss->max_ratectrl_rateidx : -1;
120
121 newidx = rate_control_pid_shift_adjust(rinfo, adj, sta->txrate,
122 mode->num_rates);
123
124 while (newidx != sta->txrate) {
125 if (rate_supported(sta, mode, newidx) &&
126 (maxrate < 0 || newidx <= maxrate)) {
127 sta->txrate = newidx;
128 break;
129 }
130
131 newidx += back;
132 }
133
134#ifdef CONFIG_MAC80211_DEBUGFS
135 rate_control_pid_event_rate_change(
136 &((struct rc_pid_sta_info *)sta->rate_ctrl_priv)->events,
137 newidx, mode->rates[newidx].rate);
138#endif
139}
140
141/* Normalize the failed frames per-rate differences. */
142static void rate_control_pid_normalize(struct rc_pid_rateinfo *r, int l)
143{
144 int i;
145
146 if (r[0].diff > RC_PID_NORM_OFFSET)
147 r[0].diff -= RC_PID_NORM_OFFSET;
148 else if (r[0].diff < -RC_PID_NORM_OFFSET)
149 r[0].diff += RC_PID_NORM_OFFSET;
150 for (i = 0; i < l - 1; i++)
151 if (r[i + 1].diff > r[i].diff + RC_PID_NORM_OFFSET)
152 r[i + 1].diff -= RC_PID_NORM_OFFSET;
153 else if (r[i + 1].diff <= r[i].diff)
154 r[i + 1].diff += RC_PID_NORM_OFFSET;
155}
156
157static void rate_control_pid_sample(struct rc_pid_info *pinfo,
158 struct ieee80211_local *local,
159 struct sta_info *sta)
160{
161 struct rc_pid_sta_info *spinfo = sta->rate_ctrl_priv;
162 struct rc_pid_rateinfo *rinfo = pinfo->rinfo;
163 struct ieee80211_hw_mode *mode;
164 u32 pf;
165 s32 err_avg;
166 s32 err_prop;
167 s32 err_int;
168 s32 err_der;
169 int adj, i, j, tmp;
170
171 mode = local->oper_hw_mode;
172 spinfo = sta->rate_ctrl_priv;
173
174 /* In case nothing happened during the previous control interval, turn
175 * the sharpening factor on. */
176 if (jiffies - spinfo->last_sample > 2 * RC_PID_INTERVAL)
177 spinfo->sharp_cnt = RC_PID_SHARPENING_DURATION;
178
179 spinfo->last_sample = jiffies;
180
181 /* This should never happen, but in case, we assume the old sample is
182 * still a good measurement and copy it. */
183 if (unlikely(spinfo->tx_num_xmit == 0))
184 pf = spinfo->last_pf;
185 else {
186 pf = spinfo->tx_num_failed * 100 / spinfo->tx_num_xmit;
187 pf <<= RC_PID_ARITH_SHIFT;
188 }
189
190 spinfo->tx_num_xmit = 0;
191 spinfo->tx_num_failed = 0;
192
193 /* If we just switched rate, update the rate behaviour info. */
194 if (pinfo->oldrate != sta->txrate) {
195
196 i = rinfo[pinfo->oldrate].rev_index;
197 j = rinfo[sta->txrate].rev_index;
198
199 tmp = (pf - spinfo->last_pf);
200 tmp = RC_PID_DO_ARITH_RIGHT_SHIFT(tmp, RC_PID_ARITH_SHIFT);
201
202 rinfo[j].diff = rinfo[i].diff + tmp;
203 pinfo->oldrate = sta->txrate;
204 }
205 rate_control_pid_normalize(rinfo, mode->num_rates);
206
207 /* Compute the proportional, integral and derivative errors. */
208 err_prop = RC_PID_TARGET_PF - pf;
209
210 err_avg = spinfo->err_avg_sc >> RC_PID_SMOOTHING_SHIFT;
211 spinfo->err_avg_sc = spinfo->err_avg_sc - err_avg + err_prop;
212 err_int = spinfo->err_avg_sc >> RC_PID_SMOOTHING_SHIFT;
213
214 err_der = pf - spinfo->last_pf
215 * (1 + RC_PID_SHARPENING_FACTOR * spinfo->sharp_cnt);
216 spinfo->last_pf = pf;
217 if (spinfo->sharp_cnt)
218 spinfo->sharp_cnt--;
219
220#ifdef CONFIG_MAC80211_DEBUGFS
221 rate_control_pid_event_pf_sample(&spinfo->events, pf, err_prop, err_int,
222 err_der);
223#endif
224
225 /* Compute the controller output. */
226 adj = (err_prop * pinfo->coeff_p + err_int * pinfo->coeff_i
227 + err_der * pinfo->coeff_d);
228 adj = RC_PID_DO_ARITH_RIGHT_SHIFT(adj, 2 * RC_PID_ARITH_SHIFT);
229
230 /* Change rate. */
231 if (adj)
232 rate_control_pid_adjust_rate(local, sta, adj, rinfo);
233}
234
235static void rate_control_pid_tx_status(void *priv, struct net_device *dev,
236 struct sk_buff *skb,
237 struct ieee80211_tx_status *status)
238{
239 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
240 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
241 struct rc_pid_info *pinfo = priv;
242 struct sta_info *sta;
243 struct rc_pid_sta_info *spinfo;
244
245 sta = sta_info_get(local, hdr->addr1);
246
247 if (!sta)
248 return;
249
250 /* Ignore all frames that were sent with a different rate than the rate
251 * we currently advise mac80211 to use. */
252 if (status->control.rate != &local->oper_hw_mode->rates[sta->txrate])
253 return;
254
255 spinfo = sta->rate_ctrl_priv;
256 spinfo->tx_num_xmit++;
257
258#ifdef CONFIG_MAC80211_DEBUGFS
259 rate_control_pid_event_tx_status(&spinfo->events, status);
260#endif
261
262 /* We count frames that totally failed to be transmitted as two bad
263 * frames, those that made it out but had some retries as one good and
264 * one bad frame. */
265 if (status->excessive_retries) {
266 spinfo->tx_num_failed += 2;
267 spinfo->tx_num_xmit++;
268 } else if (status->retry_count) {
269 spinfo->tx_num_failed++;
270 spinfo->tx_num_xmit++;
271 }
272
273 if (status->excessive_retries) {
274 sta->tx_retry_failed++;
275 sta->tx_num_consecutive_failures++;
276 sta->tx_num_mpdu_fail++;
277 } else {
278 sta->last_ack_rssi[0] = sta->last_ack_rssi[1];
279 sta->last_ack_rssi[1] = sta->last_ack_rssi[2];
280 sta->last_ack_rssi[2] = status->ack_signal;
281 sta->tx_num_consecutive_failures = 0;
282 sta->tx_num_mpdu_ok++;
283 }
284 sta->tx_retry_count += status->retry_count;
285 sta->tx_num_mpdu_fail += status->retry_count;
286
287 /* Update PID controller state. */
288 if (time_after(jiffies, spinfo->last_sample + RC_PID_INTERVAL))
289 rate_control_pid_sample(pinfo, local, sta);
290
291 sta_info_put(sta);
292}
293
294static void rate_control_pid_get_rate(void *priv, struct net_device *dev,
295 struct ieee80211_hw_mode *mode,
296 struct sk_buff *skb,
297 struct rate_selection *sel)
298{
299 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
300 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
301 struct sta_info *sta;
302 int rateidx;
303
304 sta = sta_info_get(local, hdr->addr1);
305
306 if (!sta) {
307 sel->rate = rate_lowest(local, mode, NULL);
308 sta_info_put(sta);
309 return;
310 }
311
312 rateidx = sta->txrate;
313
314 if (rateidx >= mode->num_rates)
315 rateidx = mode->num_rates - 1;
316
317 sta_info_put(sta);
318
319 sel->rate = &mode->rates[rateidx];
320
321#ifdef CONFIG_MAC80211_DEBUGFS
322 rate_control_pid_event_tx_rate(
323 &((struct rc_pid_sta_info *) sta->rate_ctrl_priv)->events,
324 rateidx, mode->rates[rateidx].rate);
325#endif
326}
327
328static void rate_control_pid_rate_init(void *priv, void *priv_sta,
329 struct ieee80211_local *local,
330 struct sta_info *sta)
331{
332 /* TODO: This routine should consider using RSSI from previous packets
333 * as we need to have IEEE 802.1X auth succeed immediately after assoc..
334 * Until that method is implemented, we will use the lowest supported
335 * rate as a workaround. */
336 sta->txrate = rate_lowest_index(local, local->oper_hw_mode, sta);
337}
338
339static void *rate_control_pid_alloc(struct ieee80211_local *local)
340{
341 struct rc_pid_info *pinfo;
342 struct rc_pid_rateinfo *rinfo;
343 struct ieee80211_hw_mode *mode;
344 int i, j, tmp;
345 bool s;
346
347 pinfo = kmalloc(sizeof(*pinfo), GFP_ATOMIC);
348 if (!pinfo)
349 return NULL;
350
351 /* We can safely assume that oper_hw_mode won't change unless we get
352 * reinitialized. */
353 mode = local->oper_hw_mode;
354 rinfo = kmalloc(sizeof(*rinfo) * mode->num_rates, GFP_ATOMIC);
355 if (!rinfo) {
356 kfree(pinfo);
357 return NULL;
358 }
359
360 /* Sort the rates. This is optimized for the most common case (i.e.
361 * almost-sorted CCK+OFDM rates). Kind of bubble-sort with reversed
362 * mapping too. */
363 for (i = 0; i < mode->num_rates; i++) {
364 rinfo[i].index = i;
365 rinfo[i].rev_index = i;
366 if (RC_PID_FAST_START)
367 rinfo[i].diff = 0;
368 else
369 rinfo[i].diff = i * RC_PID_NORM_OFFSET;
370 }
371 for (i = 1; i < mode->num_rates; i++) {
372 s = 0;
373 for (j = 0; j < mode->num_rates - i; j++)
374 if (unlikely(mode->rates[rinfo[j].index].rate >
375 mode->rates[rinfo[j + 1].index].rate)) {
376 tmp = rinfo[j].index;
377 rinfo[j].index = rinfo[j + 1].index;
378 rinfo[j + 1].index = tmp;
379 rinfo[rinfo[j].index].rev_index = j;
380 rinfo[rinfo[j + 1].index].rev_index = j + 1;
381 s = 1;
382 }
383 if (!s)
384 break;
385 }
386
387 pinfo->target = RC_PID_TARGET_PF;
388 pinfo->coeff_p = RC_PID_COEFF_P;
389 pinfo->coeff_i = RC_PID_COEFF_I;
390 pinfo->coeff_d = RC_PID_COEFF_D;
391 pinfo->rinfo = rinfo;
392 pinfo->oldrate = 0;
393
394 return pinfo;
395}
396
397static void rate_control_pid_free(void *priv)
398{
399 struct rc_pid_info *pinfo = priv;
400 kfree(pinfo->rinfo);
401 kfree(pinfo);
402}
403
404static void rate_control_pid_clear(void *priv)
405{
406}
407
408static void *rate_control_pid_alloc_sta(void *priv, gfp_t gfp)
409{
410 struct rc_pid_sta_info *spinfo;
411
412 spinfo = kzalloc(sizeof(*spinfo), gfp);
413 if (spinfo == NULL)
414 return NULL;
415
416#ifdef CONFIG_MAC80211_DEBUGFS
417 spin_lock_init(&spinfo->events.lock);
418 init_waitqueue_head(&spinfo->events.waitqueue);
419#endif
420
421 return spinfo;
422}
423
424static void rate_control_pid_free_sta(void *priv, void *priv_sta)
425{
426 struct rc_pid_sta_info *spinfo = priv_sta;
427 kfree(spinfo);
428}
429
430struct rate_control_ops mac80211_rcpid = {
431 .name = "pid",
432 .tx_status = rate_control_pid_tx_status,
433 .get_rate = rate_control_pid_get_rate,
434 .rate_init = rate_control_pid_rate_init,
435 .clear = rate_control_pid_clear,
436 .alloc = rate_control_pid_alloc,
437 .free = rate_control_pid_free,
438 .alloc_sta = rate_control_pid_alloc_sta,
439 .free_sta = rate_control_pid_free_sta,
440#ifdef CONFIG_MAC80211_DEBUGFS
441 .add_sta_debugfs = rate_control_pid_add_sta_debugfs,
442 .remove_sta_debugfs = rate_control_pid_remove_sta_debugfs,
443#endif
444};