summaryrefslogtreecommitdiffstats
path: root/debian/grub-extras/disabled/gpxe/src/net/80211/rc80211.c
blob: 5bd1914380906f6b5b347e7f4e8e3da922f7635d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
/*
 * Simple 802.11 rate-control algorithm for gPXE.
 *
 * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

FILE_LICENCE ( GPL2_OR_LATER );

#include <stdlib.h>
#include <gpxe/net80211.h>

/**
 * @file
 *
 * Simple 802.11 rate-control algorithm
 */

/** @page rc80211 Rate control philosophy
 *
 * We want to maximize our transmission speed, to the extent that we
 * can do that without dropping undue numbers of packets. We also
 * don't want to take up very much code space, so our algorithm has to
 * be pretty simple
 *
 * When we receive a packet, we know what rate it was transmitted at,
 * and whether it had to be retransmitted to get to us.
 *
 * When we send a packet, we hear back how many times it had to be
 * retried to get through, and whether it got through at all.
 *
 * Indications of TX success are more reliable than RX success, but RX
 * information helps us know where to start.
 *
 * To handle all of this, we keep for each rate and each direction (TX
 * and RX separately) some state information for the most recent
 * packets on that rate and the number of packets for which we have
 * information. The state is a 32-bit unsigned integer in which two
 * bits represent a packet: 11 if it went through well, 10 if it went
 * through with one retry, 01 if it went through with more than one
 * retry, or 00 if it didn't go through at all. We define the
 * "goodness" for a particular (rate, direction) combination as the
 * sum of all the 2-bit fields, times 33, divided by the number of
 * 2-bit fields containing valid information (16 except when we're
 * starting out). The number produced is between 0 and 99; we use -1
 * for rates with less than 4 RX packets or 1 TX, as an indicator that
 * we do not have enough information to rely on them.
 *
 * In deciding which rates are best, we find the weighted average of
 * TX and RX goodness, where the weighting is by number of packets
 * with data and TX packets are worth 4 times as much as RX packets.
 * The weighted average is called "net goodness" and is also a number
 * between 0 and 99.  If 3 consecutive packets fail transmission
 * outright, we automatically ratchet down the rate; otherwise, we
 * switch to the best rate whenever the current rate's goodness falls
 * below some threshold, and try increasing our rate when the goodness
 * is very high.
 *
 * This system is optimized for gPXE's style of usage. Because normal
 * operation always involves receiving something, we'll make our way
 * to the best rate pretty quickly. We tend to follow the lead of the
 * sending AP in choosing rates, but we won't use rates for long that
 * don't work well for us in transmission. We assume gPXE won't be
 * running for long enough that rate patterns will change much, so we
 * don't have to keep time counters or the like.  And if this doesn't
 * work well in practice there are many ways it could be tweaked.
 *
 * To avoid staying at 1Mbps for a long time, we don't track any
 * transmitted packets until we've set our rate based on received
 * packets.
 */

/** Two-bit packet status indicator for a packet with no retries */
#define RC_PKT_OK		0x3

/** Two-bit packet status indicator for a packet with one retry */
#define RC_PKT_RETRIED_ONCE	0x2

/** Two-bit packet status indicator for a TX packet with multiple retries
 *
 * It is not possible to tell whether an RX packet had one or multiple
 * retries; we rely instead on the fact that failed RX packets won't
 * get to us at all, so if we receive a lot of RX packets on a certain
 * rate it must be pretty good.
 */
#define RC_PKT_RETRIED_MULTI	0x1

/** Two-bit packet status indicator for a TX packet that was never ACKed
 *
 * It is not possible to tell whether an RX packet was setn if it
 * didn't get through to us, but if we don't see one we won't increase
 * the goodness for its rate. This asymmetry is part of why TX packets
 * are weighted much more heavily than RX.
 */
#define RC_PKT_FAILED		0x0

/** Number of times to weight TX packets more heavily than RX packets */
#define RC_TX_FACTOR		4

/** Number of consecutive failed TX packets that cause an automatic rate drop */
#define RC_TX_EMERG_FAIL	3

/** Minimum net goodness below which we will search for a better rate */
#define RC_GOODNESS_MIN		85

/** Maximum net goodness above which we will try to increase our rate */
#define RC_GOODNESS_MAX		95

/** Minimum (num RX + @c RC_TX_FACTOR * num TX) to use a certain rate */
#define RC_UNCERTAINTY_THRESH	4

/** TX direction */
#define TX	0

/** RX direction */
#define RX	1

/** A rate control context */
struct rc80211_ctx
{
	/** Goodness state for each rate, TX and RX */
	u32 goodness[2][NET80211_MAX_RATES];

	/** Number of packets recorded for each rate */
	u8 count[2][NET80211_MAX_RATES];

	/** Indication of whether we've set the device rate yet */
	int started;

	/** Counter of all packets sent and received */
	int packets;
};

/**
 * Initialize rate-control algorithm
 *
 * @v dev	802.11 device
 * @ret ctx	Rate-control context, to be stored in @c dev->rctl
 */
struct rc80211_ctx * rc80211_init ( struct net80211_device *dev __unused )
{
	struct rc80211_ctx *ret = zalloc ( sizeof ( *ret ) );
	return ret;
}

/**
 * Calculate net goodness for a certain rate
 *
 * @v ctx	Rate-control context
 * @v rate_idx	Index of rate to calculate net goodness for
 */
static int rc80211_calc_net_goodness ( struct rc80211_ctx *ctx,
				       int rate_idx )
{
	int sum[2], num[2], dir, pkt;

	for ( dir = 0; dir < 2; dir++ ) {
		u32 good = ctx->goodness[dir][rate_idx];

		num[dir] = ctx->count[dir][rate_idx];
		sum[dir] = 0;

		for ( pkt = 0; pkt < num[dir]; pkt++ )
			sum[dir] += ( good >> ( 2 * pkt ) ) & 0x3;
	}

	if ( ( num[TX] * RC_TX_FACTOR + num[RX] ) < RC_UNCERTAINTY_THRESH )
		return -1;

	return ( 33 * ( sum[TX] * RC_TX_FACTOR + sum[RX] ) /
		      ( num[TX] * RC_TX_FACTOR + num[RX] ) );
}

/**
 * Determine the best rate to switch to and return it
 *
 * @v dev		802.11 device
 * @ret rate_idx	Index of the best rate to switch to
 */
static int rc80211_pick_best ( struct net80211_device *dev )
{
	struct rc80211_ctx *ctx = dev->rctl;
	int best_net_good = 0, best_rate = -1, i;

	for ( i = 0; i < dev->nr_rates; i++ ) {
		int net_good = rc80211_calc_net_goodness ( ctx, i );

		if ( net_good > best_net_good ||
		     ( best_net_good > RC_GOODNESS_MIN &&
		       net_good > RC_GOODNESS_MIN ) ) {
			best_net_good = net_good;
			best_rate = i;
		}
	}

	if ( best_rate >= 0 ) {
		int old_good = rc80211_calc_net_goodness ( ctx, dev->rate );
		if ( old_good != best_net_good )
			DBGC ( ctx, "802.11 RC %p switching from goodness "
			       "%d to %d\n", ctx, old_good, best_net_good );

		ctx->started = 1;
		return best_rate;
	}

	return dev->rate;
}

/**
 * Set 802.11 device rate
 *
 * @v dev	802.11 device
 * @v rate_idx	Index of rate to switch to
 *
 * This is a thin wrapper around net80211_set_rate_idx to insert a
 * debugging message where appropriate.
 */
static inline void rc80211_set_rate ( struct net80211_device *dev,
				      int rate_idx )
{
	DBGC ( dev->rctl, "802.11 RC %p changing rate %d->%d Mbps\n", dev->rctl,
	       dev->rates[dev->rate] / 10, dev->rates[rate_idx] / 10 );

	net80211_set_rate_idx ( dev, rate_idx );
}

/**
 * Check rate-control state and change rate if necessary
 *
 * @v dev	802.11 device
 */
static void rc80211_maybe_set_new ( struct net80211_device *dev )
{
	struct rc80211_ctx *ctx = dev->rctl;
	int net_good;

	net_good = rc80211_calc_net_goodness ( ctx, dev->rate );

	if ( ! ctx->started ) {
		rc80211_set_rate ( dev, rc80211_pick_best ( dev ) );
		return;
	}

	if ( net_good < 0 )	/* insufficient data */
		return;

	if ( net_good > RC_GOODNESS_MAX && dev->rate + 1 < dev->nr_rates ) {
		int higher = rc80211_calc_net_goodness ( ctx, dev->rate + 1 );
		if ( higher > net_good || higher < 0 )
			rc80211_set_rate ( dev, dev->rate + 1 );
		else
			rc80211_set_rate ( dev, rc80211_pick_best ( dev ) );
	}

	if ( net_good < RC_GOODNESS_MIN ) {
		rc80211_set_rate ( dev, rc80211_pick_best ( dev ) );
	}
}

/**
 * Update rate-control state
 *
 * @v dev		802.11 device
 * @v direction		One of the direction constants TX or RX
 * @v rate_idx		Index of rate at which packet was sent or received
 * @v retries		Number of times packet was retried before success
 * @v failed		If nonzero, the packet failed to get through
 */
static void rc80211_update ( struct net80211_device *dev, int direction,
			     int rate_idx, int retries, int failed )
{
	struct rc80211_ctx *ctx = dev->rctl;
	u32 goodness = ctx->goodness[direction][rate_idx];

	if ( ctx->count[direction][rate_idx] < 16 )
		ctx->count[direction][rate_idx]++;

	goodness <<= 2;
	if ( failed )
		goodness |= RC_PKT_FAILED;
	else if ( retries > 1 )
		goodness |= RC_PKT_RETRIED_MULTI;
	else if ( retries )
		goodness |= RC_PKT_RETRIED_ONCE;
	else
		goodness |= RC_PKT_OK;

	ctx->goodness[direction][rate_idx] = goodness;

	ctx->packets++;

	rc80211_maybe_set_new ( dev );
}

/**
 * Update rate-control state for transmitted packet
 *
 * @v dev	802.11 device
 * @v retries	Number of times packet was transmitted before success
 * @v rc	Return status code for transmission
 */
void rc80211_update_tx ( struct net80211_device *dev, int retries, int rc )
{
	struct rc80211_ctx *ctx = dev->rctl;

	if ( ! ctx->started )
		return;

	rc80211_update ( dev, TX, dev->rate, retries, rc );

	/* Check if the last RC_TX_EMERG_FAIL packets have all failed */
	if ( ! ( ctx->goodness[TX][dev->rate] &
		 ( ( 1 << ( 2 * RC_TX_EMERG_FAIL ) ) - 1 ) ) ) {
		if ( dev->rate == 0 )
			DBGC ( dev->rctl, "802.11 RC %p saw %d consecutive "
			       "failed TX, but cannot lower rate any further\n",
			       dev->rctl, RC_TX_EMERG_FAIL );
		else {
			DBGC ( dev->rctl, "802.11 RC %p lowering rate (%d->%d "
			       "Mbps) due to %d consecutive TX failures\n",
			       dev->rctl, dev->rates[dev->rate] / 10,
			       dev->rates[dev->rate - 1] / 10,
			       RC_TX_EMERG_FAIL );

			rc80211_set_rate ( dev, dev->rate - 1 );
		}
	}
}

/**
 * Update rate-control state for received packet
 *
 * @v dev	802.11 device
 * @v retry	Whether the received packet had been retransmitted
 * @v rate	Rate at which packet was received, in 100 kbps units
 */
void rc80211_update_rx ( struct net80211_device *dev, int retry, u16 rate )
{
	int ridx;

	for ( ridx = 0; ridx < dev->nr_rates && dev->rates[ridx] != rate;
	      ridx++ )
		;
	if ( ridx >= dev->nr_rates )
		return;		/* couldn't find the rate */

	rc80211_update ( dev, RX, ridx, retry, 0 );
}

/**
 * Free rate-control context
 *
 * @v ctx	Rate-control context
 */
void rc80211_free ( struct rc80211_ctx *ctx )
{
	free ( ctx );
}