rc80211.c

Go to the documentation of this file.
00001 /*
00002  * Simple 802.11 rate-control algorithm for gPXE.
00003  *
00004  * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>.
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License as
00008  * published by the Free Software Foundation; either version 2 of the
00009  * License, or any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful, but
00012  * WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00019  */
00020 
00021 FILE_LICENCE ( GPL2_OR_LATER );
00022 
00023 #include <stdlib.h>
00024 #include <gpxe/net80211.h>
00025 
00026 /**
00027  * @file
00028  *
00029  * Simple 802.11 rate-control algorithm
00030  */
00031 
00032 /** @page rc80211 Rate control philosophy
00033  *
00034  * We want to maximize our transmission speed, to the extent that we
00035  * can do that without dropping undue numbers of packets. We also
00036  * don't want to take up very much code space, so our algorithm has to
00037  * be pretty simple
00038  *
00039  * When we receive a packet, we know what rate it was transmitted at,
00040  * and whether it had to be retransmitted to get to us.
00041  *
00042  * When we send a packet, we hear back how many times it had to be
00043  * retried to get through, and whether it got through at all.
00044  *
00045  * Indications of TX success are more reliable than RX success, but RX
00046  * information helps us know where to start.
00047  *
00048  * To handle all of this, we keep for each rate and each direction (TX
00049  * and RX separately) some state information for the most recent
00050  * packets on that rate and the number of packets for which we have
00051  * information. The state is a 32-bit unsigned integer in which two
00052  * bits represent a packet: 11 if it went through well, 10 if it went
00053  * through with one retry, 01 if it went through with more than one
00054  * retry, or 00 if it didn't go through at all. We define the
00055  * "goodness" for a particular (rate, direction) combination as the
00056  * sum of all the 2-bit fields, times 33, divided by the number of
00057  * 2-bit fields containing valid information (16 except when we're
00058  * starting out). The number produced is between 0 and 99; we use -1
00059  * for rates with less than 4 RX packets or 1 TX, as an indicator that
00060  * we do not have enough information to rely on them.
00061  *
00062  * In deciding which rates are best, we find the weighted average of
00063  * TX and RX goodness, where the weighting is by number of packets
00064  * with data and TX packets are worth 4 times as much as RX packets.
00065  * The weighted average is called "net goodness" and is also a number
00066  * between 0 and 99.  If 3 consecutive packets fail transmission
00067  * outright, we automatically ratchet down the rate; otherwise, we
00068  * switch to the best rate whenever the current rate's goodness falls
00069  * below some threshold, and try increasing our rate when the goodness
00070  * is very high.
00071  *
00072  * This system is optimized for gPXE's style of usage. Because normal
00073  * operation always involves receiving something, we'll make our way
00074  * to the best rate pretty quickly. We tend to follow the lead of the
00075  * sending AP in choosing rates, but we won't use rates for long that
00076  * don't work well for us in transmission. We assume gPXE won't be
00077  * running for long enough that rate patterns will change much, so we
00078  * don't have to keep time counters or the like.  And if this doesn't
00079  * work well in practice there are many ways it could be tweaked.
00080  *
00081  * To avoid staying at 1Mbps for a long time, we don't track any
00082  * transmitted packets until we've set our rate based on received
00083  * packets.
00084  */
00085 
00086 /** Two-bit packet status indicator for a packet with no retries */
00087 #define RC_PKT_OK               0x3
00088 
00089 /** Two-bit packet status indicator for a packet with one retry */
00090 #define RC_PKT_RETRIED_ONCE     0x2
00091 
00092 /** Two-bit packet status indicator for a TX packet with multiple retries
00093  *
00094  * It is not possible to tell whether an RX packet had one or multiple
00095  * retries; we rely instead on the fact that failed RX packets won't
00096  * get to us at all, so if we receive a lot of RX packets on a certain
00097  * rate it must be pretty good.
00098  */
00099 #define RC_PKT_RETRIED_MULTI    0x1
00100 
00101 /** Two-bit packet status indicator for a TX packet that was never ACKed
00102  *
00103  * It is not possible to tell whether an RX packet was setn if it
00104  * didn't get through to us, but if we don't see one we won't increase
00105  * the goodness for its rate. This asymmetry is part of why TX packets
00106  * are weighted much more heavily than RX.
00107  */
00108 #define RC_PKT_FAILED           0x0
00109 
00110 /** Number of times to weight TX packets more heavily than RX packets */
00111 #define RC_TX_FACTOR            4
00112 
00113 /** Number of consecutive failed TX packets that cause an automatic rate drop */
00114 #define RC_TX_EMERG_FAIL        3
00115 
00116 /** Minimum net goodness below which we will search for a better rate */
00117 #define RC_GOODNESS_MIN         85
00118 
00119 /** Maximum net goodness above which we will try to increase our rate */
00120 #define RC_GOODNESS_MAX         95
00121 
00122 /** Minimum (num RX + @c RC_TX_FACTOR * num TX) to use a certain rate */
00123 #define RC_UNCERTAINTY_THRESH   4
00124 
00125 /** TX direction */
00126 #define TX      0
00127 
00128 /** RX direction */
00129 #define RX      1
00130 
00131 /** A rate control context */
00132 struct rc80211_ctx
00133 {
00134         /** Goodness state for each rate, TX and RX */
00135         u32 goodness[2][NET80211_MAX_RATES];
00136 
00137         /** Number of packets recorded for each rate */
00138         u8 count[2][NET80211_MAX_RATES];
00139 
00140         /** Indication of whether we've set the device rate yet */
00141         int started;
00142 
00143         /** Counter of all packets sent and received */
00144         int packets;
00145 };
00146 
00147 /**
00148  * Initialize rate-control algorithm
00149  *
00150  * @v dev       802.11 device
00151  * @ret ctx     Rate-control context, to be stored in @c dev->rctl
00152  */
00153 struct rc80211_ctx * rc80211_init ( struct net80211_device *dev __unused )
00154 {
00155         struct rc80211_ctx *ret = zalloc ( sizeof ( *ret ) );
00156         return ret;
00157 }
00158 
00159 /**
00160  * Calculate net goodness for a certain rate
00161  *
00162  * @v ctx       Rate-control context
00163  * @v rate_idx  Index of rate to calculate net goodness for
00164  */
00165 static int rc80211_calc_net_goodness ( struct rc80211_ctx *ctx,
00166                                        int rate_idx )
00167 {
00168         int sum[2], num[2], dir, pkt;
00169 
00170         for ( dir = 0; dir < 2; dir++ ) {
00171                 u32 good = ctx->goodness[dir][rate_idx];
00172 
00173                 num[dir] = ctx->count[dir][rate_idx];
00174                 sum[dir] = 0;
00175 
00176                 for ( pkt = 0; pkt < num[dir]; pkt++ )
00177                         sum[dir] += ( good >> ( 2 * pkt ) ) & 0x3;
00178         }
00179 
00180         if ( ( num[TX] * RC_TX_FACTOR + num[RX] ) < RC_UNCERTAINTY_THRESH )
00181                 return -1;
00182 
00183         return ( 33 * ( sum[TX] * RC_TX_FACTOR + sum[RX] ) /
00184                       ( num[TX] * RC_TX_FACTOR + num[RX] ) );
00185 }
00186 
00187 /**
00188  * Determine the best rate to switch to and return it
00189  *
00190  * @v dev               802.11 device
00191  * @ret rate_idx        Index of the best rate to switch to
00192  */
00193 static int rc80211_pick_best ( struct net80211_device *dev )
00194 {
00195         struct rc80211_ctx *ctx = dev->rctl;
00196         int best_net_good = 0, best_rate = -1, i;
00197 
00198         for ( i = 0; i < dev->nr_rates; i++ ) {
00199                 int net_good = rc80211_calc_net_goodness ( ctx, i );
00200 
00201                 if ( net_good > best_net_good ||
00202                      ( best_net_good > RC_GOODNESS_MIN &&
00203                        net_good > RC_GOODNESS_MIN ) ) {
00204                         best_net_good = net_good;
00205                         best_rate = i;
00206                 }
00207         }
00208 
00209         if ( best_rate >= 0 ) {
00210                 int old_good = rc80211_calc_net_goodness ( ctx, dev->rate );
00211                 if ( old_good != best_net_good )
00212                         DBGC ( ctx, "802.11 RC %p switching from goodness "
00213                                "%d to %d\n", ctx, old_good, best_net_good );
00214 
00215                 ctx->started = 1;
00216                 return best_rate;
00217         }
00218 
00219         return dev->rate;
00220 }
00221 
00222 /**
00223  * Set 802.11 device rate
00224  *
00225  * @v dev       802.11 device
00226  * @v rate_idx  Index of rate to switch to
00227  *
00228  * This is a thin wrapper around net80211_set_rate_idx to insert a
00229  * debugging message where appropriate.
00230  */
00231 static inline void rc80211_set_rate ( struct net80211_device *dev,
00232                                       int rate_idx )
00233 {
00234         DBGC ( dev->rctl, "802.11 RC %p changing rate %d->%d Mbps\n", dev->rctl,
00235                dev->rates[dev->rate] / 10, dev->rates[rate_idx] / 10 );
00236 
00237         net80211_set_rate_idx ( dev, rate_idx );
00238 }
00239 
00240 /**
00241  * Check rate-control state and change rate if necessary
00242  *
00243  * @v dev       802.11 device
00244  */
00245 static void rc80211_maybe_set_new ( struct net80211_device *dev )
00246 {
00247         struct rc80211_ctx *ctx = dev->rctl;
00248         int net_good;
00249 
00250         net_good = rc80211_calc_net_goodness ( ctx, dev->rate );
00251 
00252         if ( ! ctx->started ) {
00253                 rc80211_set_rate ( dev, rc80211_pick_best ( dev ) );
00254                 return;
00255         }
00256 
00257         if ( net_good < 0 )     /* insufficient data */
00258                 return;
00259 
00260         if ( net_good > RC_GOODNESS_MAX && dev->rate + 1 < dev->nr_rates ) {
00261                 int higher = rc80211_calc_net_goodness ( ctx, dev->rate + 1 );
00262                 if ( higher > net_good || higher < 0 )
00263                         rc80211_set_rate ( dev, dev->rate + 1 );
00264                 else
00265                         rc80211_set_rate ( dev, rc80211_pick_best ( dev ) );
00266         }
00267 
00268         if ( net_good < RC_GOODNESS_MIN ) {
00269                 rc80211_set_rate ( dev, rc80211_pick_best ( dev ) );
00270         }
00271 }
00272 
00273 /**
00274  * Update rate-control state
00275  *
00276  * @v dev               802.11 device
00277  * @v direction         One of the direction constants TX or RX
00278  * @v rate_idx          Index of rate at which packet was sent or received
00279  * @v retries           Number of times packet was retried before success
00280  * @v failed            If nonzero, the packet failed to get through
00281  */
00282 static void rc80211_update ( struct net80211_device *dev, int direction,
00283                              int rate_idx, int retries, int failed )
00284 {
00285         struct rc80211_ctx *ctx = dev->rctl;
00286         u32 goodness = ctx->goodness[direction][rate_idx];
00287 
00288         if ( ctx->count[direction][rate_idx] < 16 )
00289                 ctx->count[direction][rate_idx]++;
00290 
00291         goodness <<= 2;
00292         if ( failed )
00293                 goodness |= RC_PKT_FAILED;
00294         else if ( retries > 1 )
00295                 goodness |= RC_PKT_RETRIED_MULTI;
00296         else if ( retries )
00297                 goodness |= RC_PKT_RETRIED_ONCE;
00298         else
00299                 goodness |= RC_PKT_OK;
00300 
00301         ctx->goodness[direction][rate_idx] = goodness;
00302 
00303         ctx->packets++;
00304 
00305         rc80211_maybe_set_new ( dev );
00306 }
00307 
00308 /**
00309  * Update rate-control state for transmitted packet
00310  *
00311  * @v dev       802.11 device
00312  * @v retries   Number of times packet was transmitted before success
00313  * @v rc        Return status code for transmission
00314  */
00315 void rc80211_update_tx ( struct net80211_device *dev, int retries, int rc )
00316 {
00317         struct rc80211_ctx *ctx = dev->rctl;
00318 
00319         if ( ! ctx->started )
00320                 return;
00321 
00322         rc80211_update ( dev, TX, dev->rate, retries, rc );
00323 
00324         /* Check if the last RC_TX_EMERG_FAIL packets have all failed */
00325         if ( ! ( ctx->goodness[TX][dev->rate] &
00326                  ( ( 1 << ( 2 * RC_TX_EMERG_FAIL ) ) - 1 ) ) ) {
00327                 if ( dev->rate == 0 )
00328                         DBGC ( dev->rctl, "802.11 RC %p saw %d consecutive "
00329                                "failed TX, but cannot lower rate any further\n",
00330                                dev->rctl, RC_TX_EMERG_FAIL );
00331                 else {
00332                         DBGC ( dev->rctl, "802.11 RC %p lowering rate (%d->%d "
00333                                "Mbps) due to %d consecutive TX failures\n",
00334                                dev->rctl, dev->rates[dev->rate] / 10,
00335                                dev->rates[dev->rate - 1] / 10,
00336                                RC_TX_EMERG_FAIL );
00337 
00338                         rc80211_set_rate ( dev, dev->rate - 1 );
00339                 }
00340         }
00341 }
00342 
00343 /**
00344  * Update rate-control state for received packet
00345  *
00346  * @v dev       802.11 device
00347  * @v retry     Whether the received packet had been retransmitted
00348  * @v rate      Rate at which packet was received, in 100 kbps units
00349  */
00350 void rc80211_update_rx ( struct net80211_device *dev, int retry, u16 rate )
00351 {
00352         int ridx;
00353 
00354         for ( ridx = 0; ridx < dev->nr_rates && dev->rates[ridx] != rate;
00355               ridx++ )
00356                 ;
00357         if ( ridx >= dev->nr_rates )
00358                 return;         /* couldn't find the rate */
00359 
00360         rc80211_update ( dev, RX, ridx, retry, 0 );
00361 }
00362 
00363 /**
00364  * Free rate-control context
00365  *
00366  * @v ctx       Rate-control context
00367  */
00368 void rc80211_free ( struct rc80211_ctx *ctx )
00369 {
00370         free ( ctx );
00371 }

Generated on Tue Apr 6 20:01:09 2010 for gPXE by  doxygen 1.5.7.1