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 iPXE'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 iPXE 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.