1 /*
2 * ngtcp2
3 *
4 * Copyright (c) 2021 ngtcp2 contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25 #include "ngtcp2_bbr.h"
26
27 #include <assert.h>
28
29 #include "ngtcp2_log.h"
30 #include "ngtcp2_macro.h"
31 #include "ngtcp2_mem.h"
32 #include "ngtcp2_rcvry.h"
33 #include "ngtcp2_rst.h"
34
35 static const double pacing_gain_cycle[] = {1.25, 0.75, 1, 1, 1, 1, 1, 1};
36
37 #define NGTCP2_BBR_GAIN_CYCLELEN \
38 (sizeof(pacing_gain_cycle) / sizeof(pacing_gain_cycle[0]))
39
40 #define NGTCP2_BBR_HIGH_GAIN 2.89
41 #define NGTCP2_BBR_PROBE_RTT_DURATION (200 * NGTCP2_MILLISECONDS)
42 #define NGTCP2_RTPROP_FILTERLEN (10 * NGTCP2_SECONDS)
43 #define NGTCP2_BBR_BTL_BW_FILTERLEN 10
44
45 static void bbr_update_on_ack(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
46 const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
47 static void bbr_update_model_and_state(ngtcp2_bbr_cc *cc,
48 ngtcp2_conn_stat *cstat,
49 const ngtcp2_cc_ack *ack,
50 ngtcp2_tstamp ts);
51 static void bbr_update_control_parameters(ngtcp2_bbr_cc *cc,
52 ngtcp2_conn_stat *cstat,
53 const ngtcp2_cc_ack *ack);
54 static void bbr_on_transmit(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
55 static void bbr_init_round_counting(ngtcp2_bbr_cc *cc);
56 static void bbr_update_round(ngtcp2_bbr_cc *cc, const ngtcp2_cc_ack *ack);
57 static void bbr_update_btl_bw(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
58 const ngtcp2_cc_ack *ack);
59 static void bbr_update_rtprop(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
60 ngtcp2_tstamp ts);
61 static void bbr_init_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
62 static void bbr_set_pacing_rate_with_gain(ngtcp2_bbr_cc *cc,
63 ngtcp2_conn_stat *cstat,
64 double pacing_gain);
65 static void bbr_set_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
66 static void bbr_set_send_quantum(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
67 static void bbr_update_target_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
68 static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr_cc *cc,
69 ngtcp2_conn_stat *cstat,
70 const ngtcp2_cc_ack *ack);
71 static void bbr_save_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
72 static void bbr_restore_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
73 static void bbr_modulate_cwnd_for_probe_rtt(ngtcp2_bbr_cc *cc,
74 ngtcp2_conn_stat *cstat);
75 static void bbr_set_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
76 const ngtcp2_cc_ack *ack);
77 static void bbr_init(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
78 ngtcp2_tstamp initial_ts);
79 static void bbr_enter_startup(ngtcp2_bbr_cc *cc);
80 static void bbr_init_full_pipe(ngtcp2_bbr_cc *cc);
81 static void bbr_check_full_pipe(ngtcp2_bbr_cc *cc);
82 static void bbr_enter_drain(ngtcp2_bbr_cc *cc);
83 static void bbr_check_drain(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
84 ngtcp2_tstamp ts);
85 static void bbr_enter_probe_bw(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts);
86 static void bbr_check_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
87 const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
88 static void bbr_advance_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts);
89 static int bbr_is_next_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
90 const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
91 static void bbr_handle_restart_from_idle(ngtcp2_bbr_cc *cc,
92 ngtcp2_conn_stat *cstat);
93 static void bbr_check_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
94 ngtcp2_tstamp ts);
95 static void bbr_enter_probe_rtt(ngtcp2_bbr_cc *cc);
96 static void bbr_handle_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
97 ngtcp2_tstamp ts);
98 static void bbr_exit_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts);
99
ngtcp2_bbr_cc_init(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat,ngtcp2_rst * rst,ngtcp2_tstamp initial_ts,ngtcp2_rand rand,const ngtcp2_rand_ctx * rand_ctx,ngtcp2_log * log)100 void ngtcp2_bbr_cc_init(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
101 ngtcp2_rst *rst, ngtcp2_tstamp initial_ts,
102 ngtcp2_rand rand, const ngtcp2_rand_ctx *rand_ctx,
103 ngtcp2_log *log) {
104 cc->ccb.log = log;
105 cc->rst = rst;
106 cc->rand = rand;
107 cc->rand_ctx = *rand_ctx;
108 cc->initial_cwnd = cstat->cwnd;
109 bbr_init(cc, cstat, initial_ts);
110 }
111
ngtcp2_bbr_cc_free(ngtcp2_bbr_cc * cc)112 void ngtcp2_bbr_cc_free(ngtcp2_bbr_cc *cc) { (void)cc; }
113
ngtcp2_cc_bbr_cc_init(ngtcp2_cc * cc,ngtcp2_log * log,ngtcp2_conn_stat * cstat,ngtcp2_rst * rst,ngtcp2_tstamp initial_ts,ngtcp2_rand rand,const ngtcp2_rand_ctx * rand_ctx,const ngtcp2_mem * mem)114 int ngtcp2_cc_bbr_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
115 ngtcp2_conn_stat *cstat, ngtcp2_rst *rst,
116 ngtcp2_tstamp initial_ts, ngtcp2_rand rand,
117 const ngtcp2_rand_ctx *rand_ctx,
118 const ngtcp2_mem *mem) {
119 ngtcp2_bbr_cc *bbr_cc;
120
121 bbr_cc = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_bbr_cc));
122 if (bbr_cc == NULL) {
123 return NGTCP2_ERR_NOMEM;
124 }
125
126 ngtcp2_bbr_cc_init(bbr_cc, cstat, rst, initial_ts, rand, rand_ctx, log);
127
128 cc->ccb = &bbr_cc->ccb;
129 cc->on_pkt_acked = ngtcp2_cc_bbr_cc_on_pkt_acked;
130 cc->congestion_event = ngtcp2_cc_bbr_cc_congestion_event;
131 cc->on_spurious_congestion = ngtcp2_cc_bbr_cc_on_spurious_congestion;
132 cc->on_persistent_congestion = ngtcp2_cc_bbr_cc_on_persistent_congestion;
133 cc->on_ack_recv = ngtcp2_cc_bbr_cc_on_ack_recv;
134 cc->on_pkt_sent = ngtcp2_cc_bbr_cc_on_pkt_sent;
135 cc->new_rtt_sample = ngtcp2_cc_bbr_cc_new_rtt_sample;
136 cc->reset = ngtcp2_cc_bbr_cc_reset;
137 cc->event = ngtcp2_cc_bbr_cc_event;
138
139 return 0;
140 }
141
ngtcp2_cc_bbr_cc_free(ngtcp2_cc * cc,const ngtcp2_mem * mem)142 void ngtcp2_cc_bbr_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem) {
143 ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(cc->ccb, ngtcp2_bbr_cc, ccb);
144
145 ngtcp2_bbr_cc_free(bbr_cc);
146 ngtcp2_mem_free(mem, bbr_cc);
147 }
148
ngtcp2_cc_bbr_cc_on_pkt_acked(ngtcp2_cc * ccx,ngtcp2_conn_stat * cstat,const ngtcp2_cc_pkt * pkt,ngtcp2_tstamp ts)149 void ngtcp2_cc_bbr_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
150 const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) {
151 (void)ccx;
152 (void)cstat;
153 (void)pkt;
154 (void)ts;
155 }
156
in_congestion_recovery(const ngtcp2_conn_stat * cstat,ngtcp2_tstamp sent_time)157 static int in_congestion_recovery(const ngtcp2_conn_stat *cstat,
158 ngtcp2_tstamp sent_time) {
159 return cstat->congestion_recovery_start_ts != UINT64_MAX &&
160 sent_time <= cstat->congestion_recovery_start_ts;
161 }
162
ngtcp2_cc_bbr_cc_congestion_event(ngtcp2_cc * ccx,ngtcp2_conn_stat * cstat,ngtcp2_tstamp sent_ts,ngtcp2_tstamp ts)163 void ngtcp2_cc_bbr_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
164 ngtcp2_tstamp sent_ts,
165 ngtcp2_tstamp ts) {
166 ngtcp2_bbr_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
167
168 if (cc->in_loss_recovery || cc->congestion_recovery_start_ts != UINT64_MAX ||
169 in_congestion_recovery(cstat, sent_ts)) {
170 return;
171 }
172
173 cc->congestion_recovery_start_ts = ts;
174 }
175
ngtcp2_cc_bbr_cc_on_spurious_congestion(ngtcp2_cc * ccx,ngtcp2_conn_stat * cstat,ngtcp2_tstamp ts)176 void ngtcp2_cc_bbr_cc_on_spurious_congestion(ngtcp2_cc *ccx,
177 ngtcp2_conn_stat *cstat,
178 ngtcp2_tstamp ts) {
179 ngtcp2_bbr_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
180 (void)ts;
181
182 cc->congestion_recovery_start_ts = UINT64_MAX;
183 cstat->congestion_recovery_start_ts = UINT64_MAX;
184
185 if (cc->in_loss_recovery) {
186 cc->in_loss_recovery = 0;
187 cc->packet_conservation = 0;
188 bbr_restore_cwnd(cc, cstat);
189 }
190 }
191
ngtcp2_cc_bbr_cc_on_persistent_congestion(ngtcp2_cc * ccx,ngtcp2_conn_stat * cstat,ngtcp2_tstamp ts)192 void ngtcp2_cc_bbr_cc_on_persistent_congestion(ngtcp2_cc *ccx,
193 ngtcp2_conn_stat *cstat,
194 ngtcp2_tstamp ts) {
195 ngtcp2_bbr_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
196 (void)ts;
197
198 cstat->congestion_recovery_start_ts = UINT64_MAX;
199 cc->congestion_recovery_start_ts = UINT64_MAX;
200 cc->in_loss_recovery = 0;
201 cc->packet_conservation = 0;
202
203 bbr_save_cwnd(cc, cstat);
204 cstat->cwnd = 2 * cstat->max_udp_payload_size;
205 }
206
ngtcp2_cc_bbr_cc_on_ack_recv(ngtcp2_cc * ccx,ngtcp2_conn_stat * cstat,const ngtcp2_cc_ack * ack,ngtcp2_tstamp ts)207 void ngtcp2_cc_bbr_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
208 const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
209 ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
210
211 bbr_update_on_ack(bbr_cc, cstat, ack, ts);
212 }
213
ngtcp2_cc_bbr_cc_on_pkt_sent(ngtcp2_cc * ccx,ngtcp2_conn_stat * cstat,const ngtcp2_cc_pkt * pkt)214 void ngtcp2_cc_bbr_cc_on_pkt_sent(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
215 const ngtcp2_cc_pkt *pkt) {
216 ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
217 (void)pkt;
218
219 bbr_on_transmit(bbr_cc, cstat);
220 }
221
ngtcp2_cc_bbr_cc_new_rtt_sample(ngtcp2_cc * ccx,ngtcp2_conn_stat * cstat,ngtcp2_tstamp ts)222 void ngtcp2_cc_bbr_cc_new_rtt_sample(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
223 ngtcp2_tstamp ts) {
224 (void)ccx;
225 (void)cstat;
226 (void)ts;
227 }
228
ngtcp2_cc_bbr_cc_reset(ngtcp2_cc * ccx,ngtcp2_conn_stat * cstat,ngtcp2_tstamp ts)229 void ngtcp2_cc_bbr_cc_reset(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
230 ngtcp2_tstamp ts) {
231 ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
232 bbr_init(bbr_cc, cstat, ts);
233 }
234
ngtcp2_cc_bbr_cc_event(ngtcp2_cc * ccx,ngtcp2_conn_stat * cstat,ngtcp2_cc_event_type event,ngtcp2_tstamp ts)235 void ngtcp2_cc_bbr_cc_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
236 ngtcp2_cc_event_type event, ngtcp2_tstamp ts) {
237 (void)ccx;
238 (void)cstat;
239 (void)event;
240 (void)ts;
241 }
242
bbr_update_on_ack(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat,const ngtcp2_cc_ack * ack,ngtcp2_tstamp ts)243 static void bbr_update_on_ack(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
244 const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
245 bbr_update_model_and_state(cc, cstat, ack, ts);
246 bbr_update_control_parameters(cc, cstat, ack);
247 }
248
bbr_update_model_and_state(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat,const ngtcp2_cc_ack * ack,ngtcp2_tstamp ts)249 static void bbr_update_model_and_state(ngtcp2_bbr_cc *cc,
250 ngtcp2_conn_stat *cstat,
251 const ngtcp2_cc_ack *ack,
252 ngtcp2_tstamp ts) {
253 bbr_update_btl_bw(cc, cstat, ack);
254 bbr_check_cycle_phase(cc, cstat, ack, ts);
255 bbr_check_full_pipe(cc);
256 bbr_check_drain(cc, cstat, ts);
257 bbr_update_rtprop(cc, cstat, ts);
258 bbr_check_probe_rtt(cc, cstat, ts);
259 }
260
bbr_update_control_parameters(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat,const ngtcp2_cc_ack * ack)261 static void bbr_update_control_parameters(ngtcp2_bbr_cc *cc,
262 ngtcp2_conn_stat *cstat,
263 const ngtcp2_cc_ack *ack) {
264 bbr_set_pacing_rate(cc, cstat);
265 bbr_set_send_quantum(cc, cstat);
266 bbr_set_cwnd(cc, cstat, ack);
267 }
268
bbr_on_transmit(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat)269 static void bbr_on_transmit(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
270 bbr_handle_restart_from_idle(cc, cstat);
271 }
272
bbr_init_round_counting(ngtcp2_bbr_cc * cc)273 static void bbr_init_round_counting(ngtcp2_bbr_cc *cc) {
274 cc->next_round_delivered = 0;
275 cc->round_start = 0;
276 cc->round_count = 0;
277 }
278
bbr_update_round(ngtcp2_bbr_cc * cc,const ngtcp2_cc_ack * ack)279 static void bbr_update_round(ngtcp2_bbr_cc *cc, const ngtcp2_cc_ack *ack) {
280 if (ack->pkt_delivered >= cc->next_round_delivered) {
281 cc->next_round_delivered = cc->rst->delivered;
282 ++cc->round_count;
283 cc->round_start = 1;
284
285 return;
286 }
287
288 cc->round_start = 0;
289 }
290
bbr_handle_recovery(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat,const ngtcp2_cc_ack * ack)291 static void bbr_handle_recovery(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
292 const ngtcp2_cc_ack *ack) {
293 if (cc->in_loss_recovery) {
294 if (ack->pkt_delivered >= cc->congestion_recovery_next_round_delivered) {
295 cc->packet_conservation = 0;
296 }
297
298 if (!in_congestion_recovery(cstat, ack->largest_acked_sent_ts)) {
299 cc->in_loss_recovery = 0;
300 cc->packet_conservation = 0;
301 bbr_restore_cwnd(cc, cstat);
302 }
303
304 return;
305 }
306
307 if (cc->congestion_recovery_start_ts != UINT64_MAX) {
308 cc->in_loss_recovery = 1;
309 bbr_save_cwnd(cc, cstat);
310 cstat->cwnd = cstat->bytes_in_flight +
311 ngtcp2_max(ack->bytes_delivered, cstat->max_udp_payload_size);
312
313 cstat->congestion_recovery_start_ts = cc->congestion_recovery_start_ts;
314 cc->congestion_recovery_start_ts = UINT64_MAX;
315 cc->packet_conservation = 1;
316 cc->congestion_recovery_next_round_delivered = cc->rst->delivered;
317 }
318 }
319
bbr_update_btl_bw(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat,const ngtcp2_cc_ack * ack)320 static void bbr_update_btl_bw(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
321 const ngtcp2_cc_ack *ack) {
322 bbr_update_round(cc, ack);
323 bbr_handle_recovery(cc, cstat, ack);
324
325 if (cstat->delivery_rate_sec < cc->btl_bw && cc->rst->rs.is_app_limited) {
326 return;
327 }
328
329 ngtcp2_window_filter_update(&cc->btl_bw_filter, cstat->delivery_rate_sec,
330 cc->round_count);
331
332 cc->btl_bw = ngtcp2_window_filter_get_best(&cc->btl_bw_filter);
333 }
334
bbr_update_rtprop(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat,ngtcp2_tstamp ts)335 static void bbr_update_rtprop(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
336 ngtcp2_tstamp ts) {
337 cc->rtprop_expired = ts > cc->rtprop_stamp + NGTCP2_RTPROP_FILTERLEN;
338
339 /* Need valid RTT sample */
340 if (cstat->latest_rtt &&
341 (cstat->latest_rtt <= cc->rt_prop || cc->rtprop_expired)) {
342 cc->rt_prop = cstat->latest_rtt;
343 cc->rtprop_stamp = ts;
344
345 ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
346 "bbr update RTprop=%" PRIu64, cc->rt_prop);
347 }
348 }
349
bbr_init_pacing_rate(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat)350 static void bbr_init_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
351 double nominal_bandwidth =
352 (double)cc->initial_cwnd / (double)NGTCP2_MILLISECONDS;
353
354 cstat->pacing_rate = cc->pacing_gain * nominal_bandwidth;
355 }
356
bbr_set_pacing_rate_with_gain(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat,double pacing_gain)357 static void bbr_set_pacing_rate_with_gain(ngtcp2_bbr_cc *cc,
358 ngtcp2_conn_stat *cstat,
359 double pacing_gain) {
360 double rate = pacing_gain * (double)cc->btl_bw / NGTCP2_SECONDS;
361
362 if (cc->filled_pipe || rate > cstat->pacing_rate) {
363 cstat->pacing_rate = rate;
364 }
365 }
366
bbr_set_pacing_rate(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat)367 static void bbr_set_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
368 bbr_set_pacing_rate_with_gain(cc, cstat, cc->pacing_gain);
369 }
370
bbr_set_send_quantum(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat)371 static void bbr_set_send_quantum(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
372 uint64_t send_quantum;
373 (void)cc;
374
375 if (cstat->pacing_rate < 1.2 * 1024 * 1024 / 8 / NGTCP2_SECONDS) {
376 cstat->send_quantum = cstat->max_udp_payload_size;
377 } else if (cstat->pacing_rate < 24.0 * 1024 * 1024 / 8 / NGTCP2_SECONDS) {
378 cstat->send_quantum = cstat->max_udp_payload_size * 2;
379 } else {
380 send_quantum =
381 (uint64_t)(cstat->pacing_rate * (double)(cstat->min_rtt == UINT64_MAX
382 ? NGTCP2_MILLISECONDS
383 : cstat->min_rtt));
384 cstat->send_quantum = (size_t)ngtcp2_min(send_quantum, 64 * 1024);
385 }
386
387 cstat->send_quantum =
388 ngtcp2_max(cstat->send_quantum, cstat->max_udp_payload_size * 10);
389 }
390
bbr_inflight(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat,double gain)391 static uint64_t bbr_inflight(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
392 double gain) {
393 uint64_t quanta = 3 * cstat->send_quantum;
394 double estimated_bdp;
395
396 if (cc->rt_prop == UINT64_MAX) {
397 /* no valid RTT samples yet */
398 return cc->initial_cwnd;
399 }
400
401 estimated_bdp = (double)cc->btl_bw * (double)cc->rt_prop / NGTCP2_SECONDS;
402
403 return (uint64_t)(gain * estimated_bdp) + quanta;
404 }
405
bbr_update_target_cwnd(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat)406 static void bbr_update_target_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
407 cc->target_cwnd = bbr_inflight(cc, cstat, cc->cwnd_gain);
408 }
409
bbr_modulate_cwnd_for_recovery(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat,const ngtcp2_cc_ack * ack)410 static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr_cc *cc,
411 ngtcp2_conn_stat *cstat,
412 const ngtcp2_cc_ack *ack) {
413 if (ack->bytes_lost > 0) {
414 if (cstat->cwnd > ack->bytes_lost) {
415 cstat->cwnd -= ack->bytes_lost;
416 cstat->cwnd = ngtcp2_max(cstat->cwnd, 2 * cstat->max_udp_payload_size);
417 } else {
418 cstat->cwnd = cstat->max_udp_payload_size;
419 }
420 }
421
422 if (cc->packet_conservation) {
423 cstat->cwnd =
424 ngtcp2_max(cstat->cwnd, cstat->bytes_in_flight + ack->bytes_delivered);
425 }
426 }
427
bbr_save_cwnd(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat)428 static void bbr_save_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
429 if (!cc->in_loss_recovery && cc->state != NGTCP2_BBR_STATE_PROBE_RTT) {
430 cc->prior_cwnd = cstat->cwnd;
431 return;
432 }
433
434 cc->prior_cwnd = ngtcp2_max(cc->prior_cwnd, cstat->cwnd);
435 }
436
bbr_restore_cwnd(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat)437 static void bbr_restore_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
438 cstat->cwnd = ngtcp2_max(cstat->cwnd, cc->prior_cwnd);
439 }
440
min_pipe_cwnd(size_t max_udp_payload_size)441 static uint64_t min_pipe_cwnd(size_t max_udp_payload_size) {
442 return max_udp_payload_size * 4;
443 }
444
bbr_modulate_cwnd_for_probe_rtt(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat)445 static void bbr_modulate_cwnd_for_probe_rtt(ngtcp2_bbr_cc *cc,
446 ngtcp2_conn_stat *cstat) {
447 if (cc->state == NGTCP2_BBR_STATE_PROBE_RTT) {
448 cstat->cwnd =
449 ngtcp2_min(cstat->cwnd, min_pipe_cwnd(cstat->max_udp_payload_size));
450 }
451 }
452
bbr_set_cwnd(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat,const ngtcp2_cc_ack * ack)453 static void bbr_set_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
454 const ngtcp2_cc_ack *ack) {
455 bbr_update_target_cwnd(cc, cstat);
456 bbr_modulate_cwnd_for_recovery(cc, cstat, ack);
457
458 if (!cc->packet_conservation) {
459 if (cc->filled_pipe) {
460 cstat->cwnd =
461 ngtcp2_min(cstat->cwnd + ack->bytes_delivered, cc->target_cwnd);
462 } else if (cstat->cwnd < cc->target_cwnd ||
463 cc->rst->delivered < cc->initial_cwnd) {
464 cstat->cwnd += ack->bytes_delivered;
465 }
466
467 cstat->cwnd =
468 ngtcp2_max(cstat->cwnd, min_pipe_cwnd(cstat->max_udp_payload_size));
469 }
470
471 bbr_modulate_cwnd_for_probe_rtt(cc, cstat);
472 }
473
bbr_init(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat,ngtcp2_tstamp initial_ts)474 static void bbr_init(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
475 ngtcp2_tstamp initial_ts) {
476 cc->pacing_gain = NGTCP2_BBR_HIGH_GAIN;
477 cc->prior_cwnd = 0;
478 cc->target_cwnd = 0;
479 cc->btl_bw = 0;
480 cc->rt_prop = UINT64_MAX;
481 cc->rtprop_stamp = initial_ts;
482 cc->cycle_stamp = UINT64_MAX;
483 cc->probe_rtt_done_stamp = UINT64_MAX;
484 cc->cycle_index = 0;
485 cc->rtprop_expired = 0;
486 cc->idle_restart = 0;
487 cc->packet_conservation = 0;
488 cc->probe_rtt_round_done = 0;
489
490 cc->congestion_recovery_start_ts = UINT64_MAX;
491 cc->congestion_recovery_next_round_delivered = 0;
492 cc->in_loss_recovery = 0;
493
494 cstat->send_quantum = cstat->max_udp_payload_size * 10;
495
496 ngtcp2_window_filter_init(&cc->btl_bw_filter, NGTCP2_BBR_BTL_BW_FILTERLEN);
497
498 bbr_init_round_counting(cc);
499 bbr_init_full_pipe(cc);
500 bbr_init_pacing_rate(cc, cstat);
501 bbr_enter_startup(cc);
502 }
503
bbr_enter_startup(ngtcp2_bbr_cc * cc)504 static void bbr_enter_startup(ngtcp2_bbr_cc *cc) {
505 cc->state = NGTCP2_BBR_STATE_STARTUP;
506 cc->pacing_gain = NGTCP2_BBR_HIGH_GAIN;
507 cc->cwnd_gain = NGTCP2_BBR_HIGH_GAIN;
508 }
509
bbr_init_full_pipe(ngtcp2_bbr_cc * cc)510 static void bbr_init_full_pipe(ngtcp2_bbr_cc *cc) {
511 cc->filled_pipe = 0;
512 cc->full_bw = 0;
513 cc->full_bw_count = 0;
514 }
515
bbr_check_full_pipe(ngtcp2_bbr_cc * cc)516 static void bbr_check_full_pipe(ngtcp2_bbr_cc *cc) {
517 if (cc->filled_pipe || !cc->round_start || cc->rst->rs.is_app_limited) {
518 /* no need to check for a full pipe now. */
519 return;
520 }
521
522 /* cc->btl_bw still growing? */
523 if (cc->btl_bw * 100 >= cc->full_bw * 125) {
524 /* record new baseline level */
525 cc->full_bw = cc->btl_bw;
526 cc->full_bw_count = 0;
527 return;
528 }
529 /* another round w/o much growth */
530 ++cc->full_bw_count;
531 if (cc->full_bw_count >= 3) {
532 cc->filled_pipe = 1;
533 ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
534 "bbr filled pipe, btl_bw=%" PRIu64, cc->btl_bw);
535 }
536 }
537
bbr_enter_drain(ngtcp2_bbr_cc * cc)538 static void bbr_enter_drain(ngtcp2_bbr_cc *cc) {
539 cc->state = NGTCP2_BBR_STATE_DRAIN;
540 /* pace slowly */
541 cc->pacing_gain = 1.0 / NGTCP2_BBR_HIGH_GAIN;
542 /* maintain cwnd */
543 cc->cwnd_gain = NGTCP2_BBR_HIGH_GAIN;
544 }
545
bbr_check_drain(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat,ngtcp2_tstamp ts)546 static void bbr_check_drain(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
547 ngtcp2_tstamp ts) {
548 if (cc->state == NGTCP2_BBR_STATE_STARTUP && cc->filled_pipe) {
549 ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
550 "bbr exit Startup and enter Drain");
551
552 bbr_enter_drain(cc);
553 }
554
555 if (cc->state == NGTCP2_BBR_STATE_DRAIN &&
556 cstat->bytes_in_flight <= bbr_inflight(cc, cstat, 1.0)) {
557 ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
558 "bbr exit Drain and enter ProbeBW");
559
560 /* we estimate queue is drained */
561 bbr_enter_probe_bw(cc, ts);
562 }
563 }
564
bbr_enter_probe_bw(ngtcp2_bbr_cc * cc,ngtcp2_tstamp ts)565 static void bbr_enter_probe_bw(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts) {
566 uint8_t rand;
567
568 cc->state = NGTCP2_BBR_STATE_PROBE_BW;
569 cc->pacing_gain = 1;
570 cc->cwnd_gain = 2;
571
572 assert(cc->rand);
573
574 cc->rand(&rand, 1, &cc->rand_ctx);
575
576 cc->cycle_index = NGTCP2_BBR_GAIN_CYCLELEN - 1 - (size_t)(rand * 7 / 256);
577 bbr_advance_cycle_phase(cc, ts);
578 }
579
bbr_check_cycle_phase(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat,const ngtcp2_cc_ack * ack,ngtcp2_tstamp ts)580 static void bbr_check_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
581 const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
582 if (cc->state == NGTCP2_BBR_STATE_PROBE_BW &&
583 bbr_is_next_cycle_phase(cc, cstat, ack, ts)) {
584 bbr_advance_cycle_phase(cc, ts);
585 }
586 }
587
bbr_advance_cycle_phase(ngtcp2_bbr_cc * cc,ngtcp2_tstamp ts)588 static void bbr_advance_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts) {
589 cc->cycle_stamp = ts;
590 cc->cycle_index = (cc->cycle_index + 1) & (NGTCP2_BBR_GAIN_CYCLELEN - 1);
591 cc->pacing_gain = pacing_gain_cycle[cc->cycle_index];
592 }
593
bbr_is_next_cycle_phase(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat,const ngtcp2_cc_ack * ack,ngtcp2_tstamp ts)594 static int bbr_is_next_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
595 const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
596 int is_full_length = (ts - cc->cycle_stamp) > cc->rt_prop;
597
598 if (cc->pacing_gain > 1) {
599 return is_full_length && (ack->bytes_lost > 0 ||
600 ack->prior_bytes_in_flight >=
601 bbr_inflight(cc, cstat, cc->pacing_gain));
602 }
603
604 if (cc->pacing_gain < 1) {
605 return is_full_length ||
606 ack->prior_bytes_in_flight <= bbr_inflight(cc, cstat, 1);
607 }
608
609 return is_full_length;
610 }
611
bbr_handle_restart_from_idle(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat)612 static void bbr_handle_restart_from_idle(ngtcp2_bbr_cc *cc,
613 ngtcp2_conn_stat *cstat) {
614 if (cstat->bytes_in_flight == 0 && cc->rst->app_limited) {
615 ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr restart from idle");
616
617 cc->idle_restart = 1;
618
619 if (cc->state == NGTCP2_BBR_STATE_PROBE_BW) {
620 bbr_set_pacing_rate_with_gain(cc, cstat, 1);
621 }
622 }
623 }
624
bbr_check_probe_rtt(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat,ngtcp2_tstamp ts)625 static void bbr_check_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
626 ngtcp2_tstamp ts) {
627 if (cc->state != NGTCP2_BBR_STATE_PROBE_RTT && cc->rtprop_expired &&
628 !cc->idle_restart) {
629 ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr enter ProbeRTT");
630
631 bbr_enter_probe_rtt(cc);
632 bbr_save_cwnd(cc, cstat);
633 cc->probe_rtt_done_stamp = UINT64_MAX;
634 }
635
636 if (cc->state == NGTCP2_BBR_STATE_PROBE_RTT) {
637 bbr_handle_probe_rtt(cc, cstat, ts);
638 }
639
640 cc->idle_restart = 0;
641 }
642
bbr_enter_probe_rtt(ngtcp2_bbr_cc * cc)643 static void bbr_enter_probe_rtt(ngtcp2_bbr_cc *cc) {
644 cc->state = NGTCP2_BBR_STATE_PROBE_RTT;
645 cc->pacing_gain = 1;
646 cc->cwnd_gain = 1;
647 }
648
bbr_handle_probe_rtt(ngtcp2_bbr_cc * cc,ngtcp2_conn_stat * cstat,ngtcp2_tstamp ts)649 static void bbr_handle_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
650 ngtcp2_tstamp ts) {
651 uint64_t app_limited = cc->rst->delivered + cstat->bytes_in_flight;
652
653 /* Ignore low rate samples during NGTCP2_BBR_STATE_PROBE_RTT. */
654 cc->rst->app_limited = app_limited ? app_limited : 1;
655
656 if (cc->probe_rtt_done_stamp == UINT64_MAX &&
657 cstat->bytes_in_flight <= min_pipe_cwnd(cstat->max_udp_payload_size)) {
658 cc->probe_rtt_done_stamp = ts + NGTCP2_BBR_PROBE_RTT_DURATION;
659 cc->probe_rtt_round_done = 0;
660 cc->next_round_delivered = cc->rst->delivered;
661
662 return;
663 }
664
665 if (cc->probe_rtt_done_stamp != UINT64_MAX) {
666 if (cc->round_start) {
667 cc->probe_rtt_round_done = 1;
668 }
669
670 if (cc->probe_rtt_round_done && ts > cc->probe_rtt_done_stamp) {
671 cc->rtprop_stamp = ts;
672 bbr_restore_cwnd(cc, cstat);
673 bbr_exit_probe_rtt(cc, ts);
674 }
675 }
676 }
677
bbr_exit_probe_rtt(ngtcp2_bbr_cc * cc,ngtcp2_tstamp ts)678 static void bbr_exit_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts) {
679 if (cc->filled_pipe) {
680 ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
681 "bbr exit ProbeRTT and enter ProbeBW");
682
683 bbr_enter_probe_bw(cc, ts);
684
685 return;
686 }
687
688 ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
689 "bbr exit ProbeRTT and enter Startup");
690
691 bbr_enter_startup(cc);
692 }
693