1mod drain;
32mod mode;
33mod network_model;
34mod probe_bw;
35mod probe_rtt;
36mod startup;
37
38use std::time::Duration;
39use std::time::Instant;
40
41use network_model::BBRv2NetworkModel;
42
43use crate::recovery::gcongestion::Bandwidth;
44use crate::recovery::RecoveryStats;
45
46use self::mode::Mode;
47use self::mode::ModeImpl;
48
49use super::bbr::SendTimeState;
50use super::Acked;
51use super::BbrBwLoReductionStrategy;
52use super::BbrParams;
53use super::CongestionControl;
54use super::Lost;
55use super::RttStats;
56
57const MAX_MODE_CHANGES_PER_CONGESTION_EVENT: usize = 4;
58
59#[derive(Debug)]
60struct Params {
61 startup_cwnd_gain: f32,
64
65 startup_pacing_gain: f32,
66
67 full_bw_threshold: f32,
71
72 startup_full_bw_rounds: usize,
75
76 max_startup_queue_rounds: usize,
80
81 startup_full_loss_count: usize,
83
84 drain_cwnd_gain: f32,
86
87 drain_pacing_gain: f32,
88
89 probe_bw_probe_max_rounds: usize,
92
93 enable_reno_coexistence: bool,
94
95 probe_bw_probe_reno_gain: f32,
98
99 probe_bw_probe_base_duration: Duration,
101
102 probe_bw_full_loss_count: usize,
104
105 probe_bw_probe_up_pacing_gain: f32,
107 probe_bw_probe_down_pacing_gain: f32,
108 probe_bw_default_pacing_gain: f32,
109
110 probe_bw_cwnd_gain: f32,
112
113 probe_bw_up_cwnd_gain: f32,
115
116 probe_up_ignore_inflight_hi: bool,
118
119 max_probe_up_queue_rounds: usize,
124
125 probe_rtt_inflight_target_bdp_fraction: f32,
127
128 probe_rtt_period: Duration,
130
131 probe_rtt_duration: Duration,
132
133 probe_rtt_pacing_gain: f32,
134 probe_rtt_cwnd_gain: f32,
135
136 initial_max_ack_height_filter_window: usize,
139
140 inflight_hi_headroom: f32,
143
144 loss_threshold: f32,
146
147 beta: f32,
150
151 add_ack_height_to_queueing_threshold: bool,
153
154 avoid_unnecessary_probe_rtt: bool,
156
157 limit_inflight_hi_by_max_delivered: bool,
160
161 startup_loss_exit_use_max_delivered_for_inflight_hi: bool,
162
163 use_bytes_delivered_for_inflight_hi: bool,
165
166 decrease_startup_pacing_at_end_of_round: bool,
169
170 enable_overestimate_avoidance: bool,
172
173 choose_a0_point_fix: bool,
177
178 bw_lo_mode: BwLoMode,
179
180 ignore_app_limited_for_no_bandwidth_growth: bool,
183
184 initial_pacing_rate_bytes_per_second: Option<u64>,
189
190 scale_pacing_rate_by_mss: bool,
192
193 disable_probe_down_early_exit: bool,
196}
197
198impl Params {
199 fn with_overrides(mut self, custom_bbr_settings: &BbrParams) -> Self {
200 macro_rules! apply_override {
201 ($field:ident) => {
202 if let Some(custom_value) = custom_bbr_settings.$field {
203 self.$field = custom_value;
204 }
205 };
206 }
207
208 macro_rules! apply_optional_override {
209 ($field:ident) => {
210 if let Some(custom_value) = custom_bbr_settings.$field {
211 self.$field = Some(custom_value);
212 }
213 };
214 }
215
216 apply_override!(startup_cwnd_gain);
217 apply_override!(startup_pacing_gain);
218 apply_override!(full_bw_threshold);
219 apply_override!(startup_full_bw_rounds);
220 apply_override!(startup_full_loss_count);
221 apply_override!(drain_cwnd_gain);
222 apply_override!(drain_pacing_gain);
223 apply_override!(enable_reno_coexistence);
224 apply_override!(enable_overestimate_avoidance);
225 apply_override!(choose_a0_point_fix);
226 apply_override!(probe_bw_probe_up_pacing_gain);
227 apply_override!(probe_bw_probe_down_pacing_gain);
228 apply_override!(probe_bw_cwnd_gain);
229 apply_override!(probe_bw_up_cwnd_gain);
230 apply_override!(probe_rtt_pacing_gain);
231 apply_override!(probe_rtt_cwnd_gain);
232 apply_override!(max_probe_up_queue_rounds);
233 apply_override!(loss_threshold);
234 apply_override!(use_bytes_delivered_for_inflight_hi);
235 apply_override!(decrease_startup_pacing_at_end_of_round);
236 apply_override!(ignore_app_limited_for_no_bandwidth_growth);
237 apply_override!(scale_pacing_rate_by_mss);
238 apply_override!(disable_probe_down_early_exit);
239 apply_optional_override!(initial_pacing_rate_bytes_per_second);
240
241 if let Some(custom_value) = custom_bbr_settings.bw_lo_reduction_strategy {
242 self.bw_lo_mode = custom_value.into();
243 }
244
245 self
246 }
247}
248
249const DEFAULT_PARAMS: Params = Params {
250 startup_cwnd_gain: 2.0,
251
252 startup_pacing_gain: 2.773,
253
254 full_bw_threshold: 1.25,
255
256 startup_full_bw_rounds: 3,
257
258 max_startup_queue_rounds: 0,
259
260 startup_full_loss_count: 8,
261
262 drain_cwnd_gain: 2.0,
263
264 drain_pacing_gain: 1.0 / 2.885,
265
266 probe_bw_probe_max_rounds: 63,
267
268 enable_reno_coexistence: true,
269
270 probe_bw_probe_reno_gain: 1.0,
271
272 probe_bw_probe_base_duration: Duration::from_millis(2000),
273
274 probe_bw_full_loss_count: 2,
275
276 probe_bw_probe_up_pacing_gain: 1.25,
277
278 probe_bw_probe_down_pacing_gain: 0.9, probe_bw_default_pacing_gain: 1.0,
281
282 probe_bw_cwnd_gain: 2.25, probe_bw_up_cwnd_gain: 2.25, probe_up_ignore_inflight_hi: false,
287
288 max_probe_up_queue_rounds: 2,
289
290 probe_rtt_inflight_target_bdp_fraction: 0.5,
291
292 probe_rtt_period: Duration::from_millis(10000),
293
294 probe_rtt_duration: Duration::from_millis(200),
295
296 probe_rtt_pacing_gain: 1.0,
297
298 probe_rtt_cwnd_gain: 1.0,
299
300 initial_max_ack_height_filter_window: 10,
301
302 inflight_hi_headroom: 0.15,
303
304 loss_threshold: 0.015,
305
306 beta: 0.3,
307
308 add_ack_height_to_queueing_threshold: false,
309
310 avoid_unnecessary_probe_rtt: true,
311
312 limit_inflight_hi_by_max_delivered: true,
313
314 startup_loss_exit_use_max_delivered_for_inflight_hi: true,
315
316 use_bytes_delivered_for_inflight_hi: true,
317
318 decrease_startup_pacing_at_end_of_round: true,
319
320 enable_overestimate_avoidance: true,
321
322 choose_a0_point_fix: false,
323
324 bw_lo_mode: BwLoMode::InflightReduction,
325
326 ignore_app_limited_for_no_bandwidth_growth: false,
327
328 initial_pacing_rate_bytes_per_second: None,
329
330 scale_pacing_rate_by_mss: false,
331
332 disable_probe_down_early_exit: false,
333};
334
335#[derive(Debug, PartialEq)]
336enum BwLoMode {
337 Default,
338 MinRttReduction,
339 InflightReduction,
340 CwndReduction,
341}
342
343impl From<BbrBwLoReductionStrategy> for BwLoMode {
344 fn from(value: BbrBwLoReductionStrategy) -> Self {
345 match value {
346 BbrBwLoReductionStrategy::Default => BwLoMode::Default,
347 BbrBwLoReductionStrategy::MinRttReduction =>
348 BwLoMode::MinRttReduction,
349 BbrBwLoReductionStrategy::InflightReduction =>
350 BwLoMode::InflightReduction,
351 BbrBwLoReductionStrategy::CwndReduction => BwLoMode::CwndReduction,
352 }
353 }
354}
355
356#[derive(Debug)]
357struct Limits<T: Ord> {
358 lo: T,
359 hi: T,
360}
361
362impl<T: Ord + Clone + Copy> Limits<T> {
363 fn min(&self) -> T {
364 self.lo
365 }
366
367 fn apply_limits(&self, val: T) -> T {
368 val.max(self.lo).min(self.hi)
369 }
370}
371
372impl<T: Ord + Clone + Copy + From<u8>> Limits<T> {
373 pub(crate) fn no_greater_than(val: T) -> Self {
374 Self {
375 lo: T::from(0),
376 hi: val,
377 }
378 }
379}
380
381fn initial_pacing_rate(
382 cwnd_in_bytes: usize, smoothed_rtt: Duration, params: &Params,
383) -> Bandwidth {
384 if let Some(pacing_rate) = params.initial_pacing_rate_bytes_per_second {
385 return Bandwidth::from_bytes_per_second(pacing_rate);
386 }
387
388 Bandwidth::from_bytes_and_time_delta(cwnd_in_bytes, smoothed_rtt) * 2.885
389}
390
391#[derive(Debug)]
392pub(crate) struct BBRv2 {
393 mode: Mode,
394 cwnd: usize,
395 mss: usize,
396
397 pacing_rate: Bandwidth,
398
399 cwnd_limits: Limits<usize>,
400
401 initial_cwnd: usize,
402
403 last_sample_is_app_limited: bool,
404 has_non_app_limited_sample: bool,
405 last_quiescence_start: Option<Instant>,
406 params: Params,
407}
408
409struct BBRv2CongestionEvent {
410 event_time: Instant,
411
412 prior_cwnd: usize,
414 prior_bytes_in_flight: usize,
416
417 bytes_in_flight: usize,
419 bytes_acked: usize,
421 bytes_lost: usize,
423
424 end_of_round_trip: bool,
426 is_probing_for_bandwidth: bool,
428
429 sample_max_bandwidth: Option<Bandwidth>,
433
434 sample_min_rtt: Option<Duration>,
437
438 last_packet_send_state: SendTimeState,
442}
443
444impl BBRv2CongestionEvent {
445 fn new(
446 event_time: Instant, prior_cwnd: usize, prior_bytes_in_flight: usize,
447 is_probing_for_bandwidth: bool,
448 ) -> Self {
449 BBRv2CongestionEvent {
450 event_time,
451 prior_cwnd,
452 prior_bytes_in_flight,
453 is_probing_for_bandwidth,
454 bytes_in_flight: 0,
455 bytes_acked: 0,
456 bytes_lost: 0,
457 end_of_round_trip: false,
458 last_packet_send_state: Default::default(),
459 sample_max_bandwidth: None,
460 sample_min_rtt: None,
461 }
462 }
463}
464
465impl BBRv2 {
466 pub fn new(
467 initial_congestion_window: usize, max_congestion_window: usize,
468 max_segment_size: usize, smoothed_rtt: Duration,
469 custom_bbr_params: Option<&BbrParams>,
470 ) -> Self {
471 let cwnd = initial_congestion_window * max_segment_size;
472 let params = if let Some(custom_bbr_settings) = custom_bbr_params {
473 DEFAULT_PARAMS.with_overrides(custom_bbr_settings)
474 } else {
475 DEFAULT_PARAMS
476 };
477
478 BBRv2 {
479 mode: Mode::startup(BBRv2NetworkModel::new(¶ms, smoothed_rtt)),
480 cwnd,
481 pacing_rate: initial_pacing_rate(cwnd, smoothed_rtt, ¶ms),
482 cwnd_limits: Limits {
483 lo: initial_congestion_window * max_segment_size,
484 hi: max_congestion_window * max_segment_size,
485 },
486 initial_cwnd: initial_congestion_window * max_segment_size,
487 last_sample_is_app_limited: false,
488 has_non_app_limited_sample: false,
489 last_quiescence_start: None,
490 mss: max_segment_size,
491 params,
492 }
493 }
494
495 fn on_exit_quiescence(&mut self, now: Instant) {
496 if let Some(last_quiescence_start) = self.last_quiescence_start.take() {
497 self.mode.do_on_exit_quiescence(
498 now,
499 last_quiescence_start,
500 &self.params,
501 )
502 }
503 }
504
505 fn get_target_congestion_window(&self, gain: f32) -> usize {
506 let network_model = self.mode.network_model();
507 network_model
508 .bdp(network_model.bandwidth_estimate(), gain)
509 .max(self.cwnd_limits.min())
510 }
511
512 fn update_pacing_rate(&mut self, bytes_acked: usize) {
513 let network_model = self.mode.network_model();
514 let bandwidth_estimate = match network_model.bandwidth_estimate() {
515 e if e == Bandwidth::zero() => return,
516 e => e,
517 };
518
519 if network_model.total_bytes_acked() == bytes_acked {
520 self.pacing_rate = Bandwidth::from_bytes_and_time_delta(
522 self.cwnd,
523 network_model.min_rtt(),
524 );
525
526 if let Some(pacing_rate) =
527 self.params.initial_pacing_rate_bytes_per_second
528 {
529 let initial_pacing_rate =
533 Bandwidth::from_bytes_per_second(pacing_rate);
534 self.pacing_rate = self.pacing_rate.min(initial_pacing_rate);
535 }
536
537 return;
538 }
539
540 let target_rate = bandwidth_estimate * network_model.pacing_gain();
541 if network_model.full_bandwidth_reached() {
542 self.pacing_rate = target_rate;
543 return;
544 }
545
546 if self.params.decrease_startup_pacing_at_end_of_round &&
547 network_model.pacing_gain() < self.params.startup_pacing_gain
548 {
549 self.pacing_rate = target_rate;
550 return;
551 }
552
553 if self.params.bw_lo_mode != BwLoMode::Default &&
554 network_model.loss_events_in_round() > 0
555 {
556 self.pacing_rate = target_rate;
557 return;
558 }
559
560 self.pacing_rate = self.pacing_rate.max(target_rate);
562 }
563
564 fn update_congestion_window(&mut self, bytes_acked: usize) {
565 let network_model = self.mode.network_model();
566 let mut target_cwnd =
567 self.get_target_congestion_window(network_model.cwnd_gain());
568
569 let prior_cwnd = self.cwnd;
570 if network_model.full_bandwidth_reached() {
571 target_cwnd += network_model.max_ack_height();
572 self.cwnd = target_cwnd.min(prior_cwnd + bytes_acked);
573 } else if prior_cwnd < target_cwnd || prior_cwnd < 2 * self.initial_cwnd {
574 self.cwnd = prior_cwnd + bytes_acked;
575 }
576
577 self.cwnd = self
578 .mode
579 .get_cwnd_limits(&self.params)
580 .apply_limits(self.cwnd);
581 self.cwnd = self.cwnd_limits.apply_limits(self.cwnd);
582 }
583
584 fn on_enter_quiescence(&mut self, time: Instant) {
585 self.last_quiescence_start = Some(time);
586 }
587
588 fn target_bytes_inflight(&self) -> usize {
589 let network_model = &self.mode.network_model();
590 let bdp = network_model.bdp1(network_model.bandwidth_estimate());
591 bdp.min(self.get_congestion_window())
592 }
593}
594
595impl CongestionControl for BBRv2 {
596 #[cfg(feature = "qlog")]
597 fn state_str(&self) -> &'static str {
598 self.mode.state_str()
599 }
600
601 fn get_congestion_window(&self) -> usize {
602 self.cwnd
603 }
604
605 fn get_congestion_window_in_packets(&self) -> usize {
606 self.cwnd / self.mss
607 }
608
609 fn can_send(&self, bytes_in_flight: usize) -> bool {
610 bytes_in_flight < self.get_congestion_window()
611 }
612
613 fn on_packet_sent(
614 &mut self, sent_time: Instant, bytes_in_flight: usize,
615 packet_number: u64, bytes: usize, is_retransmissible: bool,
616 rtt_stats: &RttStats,
617 ) {
618 if bytes_in_flight == 0 && self.params.avoid_unnecessary_probe_rtt {
619 self.on_exit_quiescence(sent_time);
620 }
621
622 let network_model = self.mode.network_model_mut();
623 network_model.on_packet_sent(
624 sent_time,
625 bytes_in_flight,
626 packet_number,
627 bytes,
628 is_retransmissible,
629 rtt_stats,
630 );
631 }
632
633 fn on_congestion_event(
634 &mut self, _rtt_updated: bool, prior_in_flight: usize,
635 _bytes_in_flight: usize, event_time: Instant, acked_packets: &[Acked],
636 lost_packets: &[Lost], least_unacked: u64, _rtt_stats: &RttStats,
637 recovery_stats: &mut RecoveryStats,
638 ) {
639 let mut congestion_event = BBRv2CongestionEvent::new(
640 event_time,
641 self.cwnd,
642 prior_in_flight,
643 self.mode.is_probing_for_bandwidth(),
644 );
645
646 let network_model = self.mode.network_model_mut();
647 network_model.on_congestion_event_start(
648 acked_packets,
649 lost_packets,
650 &mut congestion_event,
651 &self.params,
652 );
653
654 let mut mode_changes_allowed = MAX_MODE_CHANGES_PER_CONGESTION_EVENT;
656 while mode_changes_allowed > 0 &&
657 self.mode.do_on_congestion_event(
658 prior_in_flight,
659 event_time,
660 acked_packets,
661 lost_packets,
662 &mut congestion_event,
663 self.target_bytes_inflight(),
664 &self.params,
665 recovery_stats,
666 self.get_congestion_window(),
667 )
668 {
669 mode_changes_allowed -= 1;
670 }
671
672 self.update_pacing_rate(congestion_event.bytes_acked);
673
674 self.update_congestion_window(congestion_event.bytes_acked);
675
676 let network_model = self.mode.network_model_mut();
677 network_model
678 .on_congestion_event_finish(least_unacked, &congestion_event);
679 self.last_sample_is_app_limited =
680 congestion_event.last_packet_send_state.is_app_limited;
681 if !self.last_sample_is_app_limited {
682 self.has_non_app_limited_sample = true;
683 }
684 if congestion_event.bytes_in_flight == 0 &&
685 self.params.avoid_unnecessary_probe_rtt
686 {
687 self.on_enter_quiescence(event_time);
688 }
689 }
690
691 fn on_packet_neutered(&mut self, packet_number: u64) {
692 let network_model = self.mode.network_model_mut();
693 network_model.on_packet_neutered(packet_number);
694 }
695
696 fn on_retransmission_timeout(&mut self, _packets_retransmitted: bool) {}
697
698 fn on_connection_migration(&mut self) {}
699
700 fn is_in_recovery(&self) -> bool {
701 self.last_quiescence_start.is_none()
703 }
704
705 fn is_cwnd_limited(&self, bytes_in_flight: usize) -> bool {
706 bytes_in_flight >= self.get_congestion_window()
707 }
708
709 fn pacing_rate(
710 &self, _bytes_in_flight: usize, _rtt_stats: &RttStats,
711 ) -> Bandwidth {
712 self.pacing_rate
713 }
714
715 fn bandwidth_estimate(&self, _rtt_stats: &RttStats) -> Bandwidth {
716 let network_model = self.mode.network_model();
717 network_model.bandwidth_estimate()
718 }
719
720 fn max_bandwidth(&self) -> Bandwidth {
721 self.mode.network_model().max_bandwidth()
722 }
723
724 fn update_mss(&mut self, new_mss: usize) {
725 self.cwnd_limits.hi = (self.cwnd_limits.hi as u64 * new_mss as u64 /
726 self.mss as u64) as usize;
727 self.cwnd_limits.lo = (self.cwnd_limits.lo as u64 * new_mss as u64 /
728 self.mss as u64) as usize;
729 self.cwnd =
730 (self.cwnd as u64 * new_mss as u64 / self.mss as u64) as usize;
731 self.initial_cwnd = (self.initial_cwnd as u64 * new_mss as u64 /
732 self.mss as u64) as usize;
733 if self.params.scale_pacing_rate_by_mss {
734 self.pacing_rate =
735 self.pacing_rate * (new_mss as f64 / self.mss as f64);
736 }
737 self.mss = new_mss;
738 }
739
740 fn on_app_limited(&mut self, bytes_in_flight: usize) {
741 if bytes_in_flight >= self.get_congestion_window() {
742 return;
743 }
744
745 let network_model = self.mode.network_model_mut();
746 network_model.on_app_limited()
747 }
748
749 fn limit_cwnd(&mut self, max_cwnd: usize) {
750 self.cwnd_limits.hi = max_cwnd
751 }
752}
753
754#[cfg(test)]
755mod tests {
756 use rstest::rstest;
757
758 use super::*;
759
760 #[rstest]
761 fn update_mss(#[values(false, true)] scale_pacing_rate_by_mss: bool) {
762 const INIT_PACKET_SIZE: usize = 1200;
763 const INIT_WINDOW_PACKETS: usize = 10;
764 const MAX_WINDOW_PACKETS: usize = 10000;
765 const INIT_CWND: usize = INIT_WINDOW_PACKETS * INIT_PACKET_SIZE;
766 const MAX_CWND: usize = MAX_WINDOW_PACKETS * INIT_PACKET_SIZE;
767 let initial_rtt = Duration::from_millis(333);
768 let bbr_params = &BbrParams {
769 scale_pacing_rate_by_mss: Some(scale_pacing_rate_by_mss),
770 ..Default::default()
771 };
772
773 const NEW_PACKET_SIZE: usize = 1450;
774 const NEW_CWND: usize = INIT_WINDOW_PACKETS * NEW_PACKET_SIZE;
775 const NEW_MAX_CWND: usize = MAX_WINDOW_PACKETS * NEW_PACKET_SIZE;
776
777 let mut bbr2 = BBRv2::new(
778 INIT_WINDOW_PACKETS,
779 MAX_WINDOW_PACKETS,
780 INIT_PACKET_SIZE,
781 initial_rtt,
782 Some(bbr_params),
783 );
784
785 assert_eq!(bbr2.cwnd_limits.lo, INIT_CWND);
786 assert_eq!(bbr2.cwnd_limits.hi, MAX_CWND);
787 assert_eq!(bbr2.cwnd, INIT_CWND);
788 assert_eq!(
789 bbr2.pacing_rate.to_bytes_per_period(initial_rtt),
790 (2.88499 * INIT_CWND as f64) as u64
791 );
792
793 bbr2.update_mss(NEW_PACKET_SIZE);
794
795 assert_eq!(bbr2.cwnd_limits.lo, NEW_CWND);
796 assert_eq!(bbr2.cwnd_limits.hi, NEW_MAX_CWND);
797 assert_eq!(bbr2.cwnd, NEW_CWND);
798 let pacing_cwnd = if scale_pacing_rate_by_mss {
799 NEW_CWND
800 } else {
801 INIT_CWND
802 };
803 assert_eq!(
804 bbr2.pacing_rate.to_bytes_per_period(initial_rtt),
805 (2.88499 * pacing_cwnd as f64) as u64
806 );
807 }
808}