2 * Simple 802.11 rate-control algorithm for gPXE.
4 * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>.
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.
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.
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.
21 FILE_LICENCE ( GPL2_OR_LATER
);
24 #include <gpxe/net80211.h>
29 * Simple 802.11 rate-control algorithm
32 /** @page rc80211 Rate control philosophy
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
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.
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.
45 * Indications of TX success are more reliable than RX success, but RX
46 * information helps us know where to start.
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.
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
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.
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
86 /** Two-bit packet status indicator for a packet with no retries */
89 /** Two-bit packet status indicator for a packet with one retry */
90 #define RC_PKT_RETRIED_ONCE 0x2
92 /** Two-bit packet status indicator for a TX packet with multiple retries
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.
99 #define RC_PKT_RETRIED_MULTI 0x1
101 /** Two-bit packet status indicator for a TX packet that was never ACKed
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.
108 #define RC_PKT_FAILED 0x0
110 /** Number of times to weight TX packets more heavily than RX packets */
111 #define RC_TX_FACTOR 4
113 /** Number of consecutive failed TX packets that cause an automatic rate drop */
114 #define RC_TX_EMERG_FAIL 3
116 /** Minimum net goodness below which we will search for a better rate */
117 #define RC_GOODNESS_MIN 85
119 /** Maximum net goodness above which we will try to increase our rate */
120 #define RC_GOODNESS_MAX 95
122 /** Minimum (num RX + @c RC_TX_FACTOR * num TX) to use a certain rate */
123 #define RC_UNCERTAINTY_THRESH 4
131 /** A rate control context */
134 /** Goodness state for each rate, TX and RX */
135 u32 goodness
[2][NET80211_MAX_RATES
];
137 /** Number of packets recorded for each rate */
138 u8 count
[2][NET80211_MAX_RATES
];
140 /** Indication of whether we've set the device rate yet */
143 /** Counter of all packets sent and received */
148 * Initialize rate-control algorithm
150 * @v dev 802.11 device
151 * @ret ctx Rate-control context, to be stored in @c dev->rctl
153 struct rc80211_ctx
* rc80211_init ( struct net80211_device
*dev __unused
)
155 struct rc80211_ctx
*ret
= zalloc ( sizeof ( *ret
) );
160 * Calculate net goodness for a certain rate
162 * @v ctx Rate-control context
163 * @v rate_idx Index of rate to calculate net goodness for
165 static int rc80211_calc_net_goodness ( struct rc80211_ctx
*ctx
,
168 int sum
[2], num
[2], dir
, pkt
;
170 for ( dir
= 0; dir
< 2; dir
++ ) {
171 u32 good
= ctx
->goodness
[dir
][rate_idx
];
173 num
[dir
] = ctx
->count
[dir
][rate_idx
];
176 for ( pkt
= 0; pkt
< num
[dir
]; pkt
++ )
177 sum
[dir
] += ( good
>> ( 2 * pkt
) ) & 0x3;
180 if ( ( num
[TX
] * RC_TX_FACTOR
+ num
[RX
] ) < RC_UNCERTAINTY_THRESH
)
183 return ( 33 * ( sum
[TX
] * RC_TX_FACTOR
+ sum
[RX
] ) /
184 ( num
[TX
] * RC_TX_FACTOR
+ num
[RX
] ) );
188 * Determine the best rate to switch to and return it
190 * @v dev 802.11 device
191 * @ret rate_idx Index of the best rate to switch to
193 static int rc80211_pick_best ( struct net80211_device
*dev
)
195 struct rc80211_ctx
*ctx
= dev
->rctl
;
196 int best_net_good
= 0, best_rate
= -1, i
;
198 for ( i
= 0; i
< dev
->nr_rates
; i
++ ) {
199 int net_good
= rc80211_calc_net_goodness ( ctx
, i
);
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
;
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
);
223 * Set 802.11 device rate
225 * @v dev 802.11 device
226 * @v rate_idx Index of rate to switch to
228 * This is a thin wrapper around net80211_set_rate_idx to insert a
229 * debugging message where appropriate.
231 static inline void rc80211_set_rate ( struct net80211_device
*dev
,
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 );
237 net80211_set_rate_idx ( dev
, rate_idx
);
241 * Check rate-control state and change rate if necessary
243 * @v dev 802.11 device
245 static void rc80211_maybe_set_new ( struct net80211_device
*dev
)
247 struct rc80211_ctx
*ctx
= dev
->rctl
;
250 net_good
= rc80211_calc_net_goodness ( ctx
, dev
->rate
);
252 if ( ! ctx
->started
) {
253 rc80211_set_rate ( dev
, rc80211_pick_best ( dev
) );
257 if ( net_good
< 0 ) /* insufficient data */
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 );
265 rc80211_set_rate ( dev
, rc80211_pick_best ( dev
) );
268 if ( net_good
< RC_GOODNESS_MIN
) {
269 rc80211_set_rate ( dev
, rc80211_pick_best ( dev
) );
274 * Update rate-control state
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
282 static void rc80211_update ( struct net80211_device
*dev
, int direction
,
283 int rate_idx
, int retries
, int failed
)
285 struct rc80211_ctx
*ctx
= dev
->rctl
;
286 u32 goodness
= ctx
->goodness
[direction
][rate_idx
];
288 if ( ctx
->count
[direction
][rate_idx
] < 16 )
289 ctx
->count
[direction
][rate_idx
]++;
293 goodness
|= RC_PKT_FAILED
;
294 else if ( retries
> 1 )
295 goodness
|= RC_PKT_RETRIED_MULTI
;
297 goodness
|= RC_PKT_RETRIED_ONCE
;
299 goodness
|= RC_PKT_OK
;
301 ctx
->goodness
[direction
][rate_idx
] = goodness
;
305 rc80211_maybe_set_new ( dev
);
309 * Update rate-control state for transmitted packet
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
315 void rc80211_update_tx ( struct net80211_device
*dev
, int retries
, int rc
)
317 struct rc80211_ctx
*ctx
= dev
->rctl
;
319 if ( ! ctx
->started
)
322 rc80211_update ( dev
, TX
, dev
->rate
, retries
, rc
);
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
);
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,
338 rc80211_set_rate ( dev
, dev
->rate
- 1 );
344 * Update rate-control state for received packet
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
350 void rc80211_update_rx ( struct net80211_device
*dev
, int retry
, u16 rate
)
354 for ( ridx
= 0; ridx
< dev
->nr_rates
&& dev
->rates
[ridx
] != rate
;
357 if ( ridx
>= dev
->nr_rates
)
358 return; /* couldn't find the rate */
360 rc80211_update ( dev
, RX
, ridx
, retry
, 0 );
364 * Free rate-control context
366 * @v ctx Rate-control context
368 void rc80211_free ( struct rc80211_ctx
*ctx
)