source: bootcd/isolinux/syslinux-6.03/gpxe/src/net/80211/rc80211.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 11.1 KB
Line 
1/*
2 * Simple 802.11 rate-control algorithm for gPXE.
3 *
4 * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21FILE_LICENCE ( GPL2_OR_LATER );
22
23#include <stdlib.h>
24#include <gpxe/net80211.h>
25
26/**
27 * @file
28 *
29 * Simple 802.11 rate-control algorithm
30 */
31
32/** @page rc80211 Rate control philosophy
33 *
34 * We want to maximize our transmission speed, to the extent that we
35 * can do that without dropping undue numbers of packets. We also
36 * don't want to take up very much code space, so our algorithm has to
37 * be pretty simple
38 *
39 * When we receive a packet, we know what rate it was transmitted at,
40 * and whether it had to be retransmitted to get to us.
41 *
42 * When we send a packet, we hear back how many times it had to be
43 * retried to get through, and whether it got through at all.
44 *
45 * Indications of TX success are more reliable than RX success, but RX
46 * information helps us know where to start.
47 *
48 * To handle all of this, we keep for each rate and each direction (TX
49 * and RX separately) some state information for the most recent
50 * packets on that rate and the number of packets for which we have
51 * information. The state is a 32-bit unsigned integer in which two
52 * bits represent a packet: 11 if it went through well, 10 if it went
53 * through with one retry, 01 if it went through with more than one
54 * retry, or 00 if it didn't go through at all. We define the
55 * "goodness" for a particular (rate, direction) combination as the
56 * sum of all the 2-bit fields, times 33, divided by the number of
57 * 2-bit fields containing valid information (16 except when we're
58 * starting out). The number produced is between 0 and 99; we use -1
59 * for rates with less than 4 RX packets or 1 TX, as an indicator that
60 * we do not have enough information to rely on them.
61 *
62 * In deciding which rates are best, we find the weighted average of
63 * TX and RX goodness, where the weighting is by number of packets
64 * with data and TX packets are worth 4 times as much as RX packets.
65 * The weighted average is called "net goodness" and is also a number
66 * between 0 and 99.  If 3 consecutive packets fail transmission
67 * outright, we automatically ratchet down the rate; otherwise, we
68 * switch to the best rate whenever the current rate's goodness falls
69 * below some threshold, and try increasing our rate when the goodness
70 * is very high.
71 *
72 * This system is optimized for gPXE's style of usage. Because normal
73 * operation always involves receiving something, we'll make our way
74 * to the best rate pretty quickly. We tend to follow the lead of the
75 * sending AP in choosing rates, but we won't use rates for long that
76 * don't work well for us in transmission. We assume gPXE won't be
77 * running for long enough that rate patterns will change much, so we
78 * don't have to keep time counters or the like.  And if this doesn't
79 * work well in practice there are many ways it could be tweaked.
80 *
81 * To avoid staying at 1Mbps for a long time, we don't track any
82 * transmitted packets until we've set our rate based on received
83 * packets.
84 */
85
86/** Two-bit packet status indicator for a packet with no retries */
87#define RC_PKT_OK               0x3
88
89/** Two-bit packet status indicator for a packet with one retry */
90#define RC_PKT_RETRIED_ONCE     0x2
91
92/** Two-bit packet status indicator for a TX packet with multiple retries
93 *
94 * It is not possible to tell whether an RX packet had one or multiple
95 * retries; we rely instead on the fact that failed RX packets won't
96 * get to us at all, so if we receive a lot of RX packets on a certain
97 * rate it must be pretty good.
98 */
99#define RC_PKT_RETRIED_MULTI    0x1
100
101/** Two-bit packet status indicator for a TX packet that was never ACKed
102 *
103 * It is not possible to tell whether an RX packet was setn if it
104 * didn't get through to us, but if we don't see one we won't increase
105 * the goodness for its rate. This asymmetry is part of why TX packets
106 * are weighted much more heavily than RX.
107 */
108#define RC_PKT_FAILED           0x0
109
110/** Number of times to weight TX packets more heavily than RX packets */
111#define RC_TX_FACTOR            4
112
113/** Number of consecutive failed TX packets that cause an automatic rate drop */
114#define RC_TX_EMERG_FAIL        3
115
116/** Minimum net goodness below which we will search for a better rate */
117#define RC_GOODNESS_MIN         85
118
119/** Maximum net goodness above which we will try to increase our rate */
120#define RC_GOODNESS_MAX         95
121
122/** Minimum (num RX + @c RC_TX_FACTOR * num TX) to use a certain rate */
123#define RC_UNCERTAINTY_THRESH   4
124
125/** TX direction */
126#define TX      0
127
128/** RX direction */
129#define RX      1
130
131/** A rate control context */
132struct rc80211_ctx
133{
134        /** Goodness state for each rate, TX and RX */
135        u32 goodness[2][NET80211_MAX_RATES];
136
137        /** Number of packets recorded for each rate */
138        u8 count[2][NET80211_MAX_RATES];
139
140        /** Indication of whether we've set the device rate yet */
141        int started;
142
143        /** Counter of all packets sent and received */
144        int packets;
145};
146
147/**
148 * Initialize rate-control algorithm
149 *
150 * @v dev       802.11 device
151 * @ret ctx     Rate-control context, to be stored in @c dev->rctl
152 */
153struct rc80211_ctx * rc80211_init ( struct net80211_device *dev __unused )
154{
155        struct rc80211_ctx *ret = zalloc ( sizeof ( *ret ) );
156        return ret;
157}
158
159/**
160 * Calculate net goodness for a certain rate
161 *
162 * @v ctx       Rate-control context
163 * @v rate_idx  Index of rate to calculate net goodness for
164 */
165static int rc80211_calc_net_goodness ( struct rc80211_ctx *ctx,
166                                       int rate_idx )
167{
168        int sum[2], num[2], dir, pkt;
169
170        for ( dir = 0; dir < 2; dir++ ) {
171                u32 good = ctx->goodness[dir][rate_idx];
172
173                num[dir] = ctx->count[dir][rate_idx];
174                sum[dir] = 0;
175
176                for ( pkt = 0; pkt < num[dir]; pkt++ )
177                        sum[dir] += ( good >> ( 2 * pkt ) ) & 0x3;
178        }
179
180        if ( ( num[TX] * RC_TX_FACTOR + num[RX] ) < RC_UNCERTAINTY_THRESH )
181                return -1;
182
183        return ( 33 * ( sum[TX] * RC_TX_FACTOR + sum[RX] ) /
184                      ( num[TX] * RC_TX_FACTOR + num[RX] ) );
185}
186
187/**
188 * Determine the best rate to switch to and return it
189 *
190 * @v dev               802.11 device
191 * @ret rate_idx        Index of the best rate to switch to
192 */
193static int rc80211_pick_best ( struct net80211_device *dev )
194{
195        struct rc80211_ctx *ctx = dev->rctl;
196        int best_net_good = 0, best_rate = -1, i;
197
198        for ( i = 0; i < dev->nr_rates; i++ ) {
199                int net_good = rc80211_calc_net_goodness ( ctx, i );
200
201                if ( net_good > best_net_good ||
202                     ( best_net_good > RC_GOODNESS_MIN &&
203                       net_good > RC_GOODNESS_MIN ) ) {
204                        best_net_good = net_good;
205                        best_rate = i;
206                }
207        }
208
209        if ( best_rate >= 0 ) {
210                int old_good = rc80211_calc_net_goodness ( ctx, dev->rate );
211                if ( old_good != best_net_good )
212                        DBGC ( ctx, "802.11 RC %p switching from goodness "
213                               "%d to %d\n", ctx, old_good, best_net_good );
214
215                ctx->started = 1;
216                return best_rate;
217        }
218
219        return dev->rate;
220}
221
222/**
223 * Set 802.11 device rate
224 *
225 * @v dev       802.11 device
226 * @v rate_idx  Index of rate to switch to
227 *
228 * This is a thin wrapper around net80211_set_rate_idx to insert a
229 * debugging message where appropriate.
230 */
231static inline void rc80211_set_rate ( struct net80211_device *dev,
232                                      int rate_idx )
233{
234        DBGC ( dev->rctl, "802.11 RC %p changing rate %d->%d Mbps\n", dev->rctl,
235               dev->rates[dev->rate] / 10, dev->rates[rate_idx] / 10 );
236
237        net80211_set_rate_idx ( dev, rate_idx );
238}
239
240/**
241 * Check rate-control state and change rate if necessary
242 *
243 * @v dev       802.11 device
244 */
245static void rc80211_maybe_set_new ( struct net80211_device *dev )
246{
247        struct rc80211_ctx *ctx = dev->rctl;
248        int net_good;
249
250        net_good = rc80211_calc_net_goodness ( ctx, dev->rate );
251
252        if ( ! ctx->started ) {
253                rc80211_set_rate ( dev, rc80211_pick_best ( dev ) );
254                return;
255        }
256
257        if ( net_good < 0 )     /* insufficient data */
258                return;
259
260        if ( net_good > RC_GOODNESS_MAX && dev->rate + 1 < dev->nr_rates ) {
261                int higher = rc80211_calc_net_goodness ( ctx, dev->rate + 1 );
262                if ( higher > net_good || higher < 0 )
263                        rc80211_set_rate ( dev, dev->rate + 1 );
264                else
265                        rc80211_set_rate ( dev, rc80211_pick_best ( dev ) );
266        }
267
268        if ( net_good < RC_GOODNESS_MIN ) {
269                rc80211_set_rate ( dev, rc80211_pick_best ( dev ) );
270        }
271}
272
273/**
274 * Update rate-control state
275 *
276 * @v dev               802.11 device
277 * @v direction         One of the direction constants TX or RX
278 * @v rate_idx          Index of rate at which packet was sent or received
279 * @v retries           Number of times packet was retried before success
280 * @v failed            If nonzero, the packet failed to get through
281 */
282static void rc80211_update ( struct net80211_device *dev, int direction,
283                             int rate_idx, int retries, int failed )
284{
285        struct rc80211_ctx *ctx = dev->rctl;
286        u32 goodness = ctx->goodness[direction][rate_idx];
287
288        if ( ctx->count[direction][rate_idx] < 16 )
289                ctx->count[direction][rate_idx]++;
290
291        goodness <<= 2;
292        if ( failed )
293                goodness |= RC_PKT_FAILED;
294        else if ( retries > 1 )
295                goodness |= RC_PKT_RETRIED_MULTI;
296        else if ( retries )
297                goodness |= RC_PKT_RETRIED_ONCE;
298        else
299                goodness |= RC_PKT_OK;
300
301        ctx->goodness[direction][rate_idx] = goodness;
302
303        ctx->packets++;
304
305        rc80211_maybe_set_new ( dev );
306}
307
308/**
309 * Update rate-control state for transmitted packet
310 *
311 * @v dev       802.11 device
312 * @v retries   Number of times packet was transmitted before success
313 * @v rc        Return status code for transmission
314 */
315void rc80211_update_tx ( struct net80211_device *dev, int retries, int rc )
316{
317        struct rc80211_ctx *ctx = dev->rctl;
318
319        if ( ! ctx->started )
320                return;
321
322        rc80211_update ( dev, TX, dev->rate, retries, rc );
323
324        /* Check if the last RC_TX_EMERG_FAIL packets have all failed */
325        if ( ! ( ctx->goodness[TX][dev->rate] &
326                 ( ( 1 << ( 2 * RC_TX_EMERG_FAIL ) ) - 1 ) ) ) {
327                if ( dev->rate == 0 )
328                        DBGC ( dev->rctl, "802.11 RC %p saw %d consecutive "
329                               "failed TX, but cannot lower rate any further\n",
330                               dev->rctl, RC_TX_EMERG_FAIL );
331                else {
332                        DBGC ( dev->rctl, "802.11 RC %p lowering rate (%d->%d "
333                               "Mbps) due to %d consecutive TX failures\n",
334                               dev->rctl, dev->rates[dev->rate] / 10,
335                               dev->rates[dev->rate - 1] / 10,
336                               RC_TX_EMERG_FAIL );
337
338                        rc80211_set_rate ( dev, dev->rate - 1 );
339                }
340        }
341}
342
343/**
344 * Update rate-control state for received packet
345 *
346 * @v dev       802.11 device
347 * @v retry     Whether the received packet had been retransmitted
348 * @v rate      Rate at which packet was received, in 100 kbps units
349 */
350void rc80211_update_rx ( struct net80211_device *dev, int retry, u16 rate )
351{
352        int ridx;
353
354        for ( ridx = 0; ridx < dev->nr_rates && dev->rates[ridx] != rate;
355              ridx++ )
356                ;
357        if ( ridx >= dev->nr_rates )
358                return;         /* couldn't find the rate */
359
360        rc80211_update ( dev, RX, ridx, retry, 0 );
361}
362
363/**
364 * Free rate-control context
365 *
366 * @v ctx       Rate-control context
367 */
368void rc80211_free ( struct rc80211_ctx *ctx )
369{
370        free ( ctx );
371}
Note: See TracBrowser for help on using the repository browser.