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,
174
175 choose_a0_point_fix: bool,
179
180 bw_lo_mode: BwLoMode,
182
183 ignore_app_limited_for_no_bandwidth_growth: bool,
186
187 initial_pacing_rate_bytes_per_second: Option<u64>,
192
193 scale_pacing_rate_by_mss: bool,
195
196 disable_probe_down_early_exit: bool,
199
200 time_sent_set_to_now: bool,
210}
211
212impl Params {
213 fn with_overrides(mut self, custom_bbr_settings: &BbrParams) -> Self {
214 macro_rules! apply_override {
215 ($field:ident) => {
216 if let Some(custom_value) = custom_bbr_settings.$field {
217 self.$field = custom_value;
218 }
219 };
220 }
221
222 macro_rules! apply_optional_override {
223 ($field:ident) => {
224 if let Some(custom_value) = custom_bbr_settings.$field {
225 self.$field = Some(custom_value);
226 }
227 };
228 }
229
230 apply_override!(startup_cwnd_gain);
231 apply_override!(startup_pacing_gain);
232 apply_override!(full_bw_threshold);
233 apply_override!(startup_full_bw_rounds);
234 apply_override!(startup_full_loss_count);
235 apply_override!(drain_cwnd_gain);
236 apply_override!(drain_pacing_gain);
237 apply_override!(enable_reno_coexistence);
238 apply_override!(enable_overestimate_avoidance);
239 apply_override!(choose_a0_point_fix);
240 apply_override!(probe_bw_probe_up_pacing_gain);
241 apply_override!(probe_bw_probe_down_pacing_gain);
242 apply_override!(probe_bw_cwnd_gain);
243 apply_override!(probe_bw_up_cwnd_gain);
244 apply_override!(probe_rtt_pacing_gain);
245 apply_override!(probe_rtt_cwnd_gain);
246 apply_override!(max_probe_up_queue_rounds);
247 apply_override!(loss_threshold);
248 apply_override!(use_bytes_delivered_for_inflight_hi);
249 apply_override!(decrease_startup_pacing_at_end_of_round);
250 apply_override!(ignore_app_limited_for_no_bandwidth_growth);
251 apply_override!(scale_pacing_rate_by_mss);
252 apply_override!(disable_probe_down_early_exit);
253 apply_override!(time_sent_set_to_now);
254 apply_optional_override!(initial_pacing_rate_bytes_per_second);
255
256 if let Some(custom_value) = custom_bbr_settings.bw_lo_reduction_strategy {
257 self.bw_lo_mode = custom_value.into();
258 }
259
260 self
261 }
262}
263
264const DEFAULT_PARAMS: Params = Params {
265 startup_cwnd_gain: 2.0,
266
267 startup_pacing_gain: 2.773,
268
269 full_bw_threshold: 1.25,
270
271 startup_full_bw_rounds: 3,
272
273 max_startup_queue_rounds: 0,
274
275 startup_full_loss_count: 8,
276
277 drain_cwnd_gain: 2.0,
278
279 drain_pacing_gain: 1.0 / 2.885,
280
281 probe_bw_probe_max_rounds: 63,
282
283 enable_reno_coexistence: true,
284
285 probe_bw_probe_reno_gain: 1.0,
286
287 probe_bw_probe_base_duration: Duration::from_millis(2000),
288
289 probe_bw_full_loss_count: 2,
290
291 probe_bw_probe_up_pacing_gain: 1.25,
292
293 probe_bw_probe_down_pacing_gain: 0.9, probe_bw_default_pacing_gain: 1.0,
296
297 probe_bw_cwnd_gain: 2.0, probe_bw_up_cwnd_gain: 2.25, probe_up_ignore_inflight_hi: false,
302
303 max_probe_up_queue_rounds: 2,
304
305 probe_rtt_inflight_target_bdp_fraction: 0.5,
306
307 probe_rtt_period: Duration::from_millis(10000),
308
309 probe_rtt_duration: Duration::from_millis(200),
310
311 probe_rtt_pacing_gain: 1.0,
312
313 probe_rtt_cwnd_gain: 1.0,
314
315 initial_max_ack_height_filter_window: 10,
316
317 inflight_hi_headroom: 0.15,
318
319 loss_threshold: 0.015,
320
321 beta: 0.3,
322
323 add_ack_height_to_queueing_threshold: false,
324
325 avoid_unnecessary_probe_rtt: true,
326
327 limit_inflight_hi_by_max_delivered: true,
328
329 startup_loss_exit_use_max_delivered_for_inflight_hi: true,
330
331 use_bytes_delivered_for_inflight_hi: true,
332
333 decrease_startup_pacing_at_end_of_round: true,
334
335 enable_overestimate_avoidance: false,
336
337 choose_a0_point_fix: false,
338
339 bw_lo_mode: BwLoMode::Default,
340
341 ignore_app_limited_for_no_bandwidth_growth: true,
342
343 initial_pacing_rate_bytes_per_second: None,
344
345 scale_pacing_rate_by_mss: false,
346
347 disable_probe_down_early_exit: false,
348
349 time_sent_set_to_now: true,
350};
351
352#[derive(Debug, PartialEq)]
353enum BwLoMode {
354 Default,
357
358 MinRttReduction,
363
364 InflightReduction,
369
370 CwndReduction,
375}
376
377impl From<BbrBwLoReductionStrategy> for BwLoMode {
378 fn from(value: BbrBwLoReductionStrategy) -> Self {
379 match value {
380 BbrBwLoReductionStrategy::Default => BwLoMode::Default,
381 BbrBwLoReductionStrategy::MinRttReduction =>
382 BwLoMode::MinRttReduction,
383 BbrBwLoReductionStrategy::InflightReduction =>
384 BwLoMode::InflightReduction,
385 BbrBwLoReductionStrategy::CwndReduction => BwLoMode::CwndReduction,
386 }
387 }
388}
389
390#[derive(Debug)]
391struct Limits<T: Ord> {
392 lo: T,
393 hi: T,
394}
395
396impl<T: Ord + Clone + Copy> Limits<T> {
397 fn min(&self) -> T {
398 self.lo
399 }
400
401 fn apply_limits(&self, val: T) -> T {
402 val.max(self.lo).min(self.hi)
403 }
404}
405
406impl<T: Ord + Clone + Copy + From<u8>> Limits<T> {
407 pub(crate) fn no_greater_than(val: T) -> Self {
408 Self {
409 lo: T::from(0),
410 hi: val,
411 }
412 }
413}
414
415fn initial_pacing_rate(
416 cwnd_in_bytes: usize, smoothed_rtt: Duration, params: &Params,
417) -> Bandwidth {
418 if let Some(pacing_rate) = params.initial_pacing_rate_bytes_per_second {
419 return Bandwidth::from_bytes_per_second(pacing_rate);
420 }
421
422 Bandwidth::from_bytes_and_time_delta(cwnd_in_bytes, smoothed_rtt) * 2.885
423}
424
425#[derive(Debug)]
426pub(crate) struct BBRv2 {
427 mode: Mode,
428 cwnd: usize,
429 mss: usize,
430
431 pacing_rate: Bandwidth,
432
433 cwnd_limits: Limits<usize>,
434
435 initial_cwnd: usize,
436
437 last_sample_is_app_limited: bool,
438 has_non_app_limited_sample: bool,
439 last_quiescence_start: Option<Instant>,
440 params: Params,
441}
442
443struct BBRv2CongestionEvent {
444 event_time: Instant,
445
446 prior_cwnd: usize,
448 prior_bytes_in_flight: usize,
450
451 bytes_in_flight: usize,
453 bytes_acked: usize,
455 bytes_lost: usize,
457
458 end_of_round_trip: bool,
460 is_probing_for_bandwidth: bool,
462
463 sample_max_bandwidth: Option<Bandwidth>,
467
468 sample_min_rtt: Option<Duration>,
471
472 last_packet_send_state: SendTimeState,
476}
477
478impl BBRv2CongestionEvent {
479 fn new(
480 event_time: Instant, prior_cwnd: usize, prior_bytes_in_flight: usize,
481 is_probing_for_bandwidth: bool,
482 ) -> Self {
483 BBRv2CongestionEvent {
484 event_time,
485 prior_cwnd,
486 prior_bytes_in_flight,
487 is_probing_for_bandwidth,
488 bytes_in_flight: 0,
489 bytes_acked: 0,
490 bytes_lost: 0,
491 end_of_round_trip: false,
492 last_packet_send_state: Default::default(),
493 sample_max_bandwidth: None,
494 sample_min_rtt: None,
495 }
496 }
497}
498
499impl BBRv2 {
500 pub fn new(
501 initial_congestion_window: usize, max_congestion_window: usize,
502 max_segment_size: usize, smoothed_rtt: Duration,
503 custom_bbr_params: Option<&BbrParams>,
504 ) -> Self {
505 let cwnd = initial_congestion_window * max_segment_size;
506
507 let params = if let Some(custom_bbr_settings) = custom_bbr_params {
508 DEFAULT_PARAMS.with_overrides(custom_bbr_settings)
509 } else {
510 DEFAULT_PARAMS
511 };
512
513 BBRv2 {
514 mode: Mode::startup(BBRv2NetworkModel::new(¶ms, smoothed_rtt)),
515 cwnd,
516 pacing_rate: initial_pacing_rate(cwnd, smoothed_rtt, ¶ms),
517 cwnd_limits: Limits {
518 lo: initial_congestion_window * max_segment_size,
519 hi: max_congestion_window * max_segment_size,
520 },
521 initial_cwnd: initial_congestion_window * max_segment_size,
522 last_sample_is_app_limited: false,
523 has_non_app_limited_sample: false,
524 last_quiescence_start: None,
525 mss: max_segment_size,
526 params,
527 }
528 }
529
530 pub fn time_sent_set_to_now(&self) -> bool {
531 self.params.time_sent_set_to_now
532 }
533
534 fn on_exit_quiescence(&mut self, now: Instant) {
535 if let Some(last_quiescence_start) = self.last_quiescence_start.take() {
536 self.mode.do_on_exit_quiescence(
537 now,
538 last_quiescence_start,
539 &self.params,
540 )
541 }
542 }
543
544 fn get_target_congestion_window(&self, gain: f32) -> usize {
545 let network_model = self.mode.network_model();
546 network_model
547 .bdp(network_model.bandwidth_estimate(), gain)
548 .max(self.cwnd_limits.min())
549 }
550
551 fn update_pacing_rate(&mut self, bytes_acked: usize) {
552 let network_model = self.mode.network_model();
553 let bandwidth_estimate = match network_model.bandwidth_estimate() {
554 e if e == Bandwidth::zero() => return,
555 e => e,
556 };
557
558 if network_model.total_bytes_acked() == bytes_acked {
559 self.pacing_rate = Bandwidth::from_bytes_and_time_delta(
561 self.cwnd,
562 network_model.min_rtt(),
563 );
564
565 if let Some(pacing_rate) =
566 self.params.initial_pacing_rate_bytes_per_second
567 {
568 let initial_pacing_rate =
572 Bandwidth::from_bytes_per_second(pacing_rate);
573 self.pacing_rate = self.pacing_rate.min(initial_pacing_rate);
574 }
575
576 return;
577 }
578
579 let target_rate = bandwidth_estimate * network_model.pacing_gain();
580 if network_model.full_bandwidth_reached() {
581 self.pacing_rate = target_rate;
582 return;
583 }
584
585 if self.params.decrease_startup_pacing_at_end_of_round &&
586 network_model.pacing_gain() < self.params.startup_pacing_gain
587 {
588 self.pacing_rate = target_rate;
589 return;
590 }
591
592 if self.params.bw_lo_mode != BwLoMode::Default &&
593 network_model.loss_events_in_round() > 0
594 {
595 self.pacing_rate = target_rate;
596 return;
597 }
598
599 self.pacing_rate = self.pacing_rate.max(target_rate);
601 }
602
603 fn update_congestion_window(&mut self, bytes_acked: usize) {
604 let network_model = self.mode.network_model();
605 let mut target_cwnd =
606 self.get_target_congestion_window(network_model.cwnd_gain());
607
608 let prior_cwnd = self.cwnd;
609 if network_model.full_bandwidth_reached() {
610 target_cwnd += network_model.max_ack_height();
611 self.cwnd = target_cwnd.min(prior_cwnd + bytes_acked);
612 } else if prior_cwnd < target_cwnd || prior_cwnd < 2 * self.initial_cwnd {
613 self.cwnd = prior_cwnd + bytes_acked;
614 }
615
616 self.cwnd = self
617 .mode
618 .get_cwnd_limits(&self.params)
619 .apply_limits(self.cwnd);
620 self.cwnd = self.cwnd_limits.apply_limits(self.cwnd);
621 }
622
623 fn on_enter_quiescence(&mut self, time: Instant) {
624 self.last_quiescence_start = Some(time);
625 }
626
627 fn target_bytes_inflight(&self) -> usize {
628 let network_model = &self.mode.network_model();
629 let bdp = network_model.bdp1(network_model.bandwidth_estimate());
630 bdp.min(self.get_congestion_window())
631 }
632
633 #[cfg(feature = "qlog")]
634 pub(crate) fn send_rate(&self) -> Option<Bandwidth> {
635 self.mode.network_model().send_rate()
636 }
637
638 #[cfg(feature = "qlog")]
639 pub(crate) fn ack_rate(&self) -> Option<Bandwidth> {
640 self.mode.network_model().ack_rate()
641 }
642}
643
644impl CongestionControl for BBRv2 {
645 #[cfg(feature = "qlog")]
646 fn state_str(&self) -> &'static str {
647 self.mode.state_str()
648 }
649
650 fn get_congestion_window(&self) -> usize {
651 self.cwnd
652 }
653
654 fn get_congestion_window_in_packets(&self) -> usize {
655 self.cwnd / self.mss
656 }
657
658 fn can_send(&self, bytes_in_flight: usize) -> bool {
659 bytes_in_flight < self.get_congestion_window()
660 }
661
662 fn on_packet_sent(
663 &mut self, sent_time: Instant, bytes_in_flight: usize,
664 packet_number: u64, bytes: usize, is_retransmissible: bool,
665 ) {
666 if bytes_in_flight == 0 && self.params.avoid_unnecessary_probe_rtt {
667 self.on_exit_quiescence(sent_time);
668 }
669
670 let network_model = self.mode.network_model_mut();
671 network_model.on_packet_sent(
672 sent_time,
673 bytes_in_flight,
674 packet_number,
675 bytes,
676 is_retransmissible,
677 );
678 }
679
680 fn on_congestion_event(
681 &mut self, _rtt_updated: bool, prior_in_flight: usize,
682 _bytes_in_flight: usize, event_time: Instant, acked_packets: &[Acked],
683 lost_packets: &[Lost], least_unacked: u64, _rtt_stats: &RttStats,
684 recovery_stats: &mut RecoveryStats,
685 ) {
686 let mut congestion_event = BBRv2CongestionEvent::new(
687 event_time,
688 self.cwnd,
689 prior_in_flight,
690 self.mode.is_probing_for_bandwidth(),
691 );
692
693 let network_model = self.mode.network_model_mut();
694 network_model.on_congestion_event_start(
695 acked_packets,
696 lost_packets,
697 &mut congestion_event,
698 &self.params,
699 );
700
701 let mut mode_changes_allowed = MAX_MODE_CHANGES_PER_CONGESTION_EVENT;
703 while mode_changes_allowed > 0 &&
704 self.mode.do_on_congestion_event(
705 prior_in_flight,
706 event_time,
707 acked_packets,
708 lost_packets,
709 &mut congestion_event,
710 self.target_bytes_inflight(),
711 &self.params,
712 recovery_stats,
713 self.get_congestion_window(),
714 )
715 {
716 mode_changes_allowed -= 1;
717 }
718
719 self.update_pacing_rate(congestion_event.bytes_acked);
720
721 self.update_congestion_window(congestion_event.bytes_acked);
722
723 let network_model = self.mode.network_model_mut();
724 network_model
725 .on_congestion_event_finish(least_unacked, &congestion_event);
726 self.last_sample_is_app_limited =
727 congestion_event.last_packet_send_state.is_app_limited;
728 if !self.last_sample_is_app_limited {
729 self.has_non_app_limited_sample = true;
730 }
731 if congestion_event.bytes_in_flight == 0 &&
732 self.params.avoid_unnecessary_probe_rtt
733 {
734 self.on_enter_quiescence(event_time);
735 }
736 }
737
738 fn on_packet_neutered(&mut self, packet_number: u64) {
739 let network_model = self.mode.network_model_mut();
740 network_model.on_packet_neutered(packet_number);
741 }
742
743 fn on_retransmission_timeout(&mut self, _packets_retransmitted: bool) {}
744
745 fn on_connection_migration(&mut self) {}
746
747 fn is_in_recovery(&self) -> bool {
748 self.last_quiescence_start.is_none()
750 }
751
752 fn is_cwnd_limited(&self, bytes_in_flight: usize) -> bool {
753 bytes_in_flight >= self.get_congestion_window()
754 }
755
756 fn pacing_rate(
757 &self, _bytes_in_flight: usize, _rtt_stats: &RttStats,
758 ) -> Bandwidth {
759 self.pacing_rate
760 }
761
762 fn bandwidth_estimate(&self, _rtt_stats: &RttStats) -> Bandwidth {
763 let network_model = self.mode.network_model();
764 network_model.bandwidth_estimate()
765 }
766
767 fn max_bandwidth(&self) -> Bandwidth {
768 self.mode.network_model().max_bandwidth()
769 }
770
771 fn update_mss(&mut self, new_mss: usize) {
772 self.cwnd_limits.hi = (self.cwnd_limits.hi as u64 * new_mss as u64 /
773 self.mss as u64) as usize;
774 self.cwnd_limits.lo = (self.cwnd_limits.lo as u64 * new_mss as u64 /
775 self.mss as u64) as usize;
776 self.cwnd =
777 (self.cwnd as u64 * new_mss as u64 / self.mss as u64) as usize;
778 self.initial_cwnd = (self.initial_cwnd as u64 * new_mss as u64 /
779 self.mss as u64) as usize;
780 if self.params.scale_pacing_rate_by_mss {
781 self.pacing_rate =
782 self.pacing_rate * (new_mss as f64 / self.mss as f64);
783 }
784 self.mss = new_mss;
785 }
786
787 fn on_app_limited(&mut self, bytes_in_flight: usize) {
788 if bytes_in_flight >= self.get_congestion_window() {
789 return;
790 }
791
792 let network_model = self.mode.network_model_mut();
793 network_model.on_app_limited()
794 }
795
796 fn limit_cwnd(&mut self, max_cwnd: usize) {
797 self.cwnd_limits.hi = max_cwnd
798 }
799}
800
801#[cfg(test)]
802mod tests {
803 use rstest::rstest;
804
805 use super::*;
806
807 #[rstest]
808 fn update_mss(#[values(false, true)] scale_pacing_rate_by_mss: bool) {
809 const INIT_PACKET_SIZE: usize = 1200;
810 const INIT_WINDOW_PACKETS: usize = 10;
811 const MAX_WINDOW_PACKETS: usize = 10000;
812 const INIT_CWND: usize = INIT_WINDOW_PACKETS * INIT_PACKET_SIZE;
813 const MAX_CWND: usize = MAX_WINDOW_PACKETS * INIT_PACKET_SIZE;
814 let initial_rtt = Duration::from_millis(333);
815 let bbr_params = &BbrParams {
816 scale_pacing_rate_by_mss: Some(scale_pacing_rate_by_mss),
817 ..Default::default()
818 };
819
820 const NEW_PACKET_SIZE: usize = 1450;
821 const NEW_CWND: usize = INIT_WINDOW_PACKETS * NEW_PACKET_SIZE;
822 const NEW_MAX_CWND: usize = MAX_WINDOW_PACKETS * NEW_PACKET_SIZE;
823
824 let mut bbr2 = BBRv2::new(
825 INIT_WINDOW_PACKETS,
826 MAX_WINDOW_PACKETS,
827 INIT_PACKET_SIZE,
828 initial_rtt,
829 Some(bbr_params),
830 );
831
832 assert_eq!(bbr2.cwnd_limits.lo, INIT_CWND);
833 assert_eq!(bbr2.cwnd_limits.hi, MAX_CWND);
834 assert_eq!(bbr2.cwnd, INIT_CWND);
835 assert_eq!(
836 bbr2.pacing_rate.to_bytes_per_period(initial_rtt),
837 (2.88499 * INIT_CWND as f64) as u64
838 );
839
840 bbr2.update_mss(NEW_PACKET_SIZE);
841
842 assert_eq!(bbr2.cwnd_limits.lo, NEW_CWND);
843 assert_eq!(bbr2.cwnd_limits.hi, NEW_MAX_CWND);
844 assert_eq!(bbr2.cwnd, NEW_CWND);
845 let pacing_cwnd = if scale_pacing_rate_by_mss {
846 NEW_CWND
847 } else {
848 INIT_CWND
849 };
850 assert_eq!(
851 bbr2.pacing_rate.to_bytes_per_period(initial_rtt),
852 (2.88499 * pacing_cwnd as f64) as u64
853 );
854 }
855}