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