|
| 1 | +#include <inttypes.h> |
| 2 | + |
| 3 | +#include <haproxy/quic_cc.h> |
| 4 | +#include <haproxy/ticks.h> |
| 5 | +#include <haproxy/window_filter.h> |
| 6 | + |
| 7 | +/* XXX TO BE REMOVED */ |
| 8 | +#define true 1 |
| 9 | +#define false 0 |
| 10 | + |
| 11 | +#define BBR_STARTUP_PACING_GAIN 277 /* percent: 4 * ln(2)=2.77 */ |
| 12 | +#define BBR_STARTUP_CWND_GAIN 200 /* percent */ |
| 13 | + |
| 14 | +/* BBRLossThresh (2%) */ |
| 15 | +#define BBR_LOSS_THRESH_MULT 2 |
| 16 | +#define BBR_LOSS_THRESH_DIVI 100 |
| 17 | +/* BBRBeta (0.7) */ |
| 18 | +#define BBR_BETA_MULT 7 |
| 19 | +#define BBR_BETA_DIVI 10 |
| 20 | +/* BBRHeadroom (0.15) */ |
| 21 | +#define BBR_HEADROOM_MULT 15 |
| 22 | +#define BBR_HEADROOM_DIVI 100 |
| 23 | + |
| 24 | +#define BBR_MAX_BW_FILTERLEN 2 |
| 25 | +#define BBR_EXTRA_ACKED_FILTERLEN 10 |
| 26 | + |
| 27 | +#define BBR_MIN_RTT_FILTERLEN 10000 /* ms */ |
| 28 | +#define BBR_PROBE_RTT_CWND_GAIN 50 /* 50% */ |
| 29 | +#define BBR_PROBE_RTT_DURATION 200 /* ms */ |
| 30 | +#define BBR_PROBE_RTT_INTERVAL 5000 /* ms */ |
| 31 | + |
| 32 | +/* 4.1.1: State Transition Diagram */ |
| 33 | +/* BBR state */ |
| 34 | +enum bbr_state { |
| 35 | + BBR_ST_STARTUP, |
| 36 | + BBR_ST_DRAIN, |
| 37 | + BBR_ST_PROBE_BW_DOWN, |
| 38 | + BBR_ST_PROBE_BW_CRUISE, |
| 39 | + BBR_ST_PROBE_BW_REFILL, |
| 40 | + BBR_ST_PROBE_BW_UP, |
| 41 | + BBR_ST_PROBE_RTT, |
| 42 | +}; |
| 43 | + |
| 44 | +struct bbr { |
| 45 | + /* 2.4 Output Control Parameters */ |
| 46 | + uint64_t pacing_rate; |
| 47 | + uint64_t send_quantum; |
| 48 | + /* 2.5 Pacing State and Parameters */ |
| 49 | + uint64_t pacing_gain; |
| 50 | + //uint32_t next_departure_time; /* XXX check this XXX */ |
| 51 | + /* 2.6. cwnd State and Parameters */ |
| 52 | + uint64_t cwnd_gain; |
| 53 | + /* 2.7 General Algorithm State */ |
| 54 | + enum bbr_state state; |
| 55 | + uint64_t round_count; |
| 56 | + int round_start; /* boolean */ |
| 57 | + uint64_t next_round_delivered; |
| 58 | + int idle_restart; /* boolean */ |
| 59 | + /* 2.9.1 Data Rate Network Path Model Parameters */ |
| 60 | + uint64_t max_bw; |
| 61 | + uint64_t bw_lo; |
| 62 | + uint64_t bw; |
| 63 | + uint64_t prior_cwnd; |
| 64 | + /* 2.9.2 Data Volume Network Path Model Parameters */ |
| 65 | + uint32_t min_rtt; |
| 66 | + uint64_t extra_acked; |
| 67 | + uint64_t offload_budget; |
| 68 | + uint64_t max_inflight; |
| 69 | + uint64_t inflight_hi; |
| 70 | + uint64_t inflight_lo; |
| 71 | + /* 2.10 State for Responding to Congestion */ |
| 72 | + uint64_t bw_latest; |
| 73 | + uint64_t loss_in_round; |
| 74 | + uint64_t inflight_latest; |
| 75 | + /* 2.11 Estimating BBR.max_bw */ |
| 76 | + struct window_filter max_bw_filter; |
| 77 | + uint64_t cycle_count; |
| 78 | + /* 2.12 Estimating BBR.extra_acked */ |
| 79 | + uint32_t extra_acked_interval_start; |
| 80 | + uint64_t extra_acked_delivered; |
| 81 | + struct window_filter extra_acked_filter; |
| 82 | + /* 2.13 Startup Parameters and State */ |
| 83 | + int full_bw_reached; /* boolean */ |
| 84 | + int full_bw_now; /* boolean */ |
| 85 | + uint64_t full_bw; |
| 86 | + int full_bw_count; |
| 87 | + /* 2.14 ProbeRTT and min_rtt Parameters and State */ |
| 88 | + /* 2.14.1 Parameters for Estimating BBR.min_rtt */ |
| 89 | + uint32_t min_rtt_stamp; |
| 90 | + /* 2.14.2 Parameters for Scheduling ProbeRTT */ |
| 91 | + uint32_t probe_rtt_min_delay; /* ms */ |
| 92 | + uint32_t probe_rtt_min_stamp; /* ms */ |
| 93 | + uint32_t probe_rtt_done_stamp; |
| 94 | + int probe_rtt_round_done; /* boolean */ |
| 95 | + int probe_rtt_expired; /* boolean */ |
| 96 | +}; |
| 97 | + |
| 98 | +static void bbr_reset_congestion_signals(struct bbr *bbr) |
| 99 | +{ |
| 100 | + bbr->loss_in_round = 0; |
| 101 | + bbr->bw_latest = 0; |
| 102 | + bbr->inflight_latest = 0; |
| 103 | +} |
| 104 | + |
| 105 | +static void bbr_reset_lower_bounds(struct bbr *bbr) |
| 106 | +{ |
| 107 | + bbr->bw_lo = UINT64_MAX; |
| 108 | + bbr->inflight_lo = UINT64_MAX; |
| 109 | +} |
| 110 | + |
| 111 | +static void bbr_init_round_counting(struct bbr *bbr) |
| 112 | +{ |
| 113 | + bbr->next_round_delivered = 0; |
| 114 | + bbr->round_start = false; |
| 115 | + bbr->round_count = 0; |
| 116 | +} |
| 117 | + |
| 118 | +static void bbr_reset_full_bw(struct bbr *bbr) |
| 119 | +{ |
| 120 | + bbr->full_bw = 0; |
| 121 | + bbr->full_bw_count = 0; |
| 122 | + bbr->full_bw_now = false; |
| 123 | +} |
| 124 | + |
| 125 | +static void bbr_init_pacing_rate(struct bbr *bbr) |
| 126 | +{ |
| 127 | + /* XXX Not clear at this time XXX */ |
| 128 | +} |
| 129 | + |
| 130 | +static void bbr_enter_startup(struct bbr *bbr) |
| 131 | +{ |
| 132 | + bbr->state = BBR_ST_STARTUP; |
| 133 | + bbr->pacing_gain = BBR_STARTUP_PACING_GAIN; |
| 134 | + bbr->cwnd_gain = BBR_STARTUP_CWND_GAIN; |
| 135 | +} |
| 136 | + |
| 137 | +static int quic_cc_bbr_init(struct quic_cc *cc) |
| 138 | +{ |
| 139 | + struct bbr *bbr = quic_cc_priv(cc); |
| 140 | + |
| 141 | + window_filter_init(&bbr->max_bw_filter, BBR_MAX_BW_FILTERLEN); |
| 142 | + window_filter_init(&bbr->extra_acked_filter, BBR_EXTRA_ACKED_FILTERLEN); |
| 143 | + /* InitWindowedMaxFilter() */ |
| 144 | + bbr->min_rtt = 1; /* ms */ /* XXX check this XXX */ |
| 145 | + bbr->min_rtt_stamp = now_ms; |
| 146 | + bbr->probe_rtt_done_stamp = TICK_ETERNITY; /* XXX check this XXX */ |
| 147 | + bbr->probe_rtt_round_done = false; |
| 148 | + bbr->prior_cwnd = 0; |
| 149 | + bbr->idle_restart = false; |
| 150 | + bbr->extra_acked_interval_start = now_ms; |
| 151 | + bbr->extra_acked_delivered = 0; |
| 152 | + bbr->full_bw_reached = false; |
| 153 | + |
| 154 | + bbr_reset_congestion_signals(bbr); |
| 155 | + bbr_reset_lower_bounds(bbr); |
| 156 | + bbr_init_round_counting(bbr); |
| 157 | + bbr_reset_full_bw(bbr); |
| 158 | + bbr_init_pacing_rate(bbr); |
| 159 | + bbr_enter_startup(bbr); |
| 160 | + |
| 161 | + /* Not in RFC */ |
| 162 | + bbr->send_quantum = 0; /* XXX check this */ |
| 163 | + bbr->max_bw = 0; |
| 164 | + bbr->bw = 0; |
| 165 | + bbr->extra_acked = 0; |
| 166 | + bbr->offload_budget = 0; |
| 167 | + bbr->max_inflight = 0; |
| 168 | + bbr->inflight_hi = UINT64_MAX; |
| 169 | + bbr->cycle_count = 0; |
| 170 | + bbr->probe_rtt_min_delay = TICK_ETERNITY; |
| 171 | + bbr->probe_rtt_min_stamp = now_ms; |
| 172 | + bbr->probe_rtt_expired = false; |
| 173 | + |
| 174 | + return 1; |
| 175 | +} |
| 176 | + |
| 177 | +static void bbr_handle_restart_from_idle(struct bbr *bbr) |
| 178 | +{ |
| 179 | +} |
| 180 | + |
| 181 | +static void bbr_on_transmit(struct bbr *bbr) |
| 182 | +{ |
| 183 | + bbr_handle_restart_from_idle(bbr); |
| 184 | +} |
| 185 | + |
| 186 | +static bbr_check_startup_high_loss() |
| 187 | +{ |
| 188 | +} |
| 189 | + |
| 190 | +static void bbr_check_startup_done(struct bbr *bbr) |
| 191 | +{ |
| 192 | + bbr_check_startup_high_loss(); |
| 193 | + if (bbr->state == BBR_ST_STARTUP and bbr->full_bw_reached) |
| 194 | + bbr_enter_drain(); |
| 195 | +} |
| 196 | + |
| 197 | +static void bbr_update_model_and_state() |
| 198 | +{ |
| 199 | + bbr_update_latest_delivery_signals(); |
| 200 | + bbr_update_congestion_signals(); |
| 201 | + bbr_update_ack_aggregation(); |
| 202 | + bbr_check_full_bw_reached(); |
| 203 | + bbr_check_startup_done(); |
| 204 | + bbr_check_drain_done(); |
| 205 | + bbr_update_probe_bw_cycle_phase(); |
| 206 | + bbr_update_min_rtt(); |
| 207 | + bbr_check_probe_rtt(); |
| 208 | + bbr_advance_latest_delivery_signals(); |
| 209 | + bbr_bound_bw_for_model(); |
| 210 | +} |
| 211 | + |
| 212 | +static void bbr_update_control_parameters() |
| 213 | +{ |
| 214 | + bbr_set_pacing_rate(); |
| 215 | + bbr_set_send_quantum(); |
| 216 | + bbr_set_cwnd(); |
| 217 | +} |
| 218 | + |
| 219 | +static void bbr_update_on_ack() |
| 220 | +{ |
| 221 | + bbr_update_model_and_state(); |
| 222 | + bbr_update_control_parameters(); |
| 223 | +} |
| 224 | + |
| 225 | +static void bbr_note_loss(struct bbr *bbr) |
| 226 | +{ |
| 227 | + if (!bbr->loss_in_round) /* first loss in this round trip? */ |
| 228 | + bbr->loss_round_delivered = C.delivered; |
| 229 | + bbr->loss_in_round = 1; |
| 230 | +} |
| 231 | + |
| 232 | +/* At what prefix of packet did losses exceed BBRLossThresh? */ |
| 233 | +static uint64_t bbr_inflight_hi_from_lost_packet(struct quic *rs, packet) |
| 234 | +{ |
| 235 | + size = packet.size; |
| 236 | + /* What was in flight before this packet? */ |
| 237 | + inflight_prev = rs->tx_in_flight - size; |
| 238 | + /* What was lost before this packet? */ |
| 239 | + lost_prev = rs->lost - size; |
| 240 | + lost_prefix = (BBR_LOSS_THRESH * inflight_prev - lost_prev) / |
| 241 | + (1 - BBR_LOSS_THRESH); |
| 242 | + /* At what inflight value did losses cross BBRLossThresh? */ |
| 243 | + inflight = inflight_prev + lost_prefix; |
| 244 | + |
| 245 | + return inflight; |
| 246 | +} |
| 247 | + |
| 248 | +static bbr_handle_lost_packet(packet) |
| 249 | +{ |
| 250 | + struct quic_rs *rs; |
| 251 | + |
| 252 | + bbr_note_loss(); |
| 253 | + if (!bbr->bw_probe_samples) |
| 254 | + return /* not a packet sent while probing bandwidth */ |
| 255 | + |
| 256 | + rs->tx_in_flight = packet.tx_in_flight; /* inflight at transmit */ |
| 257 | + rs->lost = C.lost - packet.lost; /* data lost since transmit */ |
| 258 | + rs->is_app_limited = packet.is_app_limited; |
| 259 | + if (is_flight_too_high(rs)) { |
| 260 | + rs->tx_in_flight = bbr_inflight_hi_from_lost_packet(rs, packet); |
| 261 | + bbr_handle_inflight_too_high(rs) |
| 262 | + } |
| 263 | +} |
| 264 | + |
| 265 | +static bbr_update_on_loss(packet) |
| 266 | +{ |
| 267 | + bbr_handle_lost_packet(packet); |
| 268 | +} |
| 269 | + |
| 270 | +struct quic_cc_algo quic_cc_algo_bbr = { |
| 271 | + .type = QUIC_CC_ALGO_TP_BBR, |
| 272 | + .init = quic_cc_bbr_init, |
| 273 | +}; |
| 274 | + |
| 275 | +void quic_cc_bbr_check(void) |
| 276 | +{ |
| 277 | + struct quic_cc *cc; |
| 278 | + BUG_ON_HOT(sizeof(struct bbr) > sizeof(cc->priv)); |
| 279 | +} |
| 280 | + |
| 281 | +INITCALL0(STG_REGISTER, quic_cc_bbr_check); |
0 commit comments