1mod drain;
32mod mode;
33mod network_model;
34mod probe_bw;
35mod probe_rtt;
36mod rtt_jump_detector;
37mod startup;
38
39use std::time::Duration;
40use std::time::Instant;
41
42use network_model::BBRv2NetworkModel;
43
44use crate::recovery::gcongestion::Bandwidth;
45use crate::recovery::RecoveryStats;
46
47use self::mode::Mode;
48use self::mode::ModeImpl;
49
50use super::bbr::SendTimeState;
51use super::Acked;
52use super::BbrBwLoReductionStrategy;
53use super::BbrParams;
54use super::BbrRttJumpDetector;
55use super::CongestionControl;
56use super::Lost;
57use super::RttStats;
58
59const MAX_MODE_CHANGES_PER_CONGESTION_EVENT: usize = 4;
60
61#[derive(Debug)]
62struct Params {
63 startup_cwnd_gain: f32,
66
67 startup_pacing_gain: f32,
68
69 full_bw_threshold: f32,
73
74 startup_full_bw_rounds: usize,
77
78 max_startup_queue_rounds: usize,
82
83 startup_full_loss_count: usize,
85
86 drain_cwnd_gain: f32,
88
89 drain_pacing_gain: f32,
90
91 probe_bw_probe_max_rounds: usize,
94
95 enable_reno_coexistence: bool,
96
97 probe_bw_probe_reno_gain: f32,
100
101 probe_bw_probe_base_duration: Duration,
103
104 probe_bw_full_loss_count: usize,
106
107 probe_bw_probe_up_pacing_gain: f32,
109 probe_bw_probe_down_pacing_gain: f32,
110 probe_bw_default_pacing_gain: f32,
111
112 probe_bw_cwnd_gain: f32,
114
115 probe_bw_up_cwnd_gain: f32,
117
118 probe_up_ignore_inflight_hi: bool,
120
121 max_probe_up_queue_rounds: usize,
126
127 probe_rtt_inflight_target_bdp_fraction: f32,
129
130 probe_rtt_period: Duration,
132
133 probe_rtt_duration: Duration,
134
135 probe_rtt_pacing_gain: f32,
136 probe_rtt_cwnd_gain: f32,
137
138 initial_max_ack_height_filter_window: usize,
141
142 inflight_hi_headroom: f32,
145
146 loss_threshold: f32,
148
149 beta: f32,
152
153 add_ack_height_to_queueing_threshold: bool,
155
156 avoid_unnecessary_probe_rtt: bool,
158
159 limit_inflight_hi_by_max_delivered: bool,
162
163 startup_loss_exit_use_max_delivered_for_inflight_hi: bool,
164
165 use_bytes_delivered_for_inflight_hi: bool,
167
168 decrease_startup_pacing_at_end_of_round: bool,
171
172 enable_overestimate_avoidance: bool,
176
177 choose_a0_point_fix: bool,
181
182 bw_lo_mode: BwLoMode,
184
185 ignore_app_limited_for_no_bandwidth_growth: bool,
188
189 initial_pacing_rate_bytes_per_second: Option<u64>,
194
195 scale_pacing_rate_by_mss: bool,
197
198 disable_probe_down_early_exit: bool,
201
202 time_sent_set_to_now: bool,
212
213 rtt_jump_detector: BbrRttJumpDetector,
215}
216
217impl Params {
218 fn with_overrides(mut self, custom_bbr_settings: &BbrParams) -> Self {
219 macro_rules! apply_override {
220 ($field:ident) => {
221 if let Some(custom_value) = custom_bbr_settings.$field {
222 self.$field = custom_value;
223 }
224 };
225 }
226
227 macro_rules! apply_optional_override {
228 ($field:ident) => {
229 if let Some(custom_value) = custom_bbr_settings.$field {
230 self.$field = Some(custom_value);
231 }
232 };
233 }
234
235 apply_override!(startup_cwnd_gain);
236 apply_override!(startup_pacing_gain);
237 apply_override!(full_bw_threshold);
238 apply_override!(startup_full_bw_rounds);
239 apply_override!(startup_full_loss_count);
240 apply_override!(drain_cwnd_gain);
241 apply_override!(drain_pacing_gain);
242 apply_override!(enable_reno_coexistence);
243 apply_override!(enable_overestimate_avoidance);
244 apply_override!(choose_a0_point_fix);
245 apply_override!(probe_bw_probe_up_pacing_gain);
246 apply_override!(probe_bw_probe_down_pacing_gain);
247 apply_override!(probe_bw_cwnd_gain);
248 apply_override!(probe_bw_up_cwnd_gain);
249 apply_override!(probe_rtt_pacing_gain);
250 apply_override!(probe_rtt_cwnd_gain);
251 apply_override!(max_probe_up_queue_rounds);
252 apply_override!(loss_threshold);
253 apply_override!(use_bytes_delivered_for_inflight_hi);
254 apply_override!(decrease_startup_pacing_at_end_of_round);
255 apply_override!(ignore_app_limited_for_no_bandwidth_growth);
256 apply_override!(scale_pacing_rate_by_mss);
257 apply_override!(disable_probe_down_early_exit);
258 apply_override!(time_sent_set_to_now);
259 apply_optional_override!(initial_pacing_rate_bytes_per_second);
260
261 #[cfg(feature = "internal")]
262 {
263 if let Some(custom_value) = custom_bbr_settings.rtt_jump_detector {
264 self.rtt_jump_detector = custom_value;
265 }
266 }
267
268 if let Some(custom_value) = custom_bbr_settings.bw_lo_reduction_strategy {
269 self.bw_lo_mode = custom_value.into();
270 }
271
272 self
273 }
274}
275
276const DEFAULT_PARAMS: Params = Params {
277 startup_cwnd_gain: 2.0,
278
279 startup_pacing_gain: 2.773,
280
281 full_bw_threshold: 1.25,
282
283 startup_full_bw_rounds: 3,
284
285 max_startup_queue_rounds: 0,
286
287 startup_full_loss_count: 8,
288
289 drain_cwnd_gain: 2.0,
290
291 drain_pacing_gain: 1.0 / 2.885,
292
293 probe_bw_probe_max_rounds: 63,
294
295 enable_reno_coexistence: true,
296
297 probe_bw_probe_reno_gain: 1.0,
298
299 probe_bw_probe_base_duration: Duration::from_millis(2000),
300
301 probe_bw_full_loss_count: 2,
302
303 probe_bw_probe_up_pacing_gain: 1.25,
304
305 probe_bw_probe_down_pacing_gain: 0.9, probe_bw_default_pacing_gain: 1.0,
308
309 probe_bw_cwnd_gain: 2.0, probe_bw_up_cwnd_gain: 2.25, probe_up_ignore_inflight_hi: false,
314
315 max_probe_up_queue_rounds: 2,
316
317 probe_rtt_inflight_target_bdp_fraction: 0.5,
318
319 probe_rtt_period: Duration::from_millis(10000),
320
321 probe_rtt_duration: Duration::from_millis(200),
322
323 probe_rtt_pacing_gain: 1.0,
324
325 probe_rtt_cwnd_gain: 1.0,
326
327 initial_max_ack_height_filter_window: 10,
328
329 inflight_hi_headroom: 0.15,
330
331 loss_threshold: 0.015,
332
333 beta: 0.3,
334
335 add_ack_height_to_queueing_threshold: false,
336
337 avoid_unnecessary_probe_rtt: true,
338
339 limit_inflight_hi_by_max_delivered: true,
340
341 startup_loss_exit_use_max_delivered_for_inflight_hi: true,
342
343 use_bytes_delivered_for_inflight_hi: true,
344
345 decrease_startup_pacing_at_end_of_round: true,
346
347 enable_overestimate_avoidance: false,
348
349 choose_a0_point_fix: false,
350
351 bw_lo_mode: BwLoMode::Default,
352
353 ignore_app_limited_for_no_bandwidth_growth: true,
354
355 initial_pacing_rate_bytes_per_second: None,
356
357 scale_pacing_rate_by_mss: false,
358
359 disable_probe_down_early_exit: false,
360
361 time_sent_set_to_now: true,
362
363 rtt_jump_detector: BbrRttJumpDetector::Disabled,
364};
365
366#[derive(Debug, PartialEq)]
367enum BwLoMode {
368 Default,
371
372 MinRttReduction,
377
378 InflightReduction,
383
384 CwndReduction,
389}
390
391impl From<BbrBwLoReductionStrategy> for BwLoMode {
392 fn from(value: BbrBwLoReductionStrategy) -> Self {
393 match value {
394 BbrBwLoReductionStrategy::Default => BwLoMode::Default,
395 BbrBwLoReductionStrategy::MinRttReduction =>
396 BwLoMode::MinRttReduction,
397 BbrBwLoReductionStrategy::InflightReduction =>
398 BwLoMode::InflightReduction,
399 BbrBwLoReductionStrategy::CwndReduction => BwLoMode::CwndReduction,
400 }
401 }
402}
403
404#[derive(Debug)]
405struct Limits<T: Ord> {
406 lo: T,
407 hi: T,
408}
409
410impl<T: Ord + Clone + Copy> Limits<T> {
411 fn min(&self) -> T {
412 self.lo
413 }
414
415 fn apply_limits(&self, val: T) -> T {
416 val.max(self.lo).min(self.hi)
417 }
418}
419
420impl<T: Ord + Clone + Copy + From<u8>> Limits<T> {
421 pub(crate) fn no_greater_than(val: T) -> Self {
422 Self {
423 lo: T::from(0),
424 hi: val,
425 }
426 }
427}
428
429fn initial_pacing_rate(
430 cwnd_in_bytes: usize, smoothed_rtt: Duration, params: &Params,
431) -> Bandwidth {
432 if let Some(pacing_rate) = params.initial_pacing_rate_bytes_per_second {
433 return Bandwidth::from_bytes_per_second(pacing_rate);
434 }
435
436 Bandwidth::from_bytes_and_time_delta(cwnd_in_bytes, smoothed_rtt) * 2.885
437}
438
439#[derive(Debug)]
440pub(crate) struct BBRv2 {
441 mode: Mode,
442 cwnd: usize,
443 mss: usize,
444
445 pacing_rate: Bandwidth,
446
447 cwnd_limits: Limits<usize>,
448
449 initial_cwnd: usize,
450
451 last_sample_is_app_limited: bool,
452 has_non_app_limited_sample: bool,
453 last_quiescence_start: Option<Instant>,
454 params: Params,
455}
456
457struct BBRv2CongestionEvent {
458 event_time: Instant,
459
460 prior_cwnd: usize,
462 prior_bytes_in_flight: usize,
464
465 bytes_in_flight: usize,
467 bytes_acked: usize,
469 bytes_lost: usize,
471
472 end_of_round_trip: bool,
474 is_probing_for_bandwidth: bool,
476
477 sample_max_bandwidth: Option<Bandwidth>,
481
482 sample_min_rtt: Option<Duration>,
485
486 last_packet_send_state: SendTimeState,
490}
491
492impl BBRv2CongestionEvent {
493 fn new(
494 event_time: Instant, prior_cwnd: usize, prior_bytes_in_flight: usize,
495 is_probing_for_bandwidth: bool,
496 ) -> Self {
497 BBRv2CongestionEvent {
498 event_time,
499 prior_cwnd,
500 prior_bytes_in_flight,
501 is_probing_for_bandwidth,
502 bytes_in_flight: 0,
503 bytes_acked: 0,
504 bytes_lost: 0,
505 end_of_round_trip: false,
506 last_packet_send_state: Default::default(),
507 sample_max_bandwidth: None,
508 sample_min_rtt: None,
509 }
510 }
511}
512
513impl BBRv2 {
514 pub fn new(
515 initial_congestion_window: usize, max_congestion_window: usize,
516 max_segment_size: usize, smoothed_rtt: Duration,
517 custom_bbr_params: Option<&BbrParams>,
518 ) -> Self {
519 let cwnd = initial_congestion_window * max_segment_size;
520
521 let params = if let Some(custom_bbr_settings) = custom_bbr_params {
522 DEFAULT_PARAMS.with_overrides(custom_bbr_settings)
523 } else {
524 DEFAULT_PARAMS
525 };
526
527 BBRv2 {
528 mode: Mode::startup(BBRv2NetworkModel::new(¶ms, smoothed_rtt)),
529 cwnd,
530 pacing_rate: initial_pacing_rate(cwnd, smoothed_rtt, ¶ms),
531 cwnd_limits: Limits {
532 lo: initial_congestion_window * max_segment_size,
533 hi: max_congestion_window * max_segment_size,
534 },
535 initial_cwnd: initial_congestion_window * max_segment_size,
536 last_sample_is_app_limited: false,
537 has_non_app_limited_sample: false,
538 last_quiescence_start: None,
539 mss: max_segment_size,
540 params,
541 }
542 }
543
544 pub fn time_sent_set_to_now(&self) -> bool {
545 self.params.time_sent_set_to_now
546 }
547
548 fn on_exit_quiescence(&mut self, now: Instant) {
549 if let Some(last_quiescence_start) = self.last_quiescence_start.take() {
550 self.mode.do_on_exit_quiescence(
551 now,
552 last_quiescence_start,
553 &self.params,
554 )
555 }
556 }
557
558 fn get_target_congestion_window(&self, gain: f32) -> usize {
559 let network_model = self.mode.network_model();
560 network_model
561 .bdp(network_model.bandwidth_estimate(), gain)
562 .max(self.cwnd_limits.min())
563 }
564
565 fn update_pacing_rate(&mut self, bytes_acked: usize) {
566 let network_model = self.mode.network_model();
567 let bandwidth_estimate = match network_model.bandwidth_estimate() {
568 e if e == Bandwidth::zero() => return,
569 e => e,
570 };
571
572 if network_model.total_bytes_acked() == bytes_acked {
573 self.pacing_rate = Bandwidth::from_bytes_and_time_delta(
575 self.cwnd,
576 network_model.min_rtt(),
577 );
578
579 if let Some(pacing_rate) =
580 self.params.initial_pacing_rate_bytes_per_second
581 {
582 let initial_pacing_rate =
586 Bandwidth::from_bytes_per_second(pacing_rate);
587 self.pacing_rate = self.pacing_rate.min(initial_pacing_rate);
588 }
589
590 return;
591 }
592
593 let target_rate = bandwidth_estimate * network_model.pacing_gain();
594 if network_model.full_bandwidth_reached() {
595 self.pacing_rate = target_rate;
596 return;
597 }
598
599 if self.params.decrease_startup_pacing_at_end_of_round &&
600 network_model.pacing_gain() < self.params.startup_pacing_gain
601 {
602 self.pacing_rate = target_rate;
603 return;
604 }
605
606 if self.params.bw_lo_mode != BwLoMode::Default &&
607 network_model.loss_events_in_round() > 0
608 {
609 self.pacing_rate = target_rate;
610 return;
611 }
612
613 self.pacing_rate = self.pacing_rate.max(target_rate);
615 }
616
617 fn update_congestion_window(&mut self, bytes_acked: usize) {
618 let network_model = self.mode.network_model();
619 let mut target_cwnd =
620 self.get_target_congestion_window(network_model.cwnd_gain());
621
622 let prior_cwnd = self.cwnd;
623 if network_model.full_bandwidth_reached() {
624 target_cwnd += network_model.max_ack_height();
625 self.cwnd = target_cwnd.min(prior_cwnd + bytes_acked);
626 } else if prior_cwnd < target_cwnd || prior_cwnd < 2 * self.initial_cwnd {
627 self.cwnd = prior_cwnd + bytes_acked;
628 }
629
630 self.cwnd = self
631 .mode
632 .get_cwnd_limits(&self.params)
633 .apply_limits(self.cwnd);
634 self.cwnd = self.cwnd_limits.apply_limits(self.cwnd);
635 }
636
637 fn on_enter_quiescence(&mut self, time: Instant) {
638 self.last_quiescence_start = Some(time);
639 }
640
641 fn target_bytes_inflight(&self) -> usize {
642 let network_model = &self.mode.network_model();
643 let bdp = network_model.bdp1(network_model.bandwidth_estimate());
644 bdp.min(self.get_congestion_window())
645 }
646
647 #[cfg(feature = "qlog")]
648 pub(crate) fn send_rate(&self) -> Option<Bandwidth> {
649 self.mode.network_model().send_rate()
650 }
651
652 #[cfg(feature = "qlog")]
653 pub(crate) fn ack_rate(&self) -> Option<Bandwidth> {
654 self.mode.network_model().ack_rate()
655 }
656
657 pub(crate) fn rtt_persistent_jump_count(&self) -> u64 {
658 self.mode.network_model().rtt_persistent_jump_count()
659 }
660}
661
662impl CongestionControl for BBRv2 {
663 #[cfg(feature = "qlog")]
664 fn state_str(&self) -> &'static str {
665 self.mode.state_str()
666 }
667
668 fn get_congestion_window(&self) -> usize {
669 self.cwnd
670 }
671
672 fn get_congestion_window_in_packets(&self) -> usize {
673 self.cwnd / self.mss
674 }
675
676 fn can_send(&self, bytes_in_flight: usize) -> bool {
677 bytes_in_flight < self.get_congestion_window()
678 }
679
680 fn on_packet_sent(
681 &mut self, sent_time: Instant, bytes_in_flight: usize,
682 packet_number: u64, bytes: usize, is_retransmissible: bool,
683 ) {
684 if bytes_in_flight == 0 && self.params.avoid_unnecessary_probe_rtt {
685 self.on_exit_quiescence(sent_time);
686 }
687
688 let network_model = self.mode.network_model_mut();
689 network_model.on_packet_sent(
690 sent_time,
691 bytes_in_flight,
692 packet_number,
693 bytes,
694 is_retransmissible,
695 );
696 }
697
698 fn on_congestion_event(
699 &mut self, _rtt_updated: bool, prior_in_flight: usize,
700 _bytes_in_flight: usize, event_time: Instant, acked_packets: &[Acked],
701 lost_packets: &[Lost], least_unacked: u64, _rtt_stats: &RttStats,
702 recovery_stats: &mut RecoveryStats,
703 ) {
704 let mut congestion_event = BBRv2CongestionEvent::new(
705 event_time,
706 self.cwnd,
707 prior_in_flight,
708 self.mode.is_probing_for_bandwidth(),
709 );
710
711 let network_model = self.mode.network_model_mut();
712 network_model.on_congestion_event_start(
713 acked_packets,
714 lost_packets,
715 &mut congestion_event,
716 &self.params,
717 );
718
719 let mut mode_changes_allowed = MAX_MODE_CHANGES_PER_CONGESTION_EVENT;
721 while mode_changes_allowed > 0 &&
722 self.mode.do_on_congestion_event(
723 prior_in_flight,
724 event_time,
725 acked_packets,
726 lost_packets,
727 &mut congestion_event,
728 self.target_bytes_inflight(),
729 &self.params,
730 recovery_stats,
731 self.get_congestion_window(),
732 )
733 {
734 mode_changes_allowed -= 1;
735 }
736
737 self.update_pacing_rate(congestion_event.bytes_acked);
738
739 self.update_congestion_window(congestion_event.bytes_acked);
740
741 let network_model = self.mode.network_model_mut();
742 network_model
743 .on_congestion_event_finish(least_unacked, &congestion_event);
744 self.last_sample_is_app_limited =
745 congestion_event.last_packet_send_state.is_app_limited;
746 if !self.last_sample_is_app_limited {
747 self.has_non_app_limited_sample = true;
748 }
749 if congestion_event.bytes_in_flight == 0 &&
750 self.params.avoid_unnecessary_probe_rtt
751 {
752 self.on_enter_quiescence(event_time);
753 }
754 }
755
756 fn on_packet_neutered(&mut self, packet_number: u64) {
757 let network_model = self.mode.network_model_mut();
758 network_model.on_packet_neutered(packet_number);
759 }
760
761 fn on_retransmission_timeout(&mut self, _packets_retransmitted: bool) {}
762
763 fn on_connection_migration(&mut self) {}
764
765 fn is_in_recovery(&self) -> bool {
766 self.last_quiescence_start.is_none()
768 }
769
770 fn is_cwnd_limited(&self, bytes_in_flight: usize) -> bool {
771 bytes_in_flight >= self.get_congestion_window()
772 }
773
774 fn pacing_rate(
775 &self, _bytes_in_flight: usize, _rtt_stats: &RttStats,
776 ) -> Bandwidth {
777 self.pacing_rate
778 }
779
780 fn bandwidth_estimate(&self, _rtt_stats: &RttStats) -> Bandwidth {
781 let network_model = self.mode.network_model();
782 network_model.bandwidth_estimate()
783 }
784
785 fn max_bandwidth(&self) -> Bandwidth {
786 self.mode.network_model().max_bandwidth()
787 }
788
789 fn update_mss(&mut self, new_mss: usize) {
790 self.cwnd_limits.hi = (self.cwnd_limits.hi as u64 * new_mss as u64 /
791 self.mss as u64) as usize;
792 self.cwnd_limits.lo = (self.cwnd_limits.lo as u64 * new_mss as u64 /
793 self.mss as u64) as usize;
794 self.cwnd =
795 (self.cwnd as u64 * new_mss as u64 / self.mss as u64) as usize;
796 self.initial_cwnd = (self.initial_cwnd as u64 * new_mss as u64 /
797 self.mss as u64) as usize;
798 if self.params.scale_pacing_rate_by_mss {
799 self.pacing_rate =
800 self.pacing_rate * (new_mss as f64 / self.mss as f64);
801 }
802 self.mss = new_mss;
803 }
804
805 fn on_app_limited(&mut self, bytes_in_flight: usize) {
806 if bytes_in_flight >= self.get_congestion_window() {
807 return;
808 }
809
810 let network_model = self.mode.network_model_mut();
811 network_model.on_app_limited()
812 }
813
814 fn limit_cwnd(&mut self, max_cwnd: usize) {
815 self.cwnd_limits.hi = max_cwnd
816 }
817}
818
819#[cfg(test)]
820mod tests {
821 use rstest::rstest;
822
823 use super::*;
824
825 #[rstest]
826 fn update_mss(#[values(false, true)] scale_pacing_rate_by_mss: bool) {
827 const INIT_PACKET_SIZE: usize = 1200;
828 const INIT_WINDOW_PACKETS: usize = 10;
829 const MAX_WINDOW_PACKETS: usize = 10000;
830 const INIT_CWND: usize = INIT_WINDOW_PACKETS * INIT_PACKET_SIZE;
831 const MAX_CWND: usize = MAX_WINDOW_PACKETS * INIT_PACKET_SIZE;
832 let initial_rtt = Duration::from_millis(333);
833 let bbr_params = &BbrParams {
834 scale_pacing_rate_by_mss: Some(scale_pacing_rate_by_mss),
835 ..Default::default()
836 };
837
838 const NEW_PACKET_SIZE: usize = 1450;
839 const NEW_CWND: usize = INIT_WINDOW_PACKETS * NEW_PACKET_SIZE;
840 const NEW_MAX_CWND: usize = MAX_WINDOW_PACKETS * NEW_PACKET_SIZE;
841
842 let mut bbr2 = BBRv2::new(
843 INIT_WINDOW_PACKETS,
844 MAX_WINDOW_PACKETS,
845 INIT_PACKET_SIZE,
846 initial_rtt,
847 Some(bbr_params),
848 );
849
850 assert_eq!(bbr2.cwnd_limits.lo, INIT_CWND);
851 assert_eq!(bbr2.cwnd_limits.hi, MAX_CWND);
852 assert_eq!(bbr2.cwnd, INIT_CWND);
853 assert_eq!(
854 bbr2.pacing_rate.to_bytes_per_period(initial_rtt),
855 (2.88499 * INIT_CWND as f64) as u64
856 );
857
858 bbr2.update_mss(NEW_PACKET_SIZE);
859
860 assert_eq!(bbr2.cwnd_limits.lo, NEW_CWND);
861 assert_eq!(bbr2.cwnd_limits.hi, NEW_MAX_CWND);
862 assert_eq!(bbr2.cwnd, NEW_CWND);
863 let pacing_cwnd = if scale_pacing_rate_by_mss {
864 NEW_CWND
865 } else {
866 INIT_CWND
867 };
868 assert_eq!(
869 bbr2.pacing_rate.to_bytes_per_period(initial_rtt),
870 (2.88499 * pacing_cwnd as f64) as u64
871 );
872 }
873}