quiche/recovery/congestion/bbr/
per_ack.rs1use std::cmp;
28
29use super::*;
30use crate::rand;
31use crate::recovery::MINIMUM_WINDOW_PACKETS;
32
33const PACING_RATE_1_2MBPS: u64 = 1200 * 1000 / 8;
35
36const PACING_RATE_24MBPS: u64 = 24 * 1000 * 1000 / 8;
38
39#[inline]
41fn bbr_min_pipe_cwnd(r: &mut Congestion) -> usize {
42 BBR_MIN_PIPE_CWND_PKTS * r.max_datagram_size
43}
44
45pub fn bbr_update_model_and_state(
48 r: &mut Congestion, packet: &Acked, bytes_in_flight: usize, now: Instant,
49) {
50 bbr_update_btlbw(r, packet, bytes_in_flight);
51 bbr_check_cycle_phase(r, now);
52 bbr_check_full_pipe(r);
53 bbr_check_drain(r, bytes_in_flight, now);
54 bbr_update_rtprop(r, now);
55 bbr_check_probe_rtt(r, bytes_in_flight, now);
56}
57
58pub fn bbr_update_control_parameters(
59 r: &mut Congestion, bytes_in_flight: usize, now: Instant,
60) {
61 pacing::bbr_set_pacing_rate(r);
62 bbr_set_send_quantum(r);
63
64 r.set_pacing_rate(r.bbr_state.pacing_rate, now);
67
68 bbr_set_cwnd(r, bytes_in_flight);
69}
70
71fn bbr_update_btlbw(r: &mut Congestion, packet: &Acked, _bytes_in_flight: usize) {
76 bbr_update_round(r, packet);
77
78 if r.delivery_rate().to_bytes_per_second() >= r.bbr_state.btlbw ||
79 !r.delivery_rate.sample_is_app_limited()
80 {
81 r.bbr_state.btlbw = r.bbr_state.btlbwfilter.running_max(
84 BTLBW_FILTER_LEN,
85 r.bbr_state.start_time + Duration::from_secs(r.bbr_state.round_count),
86 r.delivery_rate().to_bytes_per_second(),
87 );
88 }
89}
90
91fn bbr_update_round(r: &mut Congestion, packet: &Acked) {
93 let bbr = &mut r.bbr_state;
94
95 if packet.delivered >= bbr.next_round_delivered {
96 bbr.next_round_delivered = r.delivery_rate.delivered();
97 bbr.round_count += 1;
98 bbr.round_start = true;
99 bbr.packet_conservation = false;
100 } else {
101 bbr.round_start = false;
102 }
103}
104
105fn bbr_update_rtprop(r: &mut Congestion, now: Instant) {
107 let bbr = &mut r.bbr_state;
108 let rs_rtt = r.delivery_rate.sample_rtt();
109
110 bbr.rtprop_expired = now > bbr.rtprop_stamp + RTPROP_FILTER_LEN;
111
112 if !rs_rtt.is_zero() && (rs_rtt <= bbr.rtprop || bbr.rtprop_expired) {
113 bbr.rtprop = rs_rtt;
114 bbr.rtprop_stamp = now;
115 }
116}
117
118fn bbr_set_send_quantum(r: &mut Congestion) {
120 let rate = r.bbr_state.pacing_rate;
121
122 r.send_quantum = match rate {
123 rate if rate < PACING_RATE_1_2MBPS => r.max_datagram_size,
124
125 rate if rate < PACING_RATE_24MBPS => 2 * r.max_datagram_size,
126
127 _ => cmp::min((rate / 1000_u64) as usize, 64 * 1024),
128 }
129}
130
131fn bbr_inflight(r: &mut Congestion, gain: f64) -> usize {
133 let bbr = &mut r.bbr_state;
134
135 if bbr.rtprop == Duration::MAX {
136 return r.max_datagram_size * r.initial_congestion_window_packets;
137 }
138
139 let quanta = 3 * r.send_quantum;
140 let estimated_bdp = bbr.btlbw as f64 * bbr.rtprop.as_secs_f64();
141
142 (gain * estimated_bdp) as usize + quanta
143}
144
145fn bbr_update_target_cwnd(r: &mut Congestion) {
146 r.bbr_state.target_cwnd = bbr_inflight(r, r.bbr_state.cwnd_gain);
147}
148
149pub fn bbr_save_cwnd(r: &mut Congestion) -> usize {
151 if !r.bbr_state.in_recovery && r.bbr_state.state != BBRStateMachine::ProbeRTT
152 {
153 r.congestion_window
154 } else {
155 r.congestion_window.max(r.bbr_state.prior_cwnd)
156 }
157}
158
159pub fn bbr_restore_cwnd(r: &mut Congestion) {
160 r.congestion_window = r.congestion_window.max(r.bbr_state.prior_cwnd);
161}
162
163fn bbr_modulate_cwnd_for_recovery(r: &mut Congestion, bytes_in_flight: usize) {
164 let acked_bytes = r.bbr_state.newly_acked_bytes;
165 let lost_bytes = r.bbr_state.newly_lost_bytes;
166
167 if lost_bytes > 0 {
168 r.congestion_window = r
170 .congestion_window
171 .saturating_sub(lost_bytes)
172 .max(r.max_datagram_size * MINIMUM_WINDOW_PACKETS);
173 }
174
175 if r.bbr_state.packet_conservation {
176 r.congestion_window =
177 r.congestion_window.max(bytes_in_flight + acked_bytes);
178 }
179}
180
181fn bbr_modulate_cwnd_for_probe_rtt(r: &mut Congestion) {
183 if r.bbr_state.state == BBRStateMachine::ProbeRTT {
184 r.congestion_window = r.congestion_window.min(bbr_min_pipe_cwnd(r))
185 }
186}
187
188fn bbr_set_cwnd(r: &mut Congestion, bytes_in_flight: usize) {
190 let acked_bytes = r.bbr_state.newly_acked_bytes;
191
192 bbr_update_target_cwnd(r);
193 bbr_modulate_cwnd_for_recovery(r, bytes_in_flight);
194
195 if !r.bbr_state.packet_conservation {
196 if r.bbr_state.filled_pipe {
197 r.congestion_window = cmp::min(
198 r.congestion_window + acked_bytes,
199 r.bbr_state.target_cwnd,
200 )
201 } else if r.congestion_window < r.bbr_state.target_cwnd ||
202 r.delivery_rate.delivered() <
203 r.max_datagram_size * r.initial_congestion_window_packets
204 {
205 r.congestion_window += acked_bytes;
206 }
207
208 r.congestion_window = r.congestion_window.max(bbr_min_pipe_cwnd(r))
209 }
210
211 bbr_modulate_cwnd_for_probe_rtt(r);
212}
213
214fn bbr_check_full_pipe(r: &mut Congestion) {
216 if r.bbr_state.filled_pipe ||
218 !r.bbr_state.round_start ||
219 r.delivery_rate.sample_is_app_limited()
220 {
221 return;
222 }
223
224 if r.bbr_state.btlbw >=
226 (r.bbr_state.full_bw as f64 * BTLBW_GROWTH_TARGET) as u64
227 {
228 r.bbr_state.full_bw = r.bbr_state.btlbw;
230 r.bbr_state.full_bw_count = 0;
231 return;
232 }
233
234 r.bbr_state.full_bw_count += 1;
236
237 if r.bbr_state.full_bw_count >= 3 {
238 r.bbr_state.filled_pipe = true;
239 }
240}
241
242fn bbr_enter_drain(r: &mut Congestion) {
244 let bbr = &mut r.bbr_state;
245
246 bbr.state = BBRStateMachine::Drain;
247
248 bbr.pacing_gain = 1.0 / BBR_HIGH_GAIN;
250
251 bbr.cwnd_gain = BBR_HIGH_GAIN;
253}
254
255fn bbr_check_drain(r: &mut Congestion, bytes_in_flight: usize, now: Instant) {
256 if r.bbr_state.state == BBRStateMachine::Startup && r.bbr_state.filled_pipe {
257 bbr_enter_drain(r);
258 }
259
260 if r.bbr_state.state == BBRStateMachine::Drain &&
261 bbr_bytes_in_net(r, bytes_in_flight, now) <= bbr_inflight(r, 1.0)
262 {
263 bbr_enter_probe_bw(r, now);
265 }
266}
267
268fn bbr_bytes_in_net(r: &Congestion, in_flight: usize, now: Instant) -> usize {
279 let edt = r.pacer.next_time().max(now);
280 let interval = edt.saturating_duration_since(now);
281 let interval_delivered = interval.as_secs_f64() * r.bbr_state.btlbw as f64;
282
283 let mut in_flight_at_edt = in_flight;
284
285 if r.bbr_state.pacing_gain > 1.0 {
286 in_flight_at_edt += r.send_quantum;
287 }
288
289 in_flight_at_edt.saturating_sub(interval_delivered as usize)
290}
291
292fn bbr_enter_probe_bw(r: &mut Congestion, now: Instant) {
294 let bbr = &mut r.bbr_state;
295
296 bbr.state = BBRStateMachine::ProbeBW;
297 bbr.pacing_gain = 1.0;
298 bbr.cwnd_gain = 2.0;
299
300 bbr.cycle_index = BBR_GAIN_CYCLE_LEN -
306 1 -
307 (rand::rand_u64_uniform(BBR_GAIN_CYCLE_LEN as u64 - 1) as usize);
308
309 bbr_advance_cycle_phase(r, now);
310}
311
312fn bbr_check_cycle_phase(r: &mut Congestion, now: Instant) {
313 let bbr = &mut r.bbr_state;
314
315 if bbr.state == BBRStateMachine::ProbeBW && bbr_is_next_cycle_phase(r, now) {
316 bbr_advance_cycle_phase(r, now);
317 }
318}
319
320fn bbr_advance_cycle_phase(r: &mut Congestion, now: Instant) {
321 let bbr = &mut r.bbr_state;
322
323 bbr.cycle_stamp = now;
324 bbr.cycle_index = (bbr.cycle_index + 1) % BBR_GAIN_CYCLE_LEN;
325 bbr.pacing_gain = PACING_GAIN_CYCLE[bbr.cycle_index];
326}
327
328fn bbr_is_next_cycle_phase(r: &mut Congestion, now: Instant) -> bool {
329 let bbr = &mut r.bbr_state;
330 let lost_bytes = bbr.newly_lost_bytes;
331 let pacing_gain = bbr.pacing_gain;
332
333 let is_full_length = (now - bbr.cycle_stamp) > bbr.rtprop;
334
335 let prior_in_flight = bbr.prior_bytes_in_flight;
336 let prior_in_flight = bbr_bytes_in_net(r, prior_in_flight, now);
337
338 if (pacing_gain - 1.0).abs() < f64::EPSILON {
340 return is_full_length;
341 }
342
343 if pacing_gain > 1.0 {
344 return is_full_length &&
345 (lost_bytes > 0 ||
346 prior_in_flight >= bbr_inflight(r, pacing_gain));
347 }
348
349 is_full_length || prior_in_flight <= bbr_inflight(r, 1.0)
350}
351
352fn bbr_check_probe_rtt(r: &mut Congestion, bytes_in_flight: usize, now: Instant) {
354 if r.bbr_state.state != BBRStateMachine::ProbeRTT &&
355 r.bbr_state.rtprop_expired &&
356 !r.bbr_state.idle_restart
357 {
358 bbr_enter_probe_rtt(r);
359
360 r.bbr_state.prior_cwnd = bbr_save_cwnd(r);
361 r.bbr_state.probe_rtt_done_stamp = None;
362 }
363
364 if r.bbr_state.state == BBRStateMachine::ProbeRTT {
365 bbr_handle_probe_rtt(r, bytes_in_flight, now);
366 }
367
368 r.bbr_state.idle_restart = false;
369}
370
371fn bbr_enter_probe_rtt(r: &mut Congestion) {
372 let bbr = &mut r.bbr_state;
373
374 bbr.state = BBRStateMachine::ProbeRTT;
375 bbr.pacing_gain = 1.0;
376 bbr.cwnd_gain = 1.0;
377}
378
379fn bbr_handle_probe_rtt(
380 r: &mut Congestion, bytes_in_flight: usize, now: Instant,
381) {
382 r.delivery_rate.update_app_limited(true);
384
385 if let Some(probe_rtt_done_stamp) = r.bbr_state.probe_rtt_done_stamp {
386 if r.bbr_state.round_start {
387 r.bbr_state.probe_rtt_round_done = true;
388 }
389
390 if r.bbr_state.probe_rtt_round_done && now > probe_rtt_done_stamp {
391 r.bbr_state.rtprop_stamp = now;
392
393 bbr_restore_cwnd(r);
394 bbr_exit_probe_rtt(r, now);
395 }
396 } else if bytes_in_flight <= bbr_min_pipe_cwnd(r) {
397 r.bbr_state.probe_rtt_done_stamp = Some(now + PROBE_RTT_DURATION);
398 r.bbr_state.probe_rtt_round_done = false;
399 r.bbr_state.next_round_delivered = r.delivery_rate.delivered();
400 }
401}
402
403fn bbr_exit_probe_rtt(r: &mut Congestion, now: Instant) {
404 if r.bbr_state.filled_pipe {
405 bbr_enter_probe_bw(r, now);
406 } else {
407 init::bbr_enter_startup(r);
408 }
409}