quiche/recovery/congestion/bbr2/
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
36#[inline]
38fn bbr2_min_pipe_cwnd(r: &mut Congestion) -> usize {
39 MIN_PIPE_CWND_PKTS * r.max_datagram_size
40}
41
42pub fn bbr2_update_model_and_state(
45 r: &mut Congestion, packet: &Acked, in_flight: usize, now: Instant,
46) {
47 per_loss::bbr2_update_latest_delivery_signals(r);
48 per_loss::bbr2_update_congestion_signals(r, packet);
49 bbr2_update_ack_aggregation(r, packet, now);
50 bbr2_check_startup_done(r);
51 bbr2_check_drain(r, in_flight, now);
52 bbr2_update_probe_bw_cycle_phase(r, in_flight, now);
53 bbr2_update_min_rtt(r, now);
54 bbr2_check_probe_rtt(r, in_flight, now);
55 per_loss::bbr2_advance_latest_delivery_signals(r);
56 per_loss::bbr2_bound_bw_for_model(r);
57}
58
59pub fn bbr2_update_control_parameters(
60 r: &mut Congestion, in_flight: usize, now: Instant,
61) {
62 pacing::bbr2_set_pacing_rate(r);
63 bbr2_set_send_quantum(r);
64
65 r.set_pacing_rate(r.bbr2_state.pacing_rate, now);
68
69 bbr2_set_cwnd(r, in_flight);
70}
71
72fn bbr2_check_startup_done(r: &mut Congestion) {
77 bbr2_check_startup_full_bandwidth(r);
78 bbr2_check_startup_high_loss(r);
79
80 if r.bbr2_state.state == BBR2StateMachine::Startup && r.bbr2_state.filled_pipe
81 {
82 bbr2_enter_drain(r);
83 }
84}
85
86fn bbr2_check_startup_full_bandwidth(r: &mut Congestion) {
88 if r.bbr2_state.filled_pipe ||
89 !r.bbr2_state.round_start ||
90 r.delivery_rate.sample_is_app_limited()
91 {
92 return;
94 }
95
96 if r.bbr2_state.max_bw >=
98 (r.bbr2_state.full_bw as f64 * MAX_BW_GROWTH_THRESHOLD) as u64
99 {
100 r.bbr2_state.full_bw = r.bbr2_state.max_bw;
102 r.bbr2_state.full_bw_count = 0;
103 return;
104 }
105
106 r.bbr2_state.full_bw_count += 1;
108
109 if r.bbr2_state.full_bw_count >= MAX_BW_COUNT {
110 r.bbr2_state.filled_pipe = true;
111 }
112}
113
114fn bbr2_check_startup_high_loss(r: &mut Congestion) {
116 if r.bbr2_state.loss_round_start &&
118 r.bbr2_state.in_recovery &&
119 r.bbr2_state.loss_events_in_round >= FULL_LOSS_COUNT as usize &&
120 per_loss::bbr2_is_inflight_too_high(r)
121 {
122 bbr2_handle_queue_too_high_in_startup(r);
123 }
124 if r.bbr2_state.loss_round_start {
125 r.bbr2_state.loss_events_in_round = 0
126 }
127}
128
129fn bbr2_handle_queue_too_high_in_startup(r: &mut Congestion) {
130 r.bbr2_state.filled_pipe = true;
131 r.bbr2_state.inflight_hi = bbr2_inflight(r, r.bbr2_state.max_bw, 1.0);
132}
133
134fn bbr2_enter_drain(r: &mut Congestion) {
136 let bbr = &mut r.bbr2_state;
137
138 bbr.state = BBR2StateMachine::Drain;
139
140 bbr.pacing_gain = PACING_GAIN / STARTUP_CWND_GAIN;
142
143 bbr.cwnd_gain = STARTUP_CWND_GAIN;
145}
146
147fn bbr2_check_drain(r: &mut Congestion, in_flight: usize, now: Instant) {
148 if r.bbr2_state.state == BBR2StateMachine::Drain &&
149 in_flight <= bbr2_inflight(r, r.bbr2_state.max_bw, 1.0)
150 {
151 bbr2_enter_probe_bw(r, now);
153 }
154}
155
156fn bbr2_check_time_to_probe_bw(r: &mut Congestion, now: Instant) -> bool {
159 if bbr2_has_elapsed_in_phase(r, r.bbr2_state.bw_probe_wait, now) ||
161 bbr2_is_reno_coexistence_probe_time(r)
162 {
163 bbr2_start_probe_bw_refill(r);
164
165 return true;
166 }
167
168 false
169}
170
171fn bbr2_pick_probe_wait(r: &mut Congestion) {
174 let bbr = &mut r.bbr2_state;
175
176 bbr.rounds_since_probe = rand::rand_u8() as usize % 2;
178
179 bbr.bw_probe_wait = Duration::from_secs_f64(
181 2.0 + rand::rand_u64_uniform(1000000) as f64 / 1000000.0,
182 );
183}
184
185fn bbr2_is_reno_coexistence_probe_time(r: &mut Congestion) -> bool {
186 let reno_rounds = bbr2_target_inflight(r);
187 let rounds = reno_rounds.min(63);
188
189 r.bbr2_state.rounds_since_probe >= rounds
190}
191
192pub fn bbr2_target_inflight(r: &mut Congestion) -> usize {
195 r.bbr2_state.bdp.min(r.congestion_window)
196}
197
198fn bbr2_enter_probe_bw(r: &mut Congestion, now: Instant) {
200 bbr2_start_probe_bw_down(r, now);
201}
202
203pub fn bbr2_start_probe_bw_down(r: &mut Congestion, now: Instant) {
204 per_loss::bbr2_reset_congestion_signals(r);
205
206 r.bbr2_state.probe_up_cnt = usize::MAX;
208
209 bbr2_pick_probe_wait(r);
210
211 r.bbr2_state.cycle_stamp = now;
213 r.bbr2_state.ack_phase = BBR2AckPhase::ProbeStopping;
214
215 bbr2_start_round(r);
216
217 r.bbr2_state.state = BBR2StateMachine::ProbeBWDOWN;
218 r.bbr2_state.pacing_gain = PROBE_DOWN_PACING_GAIN;
219 r.bbr2_state.cwnd_gain = CWND_GAIN
220}
221
222fn bbr2_start_probe_bw_cruise(r: &mut Congestion) {
223 let bbr = &mut r.bbr2_state;
224
225 bbr.state = BBR2StateMachine::ProbeBWCRUISE;
226 bbr.pacing_gain = PACING_GAIN;
227 bbr.cwnd_gain = CWND_GAIN;
228}
229
230fn bbr2_start_probe_bw_refill(r: &mut Congestion) {
231 per_loss::bbr2_reset_lower_bounds(r);
232
233 r.bbr2_state.bw_probe_up_rounds = 0;
234 r.bbr2_state.bw_probe_up_acks = 0;
235 r.bbr2_state.ack_phase = BBR2AckPhase::Refilling;
236
237 bbr2_start_round(r);
238
239 r.bbr2_state.state = BBR2StateMachine::ProbeBWREFILL;
240 r.bbr2_state.pacing_gain = PACING_GAIN;
241 r.bbr2_state.cwnd_gain = CWND_GAIN;
242}
243
244fn bbr2_start_probe_bw_up(r: &mut Congestion, now: Instant) {
245 r.bbr2_state.ack_phase = BBR2AckPhase::ProbeStarting;
246
247 bbr2_start_round(r);
248
249 r.bbr2_state.cycle_stamp = now;
251 r.bbr2_state.state = BBR2StateMachine::ProbeBWUP;
252 r.bbr2_state.pacing_gain = PROBE_UP_PACING_GAIN;
253 r.bbr2_state.cwnd_gain = CWND_GAIN;
254
255 bbr2_raise_inflight_hi_slope(r);
256}
257
258fn bbr2_update_probe_bw_cycle_phase(
260 r: &mut Congestion, in_flight: usize, now: Instant,
261) {
262 if !r.bbr2_state.filled_pipe {
263 return;
265 }
266
267 bbr2_adapt_upper_bounds(r, now);
268
269 if !bbr2_is_in_a_probe_bw_state(r) {
270 return;
272 }
273
274 match r.bbr2_state.state {
275 BBR2StateMachine::ProbeBWDOWN => {
276 if bbr2_check_time_to_probe_bw(r, now) {
277 return;
279 }
280
281 if bbr2_check_time_to_cruise(r, in_flight) {
282 bbr2_start_probe_bw_cruise(r);
283 }
284 },
285
286 BBR2StateMachine::ProbeBWCRUISE => {
287 bbr2_check_time_to_probe_bw(r, now);
288 },
289
290 BBR2StateMachine::ProbeBWREFILL => {
291 if r.bbr2_state.round_start {
293 r.bbr2_state.bw_probe_samples = true;
294
295 bbr2_start_probe_bw_up(r, now);
296 }
297 },
298
299 BBR2StateMachine::ProbeBWUP => {
300 if bbr2_has_elapsed_in_phase(r, r.bbr2_state.min_rtt, now) &&
301 in_flight > bbr2_inflight(r, r.bbr2_state.max_bw, 1.25)
302 {
303 bbr2_start_probe_bw_down(r, now);
304 }
305 },
306
307 _ => (),
308 }
309}
310
311pub fn bbr2_is_in_a_probe_bw_state(r: &mut Congestion) -> bool {
312 let state = r.bbr2_state.state;
313
314 state == BBR2StateMachine::ProbeBWDOWN ||
315 state == BBR2StateMachine::ProbeBWCRUISE ||
316 state == BBR2StateMachine::ProbeBWREFILL ||
317 state == BBR2StateMachine::ProbeBWUP
318}
319
320fn bbr2_check_time_to_cruise(r: &mut Congestion, in_flight: usize) -> bool {
321 if in_flight > bbr2_inflight_with_headroom(r) {
322 return false;
324 }
325
326 if in_flight <= bbr2_inflight(r, r.bbr2_state.max_bw, 1.0) {
327 return true;
329 }
330
331 false
332}
333
334fn bbr2_has_elapsed_in_phase(
335 r: &mut Congestion, interval: Duration, now: Instant,
336) -> bool {
337 now > r.bbr2_state.cycle_stamp + interval
338}
339
340fn bbr2_inflight_with_headroom(r: &mut Congestion) -> usize {
345 let bbr = &mut r.bbr2_state;
346
347 if bbr.inflight_hi == usize::MAX {
348 return usize::MAX;
349 }
350
351 let headroom = ((HEADROOM * bbr.inflight_hi as f64) as usize).max(1);
352
353 bbr.inflight_hi
354 .saturating_sub(headroom)
355 .max(bbr2_min_pipe_cwnd(r))
356}
357
358fn bbr2_raise_inflight_hi_slope(r: &mut Congestion) {
360 let bbr = &mut r.bbr2_state;
361
362 let growth_this_round = (1 << bbr.bw_probe_up_rounds) * r.max_datagram_size;
363
364 bbr.bw_probe_up_rounds = (bbr.bw_probe_up_rounds + 1).min(30);
365 bbr.probe_up_cnt = (r.congestion_window / growth_this_round).max(1);
366}
367
368fn bbr2_probe_inflight_hi_upward(r: &mut Congestion) {
370 if r.app_limited || r.congestion_window < r.bbr2_state.inflight_hi {
371 return;
373 }
374
375 let bbr = &mut r.bbr2_state;
376
377 bbr.bw_probe_up_acks += 1;
379
380 if bbr.bw_probe_up_acks >= bbr.probe_up_cnt {
381 let delta = bbr.bw_probe_up_acks / bbr.probe_up_cnt;
382
383 bbr.bw_probe_up_acks -= delta * bbr.probe_up_cnt;
384
385 bbr.inflight_hi += delta * r.max_datagram_size;
386 }
387
388 if bbr.round_start {
389 bbr2_raise_inflight_hi_slope(r);
390 }
391}
392
393fn bbr2_adapt_upper_bounds(r: &mut Congestion, now: Instant) {
396 if r.bbr2_state.ack_phase == BBR2AckPhase::ProbeStarting &&
397 r.bbr2_state.round_start
398 {
399 r.bbr2_state.ack_phase = BBR2AckPhase::ProbeFeedback;
401 }
402
403 if r.bbr2_state.ack_phase == BBR2AckPhase::ProbeStopping &&
404 r.bbr2_state.round_start
405 {
406 r.bbr2_state.bw_probe_samples = false;
407 r.bbr2_state.ack_phase = BBR2AckPhase::Init;
408
409 if bbr2_is_in_a_probe_bw_state(r) &&
411 !r.delivery_rate.sample_is_app_limited()
412 {
413 bbr2_advance_max_bw_filter(r);
414 }
415 }
416
417 if !per_loss::bbr2_check_inflight_too_high(r, now) {
418 if r.bbr2_state.inflight_hi == usize::MAX ||
420 r.bbr2_state.bw_hi == u64::MAX
421 {
422 return;
424 }
425
426 if r.bbr2_state.tx_in_flight > r.bbr2_state.inflight_hi {
427 r.bbr2_state.inflight_hi = r.bbr2_state.tx_in_flight;
428 }
429
430 let delivery_rate = r.delivery_rate().to_bytes_per_second();
432 if delivery_rate > r.bbr2_state.bw_hi {
433 r.bbr2_state.bw_hi = delivery_rate;
434 }
435
436 if r.bbr2_state.state == BBR2StateMachine::ProbeBWUP {
437 bbr2_probe_inflight_hi_upward(r);
438 }
439 }
440}
441
442fn bbr2_update_min_rtt(r: &mut Congestion, now: Instant) {
445 let bbr = &mut r.bbr2_state;
446
447 bbr.probe_rtt_expired = now > bbr.probe_rtt_min_stamp + PROBE_RTT_INTERVAL;
448
449 let rs_rtt = r.delivery_rate.sample_rtt();
450
451 if !rs_rtt.is_zero() &&
452 (rs_rtt < bbr.probe_rtt_min_delay || bbr.probe_rtt_expired)
453 {
454 bbr.probe_rtt_min_delay = rs_rtt;
455 bbr.probe_rtt_min_stamp = now;
456 }
457
458 let min_rtt_expired =
459 now > bbr.min_rtt_stamp + rs_rtt.saturating_mul(MIN_RTT_FILTER_LEN);
460
461 if bbr.min_rtt == rtt::INITIAL_RTT || min_rtt_expired {
465 bbr.min_rtt = rs_rtt;
468 bbr.min_rtt_stamp = now;
469 }
470}
471
472fn bbr2_check_probe_rtt(r: &mut Congestion, in_flight: usize, now: Instant) {
473 if r.bbr2_state.state != BBR2StateMachine::ProbeRTT &&
474 r.bbr2_state.probe_rtt_expired &&
475 !r.bbr2_state.idle_restart
476 {
477 bbr2_enter_probe_rtt(r);
478
479 r.bbr2_state.prior_cwnd = per_ack::bbr2_save_cwnd(r);
480 r.bbr2_state.probe_rtt_done_stamp = None;
481 r.bbr2_state.ack_phase = BBR2AckPhase::ProbeStopping;
482
483 bbr2_start_round(r);
484 }
485
486 if r.bbr2_state.state == BBR2StateMachine::ProbeRTT {
487 bbr2_handle_probe_rtt(r, in_flight, now);
488 }
489
490 if r.delivery_rate.sample_delivered() > 0 {
491 r.bbr2_state.idle_restart = false;
492 }
493}
494
495fn bbr2_enter_probe_rtt(r: &mut Congestion) {
496 let bbr = &mut r.bbr2_state;
497
498 bbr.state = BBR2StateMachine::ProbeRTT;
499 bbr.pacing_gain = PACING_GAIN;
500 bbr.cwnd_gain = PROBE_RTT_CWND_GAIN;
501}
502
503fn bbr2_handle_probe_rtt(r: &mut Congestion, in_flight: usize, now: Instant) {
504 r.delivery_rate.update_app_limited(true);
506
507 if r.bbr2_state.probe_rtt_done_stamp.is_some() {
508 if r.bbr2_state.round_start {
509 r.bbr2_state.probe_rtt_round_done = true;
510 }
511
512 if r.bbr2_state.probe_rtt_round_done {
513 bbr2_check_probe_rtt_done(r, now);
514 }
515 } else if in_flight <= bbr2_probe_rtt_cwnd(r) {
516 r.bbr2_state.probe_rtt_done_stamp = Some(now + PROBE_RTT_DURATION);
518
519 r.bbr2_state.probe_rtt_round_done = false;
521
522 bbr2_start_round(r);
523 }
524}
525
526pub fn bbr2_check_probe_rtt_done(r: &mut Congestion, now: Instant) {
527 let bbr = &mut r.bbr2_state;
528
529 if let Some(probe_rtt_done_stamp) = bbr.probe_rtt_done_stamp {
530 if now > probe_rtt_done_stamp {
531 bbr.probe_rtt_min_stamp = now;
533
534 bbr2_restore_cwnd(r);
535 bbr2_exit_probe_rtt(r, now);
536 }
537 }
538}
539
540fn bbr2_exit_probe_rtt(r: &mut Congestion, now: Instant) {
542 per_loss::bbr2_reset_lower_bounds(r);
543
544 if r.bbr2_state.filled_pipe {
545 bbr2_start_probe_bw_down(r, now);
546 bbr2_start_probe_bw_cruise(r);
547 } else {
548 init::bbr2_enter_startup(r);
549 }
550}
551
552fn bbr2_update_round(r: &mut Congestion, packet: &Acked) {
554 if packet.delivered >= r.bbr2_state.next_round_delivered {
555 bbr2_start_round(r);
556
557 r.bbr2_state.round_count += 1;
558 r.bbr2_state.rounds_since_probe += 1;
559 r.bbr2_state.round_start = true;
560 } else {
561 r.bbr2_state.round_start = false;
562 }
563}
564
565fn bbr2_start_round(r: &mut Congestion) {
566 r.bbr2_state.next_round_delivered = r.delivery_rate.delivered();
567}
568
569pub fn bbr2_update_max_bw(r: &mut Congestion, packet: &Acked) {
571 bbr2_update_round(r, packet);
572
573 if r.delivery_rate().to_bytes_per_second() >= r.bbr2_state.max_bw ||
574 !r.delivery_rate.sample_is_app_limited()
575 {
576 let max_bw_filter_len = r
577 .delivery_rate
578 .sample_rtt()
579 .saturating_mul(MIN_RTT_FILTER_LEN);
580
581 r.bbr2_state.max_bw = r.bbr2_state.max_bw_filter.running_max(
582 max_bw_filter_len,
583 r.bbr2_state.start_time +
584 Duration::from_secs(r.bbr2_state.cycle_count),
585 r.delivery_rate().to_bytes_per_second(),
586 );
587 }
588}
589
590fn bbr2_advance_max_bw_filter(r: &mut Congestion) {
592 r.bbr2_state.cycle_count += 1;
593}
594
595fn bbr2_update_offload_budget(r: &mut Congestion) {
597 r.bbr2_state.offload_budget = 3 * r.send_quantum;
598}
599
600fn bbr2_update_ack_aggregation(r: &mut Congestion, packet: &Acked, now: Instant) {
602 let bbr = &mut r.bbr2_state;
603
604 let interval = now - bbr.extra_acked_interval_start;
606 let mut expected_delivered =
607 (bbr.bw as f64 * interval.as_secs_f64()) as usize;
608
609 if bbr.extra_acked_delivered <= expected_delivered {
611 bbr.extra_acked_delivered = 0;
612 bbr.extra_acked_interval_start = now;
613 expected_delivered = 0;
614 }
615
616 bbr.extra_acked_delivered += packet.size;
617
618 let extra = bbr.extra_acked_delivered.saturating_sub(expected_delivered);
619 let extra = extra.min(r.congestion_window);
620
621 let extra_acked_filter_len = r
622 .delivery_rate
623 .sample_rtt()
624 .saturating_mul(MIN_RTT_FILTER_LEN);
625
626 bbr.extra_acked = bbr.extra_acked_filter.running_max(
627 extra_acked_filter_len,
628 bbr.start_time + Duration::from_secs(bbr.round_count),
629 extra,
630 );
631}
632
633fn bbr2_set_send_quantum(r: &mut Congestion) {
635 let bbr = &mut r.bbr2_state;
636
637 let rate = bbr.pacing_rate;
638 let floor = if rate < PACING_RATE_1_2MBPS {
639 r.max_datagram_size
640 } else {
641 2 * r.max_datagram_size
642 };
643
644 r.send_quantum = cmp::min((rate / 1000_u64) as usize, 64 * 1024); r.send_quantum = r.send_quantum.max(floor);
646}
647
648fn bbr2_bdp_multiple(r: &mut Congestion, bw: u64, gain: f64) -> usize {
651 let bbr = &mut r.bbr2_state;
652
653 if bbr.min_rtt == Duration::MAX {
654 return r.max_datagram_size * r.initial_congestion_window_packets;
656 }
657
658 bbr.bdp = (bw as f64 * bbr.min_rtt.as_secs_f64()) as usize;
659
660 (gain * bbr.bdp as f64) as usize
661}
662
663fn bbr2_quantization_budget(r: &mut Congestion, inflight: usize) -> usize {
664 bbr2_update_offload_budget(r);
665
666 let inflight = inflight.max(r.bbr2_state.offload_budget);
667 let inflight = inflight.max(bbr2_min_pipe_cwnd(r));
668
669 if r.bbr2_state.state == BBR2StateMachine::ProbeBWUP {
671 return inflight + 2 * r.max_datagram_size;
672 }
673
674 inflight
675}
676
677fn bbr2_inflight(r: &mut Congestion, bw: u64, gain: f64) -> usize {
678 let inflight = bbr2_bdp_multiple(r, bw, gain);
679
680 bbr2_quantization_budget(r, inflight)
681}
682
683fn bbr2_update_max_inflight(r: &mut Congestion) {
684 let inflight =
688 bbr2_bdp_multiple(r, r.bbr2_state.max_bw, r.bbr2_state.cwnd_gain);
689 let inflight = inflight + r.bbr2_state.extra_acked;
690
691 r.bbr2_state.max_inflight = bbr2_quantization_budget(r, inflight);
692}
693
694pub fn bbr2_save_cwnd(r: &mut Congestion) -> usize {
696 if !r.bbr2_state.in_recovery &&
697 r.bbr2_state.state != BBR2StateMachine::ProbeRTT
698 {
699 r.congestion_window
700 } else {
701 r.congestion_window.max(r.bbr2_state.prior_cwnd)
702 }
703}
704
705pub fn bbr2_restore_cwnd(r: &mut Congestion) {
706 r.congestion_window = r.congestion_window.max(r.bbr2_state.prior_cwnd);
707}
708
709fn bbr2_modulate_cwnd_for_recovery(r: &mut Congestion, in_flight: usize) {
710 let acked_bytes = r.bbr2_state.newly_acked_bytes;
711 let lost_bytes = r.bbr2_state.newly_lost_bytes;
712
713 if lost_bytes > 0 {
714 r.congestion_window = r
716 .congestion_window
717 .saturating_sub(lost_bytes)
718 .max(r.max_datagram_size * MINIMUM_WINDOW_PACKETS);
719 }
720
721 if r.bbr2_state.packet_conservation {
722 r.congestion_window = r.congestion_window.max(in_flight + acked_bytes);
723 }
724}
725
726fn bbr2_probe_rtt_cwnd(r: &mut Congestion) -> usize {
728 let probe_rtt_cwnd =
729 bbr2_bdp_multiple(r, r.bbr2_state.bw, PROBE_RTT_CWND_GAIN);
730
731 probe_rtt_cwnd.max(bbr2_min_pipe_cwnd(r))
732}
733
734fn bbr2_bound_cwnd_for_probe_rtt(r: &mut Congestion) {
735 if r.bbr2_state.state == BBR2StateMachine::ProbeRTT {
736 r.congestion_window = r.congestion_window.min(bbr2_probe_rtt_cwnd(r));
737 }
738}
739
740fn bbr2_set_cwnd(r: &mut Congestion, in_flight: usize) {
742 let acked_bytes = r.bbr2_state.newly_acked_bytes;
743
744 bbr2_update_max_inflight(r);
745 bbr2_modulate_cwnd_for_recovery(r, in_flight);
746
747 if !r.bbr2_state.packet_conservation {
748 if r.bbr2_state.filled_pipe {
749 r.congestion_window = cmp::min(
750 r.congestion_window + acked_bytes,
751 r.bbr2_state.max_inflight,
752 )
753 } else if r.congestion_window < r.bbr2_state.max_inflight ||
754 r.delivery_rate.delivered() <
755 r.max_datagram_size * r.initial_congestion_window_packets
756 {
757 r.congestion_window += acked_bytes;
758 }
759
760 r.congestion_window = r.congestion_window.max(bbr2_min_pipe_cwnd(r))
761 }
762
763 bbr2_bound_cwnd_for_probe_rtt(r);
764 bbr2_bound_cwnd_for_model(r);
765}
766
767fn bbr2_bound_cwnd_for_model(r: &mut Congestion) {
769 let mut cap = usize::MAX;
770
771 if bbr2_is_in_a_probe_bw_state(r) &&
772 r.bbr2_state.state != BBR2StateMachine::ProbeBWCRUISE
773 {
774 cap = r.bbr2_state.inflight_hi;
775 } else if r.bbr2_state.state == BBR2StateMachine::ProbeRTT ||
776 r.bbr2_state.state == BBR2StateMachine::ProbeBWCRUISE
777 {
778 cap = bbr2_inflight_with_headroom(r);
779 }
780
781 cap = cap.min(r.bbr2_state.inflight_lo);
783 cap = cap.max(bbr2_min_pipe_cwnd(r));
784
785 r.congestion_window = r.congestion_window.min(cap);
786}