Home | History | Annotate | Download | only in 80211
      1 /*
      2  * Simple 802.11 rate-control algorithm for gPXE.
      3  *
      4  * Copyright (c) 2009 Joshua Oreman <oremanj (at) 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 
     21 FILE_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 */
    132 struct 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  */
    153 struct 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  */
    165 static 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  */
    193 static 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  */
    231 static 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  */
    245 static 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  */
    282 static 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  */
    315 void 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  */
    350 void 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  */
    368 void rc80211_free ( struct rc80211_ctx *ctx )
    369 {
    370 	free ( ctx );
    371 }
    372