1use super::*;
33use crate::minmax::Minmax;
34
35use std::time::Duration;
36
37use super::CongestionControlOps;
38
39pub(crate) static BBR: CongestionControlOps = CongestionControlOps {
40 on_init,
41 on_packet_sent,
42 on_packets_acked,
43 congestion_event,
44 checkpoint,
45 rollback,
46 has_custom_pacing,
47 #[cfg(feature = "qlog")]
48 state_str,
49 debug_fmt,
50};
51
52const BTLBW_FILTER_LEN: Duration = Duration::from_secs(10);
55
56const PROBE_RTT_INTERVAL: Duration = Duration::from_secs(10);
59
60const RTPROP_FILTER_LEN: Duration = PROBE_RTT_INTERVAL;
62
63const BBR_HIGH_GAIN: f64 = 2.89;
67
68const BBR_MIN_PIPE_CWND_PKTS: usize = 4;
70
71const BBR_GAIN_CYCLE_LEN: usize = 8;
73
74const PROBE_RTT_DURATION: Duration = Duration::from_millis(200);
77
78const PACING_GAIN_CYCLE: [f64; BBR_GAIN_CYCLE_LEN] =
80 [5.0 / 4.0, 3.0 / 4.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
81
82const BTLBW_GROWTH_TARGET: f64 = 1.25;
84
85#[derive(Clone, Copy, Debug, PartialEq, Eq)]
87enum BBRStateMachine {
88 Startup,
89 Drain,
90 ProbeBW,
91 ProbeRTT,
92}
93
94impl From<BBRStateMachine> for &'static str {
95 fn from(state: BBRStateMachine) -> &'static str {
96 match state {
97 BBRStateMachine::Startup => "bbr_startup",
98 BBRStateMachine::Drain => "bbr_drain",
99 BBRStateMachine::ProbeBW => "bbr_probe_bw",
100 BBRStateMachine::ProbeRTT => "bbr_probe_rtt",
101 }
102 }
103}
104
105pub struct State {
107 state: BBRStateMachine,
109
110 pacing_rate: u64,
113
114 btlbw: u64,
117
118 btlbwfilter: Minmax<u64>,
120
121 rtprop: Duration,
124
125 rtprop_stamp: Instant,
127
128 rtprop_expired: bool,
132
133 pacing_gain: f64,
136
137 cwnd_gain: f64,
140
141 filled_pipe: bool,
144
145 round_count: u64,
147
148 round_start: bool,
151
152 next_round_delivered: usize,
154
155 probe_rtt_done_stamp: Option<Instant>,
157
158 probe_rtt_round_done: bool,
160
161 packet_conservation: bool,
163
164 prior_cwnd: usize,
166
167 idle_restart: bool,
169
170 full_bw: u64,
172
173 full_bw_count: usize,
175
176 cycle_stamp: Instant,
178
179 cycle_index: usize,
181
182 target_cwnd: usize,
184
185 in_recovery: bool,
187
188 start_time: Instant,
190
191 newly_lost_bytes: usize,
193
194 newly_acked_bytes: usize,
196
197 prior_bytes_in_flight: usize,
199}
200
201impl State {
202 pub fn new() -> Self {
203 let now = Instant::now();
204
205 State {
206 state: BBRStateMachine::Startup,
207
208 pacing_rate: 0,
209
210 btlbw: 0,
211
212 btlbwfilter: Minmax::new(0),
213
214 rtprop: Duration::ZERO,
215
216 rtprop_stamp: now,
217
218 rtprop_expired: false,
219
220 pacing_gain: 0.0,
221
222 cwnd_gain: 0.0,
223
224 filled_pipe: false,
225
226 round_count: 0,
227
228 round_start: false,
229
230 next_round_delivered: 0,
231
232 probe_rtt_done_stamp: None,
233
234 probe_rtt_round_done: false,
235
236 packet_conservation: false,
237
238 prior_cwnd: 0,
239
240 idle_restart: false,
241
242 full_bw: 0,
243
244 full_bw_count: 0,
245
246 cycle_stamp: now,
247
248 cycle_index: 0,
249
250 target_cwnd: 0,
251
252 in_recovery: false,
253
254 start_time: now,
255
256 newly_lost_bytes: 0,
257
258 newly_acked_bytes: 0,
259
260 prior_bytes_in_flight: 0,
261 }
262 }
263}
264
265fn bbr_enter_recovery(r: &mut Congestion, in_flight: usize, now: Instant) {
267 r.bbr_state.prior_cwnd = per_ack::bbr_save_cwnd(r);
268
269 r.congestion_window = in_flight.max(r.max_datagram_size);
270 r.congestion_recovery_start_time = Some(now);
271
272 r.bbr_state.packet_conservation = true;
273 r.bbr_state.in_recovery = true;
274
275 r.bbr_state.newly_lost_bytes = 0;
276
277 r.bbr_state.next_round_delivered = r.delivery_rate.delivered();
279}
280
281fn bbr_exit_recovery(r: &mut Congestion) {
283 r.congestion_recovery_start_time = None;
284
285 r.bbr_state.packet_conservation = false;
286 r.bbr_state.in_recovery = false;
287
288 per_ack::bbr_restore_cwnd(r);
289}
290
291fn on_init(r: &mut Congestion) {
294 init::bbr_init(r);
295}
296
297fn on_packet_sent(
298 r: &mut Congestion, _sent_bytes: usize, bytes_in_flight: usize, _now: Instant,
299) {
300 per_transmit::bbr_on_transmit(r, bytes_in_flight);
301}
302
303fn on_packets_acked(
304 r: &mut Congestion, bytes_in_flight: usize, packets: &mut Vec<Acked>,
305 now: Instant, _rtt_stats: &RttStats,
306) {
307 r.bbr_state.prior_bytes_in_flight = bytes_in_flight;
308
309 r.bbr_state.newly_acked_bytes =
310 packets.drain(..).fold(0, |acked_bytes, p| {
311 r.bbr_state.prior_bytes_in_flight -= p.size;
312
313 per_ack::bbr_update_model_and_state(r, &p, bytes_in_flight, now);
314
315 acked_bytes + p.size
316 });
317
318 if let Some(pkt) = packets.last() {
319 if !r.in_congestion_recovery(pkt.time_sent) && r.bbr_state.in_recovery {
320 bbr_exit_recovery(r);
322 }
323 }
324
325 per_ack::bbr_update_control_parameters(r, bytes_in_flight, now);
326
327 r.bbr_state.newly_lost_bytes = 0;
328}
329
330fn congestion_event(
331 r: &mut Congestion, bytes_in_flight: usize, lost_bytes: usize,
332 largest_lost_pkt: &Sent, now: Instant,
333) {
334 r.bbr_state.newly_lost_bytes = lost_bytes;
335
336 if !r.in_congestion_recovery(largest_lost_pkt.time_sent) {
338 bbr_enter_recovery(r, bytes_in_flight - lost_bytes, now);
340 }
341}
342
343fn checkpoint(_r: &mut Congestion) {}
344
345fn rollback(_r: &mut Congestion) -> bool {
346 false
347}
348
349fn has_custom_pacing() -> bool {
350 true
351}
352
353#[cfg(feature = "qlog")]
354fn state_str(r: &Congestion, _now: Instant) -> &'static str {
355 r.bbr_state.state.into()
356}
357
358fn debug_fmt(r: &Congestion, f: &mut std::fmt::Formatter) -> std::fmt::Result {
359 let bbr = &r.bbr_state;
360
361 write!(
362 f,
363 "bbr={{ state={:?} btlbw={} rtprop={:?} pacing_rate={} pacing_gain={} cwnd_gain={} target_cwnd={} send_quantum={} filled_pipe={} round_count={} }}",
364 bbr.state, bbr.btlbw, bbr.rtprop, bbr.pacing_rate, bbr.pacing_gain, bbr.cwnd_gain, bbr.target_cwnd, r.send_quantum(), bbr.filled_pipe, bbr.round_count
365 )
366}
367
368#[cfg(test)]
369mod tests {
370 use super::*;
371
372 use crate::packet;
373 use crate::ranges;
374 use crate::recovery::congestion::recovery::LegacyRecovery;
375 use crate::recovery::congestion::test_sender::TestSender;
376 use crate::recovery::HandshakeStatus;
377 use crate::recovery::RecoveryOps;
378 use crate::OnAckReceivedOutcome;
379
380 use smallvec::smallvec;
381
382 fn test_sender() -> TestSender {
383 TestSender::new(CongestionControlAlgorithm::BBR, false)
384 }
385
386 #[test]
387 fn bbr_init() {
388 let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
389 cfg.set_cc_algorithm(CongestionControlAlgorithm::BBR);
390
391 let r = LegacyRecovery::new(&cfg);
392
393 assert_eq!(
394 r.cwnd(),
395 r.max_datagram_size * cfg.initial_congestion_window_packets
396 );
397 assert_eq!(r.bytes_in_flight(), 0);
398
399 assert_eq!(r.congestion.bbr_state.state, BBRStateMachine::Startup);
400 }
401
402 #[test]
403 fn bbr_startup() {
404 let mut sender = test_sender();
405 let mss = sender.max_datagram_size;
406
407 let rtt = Duration::from_millis(50);
408 sender.update_rtt(rtt);
409 sender.advance_time(rtt);
410
411 for _ in 0..5 {
413 sender.send_packet(mss);
414 }
415
416 sender.advance_time(rtt);
417
418 let cwnd_prev = sender.congestion_window;
419
420 sender.ack_n_packets(5, mss);
421
422 assert_eq!(sender.bbr_state.state, BBRStateMachine::Startup);
423 assert_eq!(sender.congestion_window, cwnd_prev + mss * 5);
424 assert_eq!(sender.bytes_in_flight, 0);
425 assert_eq!(
426 sender.delivery_rate().to_bytes_per_second(),
427 ((mss * 5) as f64 / rtt.as_secs_f64()) as u64
428 );
429 assert_eq!(
430 sender.bbr_state.btlbw,
431 sender.delivery_rate().to_bytes_per_second()
432 );
433 }
434
435 #[test]
436 fn bbr_congestion_event() {
437 let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
438 cfg.set_cc_algorithm(CongestionControlAlgorithm::BBR);
439
440 let mut r = LegacyRecovery::new(&cfg);
441 let now = Instant::now();
442 let mss = r.max_datagram_size;
443
444 for pn in 0..5 {
446 let pkt = Sent {
447 pkt_num: pn,
448 frames: smallvec![],
449 time_sent: now,
450 time_acked: None,
451 time_lost: None,
452 size: mss,
453 ack_eliciting: true,
454 in_flight: true,
455 delivered: 0,
456 delivered_time: now,
457 first_sent_time: now,
458 is_app_limited: false,
459 tx_in_flight: 0,
460 lost: 0,
461 has_data: false,
462 is_pmtud_probe: false,
463 };
464
465 r.on_packet_sent(
466 pkt,
467 packet::Epoch::Application,
468 HandshakeStatus::default(),
469 now,
470 "",
471 );
472 }
473
474 let rtt = Duration::from_millis(50);
475 let now = now + rtt;
476
477 let mut acked = ranges::RangeSet::default();
479 acked.insert(4..5);
480
481 assert_eq!(
483 r.on_ack_received(
484 &acked,
485 25,
486 packet::Epoch::Application,
487 HandshakeStatus::default(),
488 now,
489 None,
490 "",
491 )
492 .unwrap(),
493 OnAckReceivedOutcome {
494 lost_packets: 2,
495 lost_bytes: 2 * mss,
496 acked_bytes: mss,
497 spurious_losses: 0,
498 },
499 );
500
501 assert_eq!(r.cwnd(), mss * 4);
503 assert_eq!(r.bytes_in_flight(), mss * 2);
505 }
506
507 #[test]
508 fn bbr_drain() {
509 let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
510 cfg.set_cc_algorithm(CongestionControlAlgorithm::BBR);
511
512 let mut r = LegacyRecovery::new(&cfg);
513 let now = Instant::now();
514 let mss = r.max_datagram_size;
515
516 let mut pn = 0;
517
518 for _ in 0..3 {
520 let pkt = Sent {
521 pkt_num: pn,
522 frames: smallvec![],
523 time_sent: now,
524 time_acked: None,
525 time_lost: None,
526 size: mss,
527 ack_eliciting: true,
528 in_flight: true,
529 delivered: r.congestion.delivery_rate.delivered(),
530 delivered_time: now,
531 first_sent_time: now,
532 is_app_limited: false,
533 tx_in_flight: 0,
534 lost: 0,
535 has_data: false,
536 is_pmtud_probe: false,
537 };
538
539 r.on_packet_sent(
540 pkt,
541 packet::Epoch::Application,
542 HandshakeStatus::default(),
543 now,
544 "",
545 );
546
547 pn += 1;
548
549 let rtt = Duration::from_millis(50);
550
551 let now = now + rtt;
552
553 let mut acked = ranges::RangeSet::default();
554 acked.insert(0..pn);
555
556 assert_eq!(
557 r.on_ack_received(
558 &acked,
559 25,
560 packet::Epoch::Application,
561 HandshakeStatus::default(),
562 now,
563 None,
564 "",
565 )
566 .unwrap(),
567 OnAckReceivedOutcome {
568 lost_packets: 0,
569 lost_bytes: 0,
570 acked_bytes: mss,
571 spurious_losses: 0,
572 },
573 );
574 }
575
576 for _ in 0..7 {
577 let pkt = Sent {
578 pkt_num: pn,
579 frames: smallvec![],
580 time_sent: now,
581 time_acked: None,
582 time_lost: None,
583 size: mss,
584 ack_eliciting: true,
585 in_flight: true,
586 delivered: r.congestion.delivery_rate.delivered(),
587 delivered_time: now,
588 first_sent_time: now,
589 is_app_limited: false,
590 tx_in_flight: 0,
591 lost: 0,
592 has_data: false,
593 is_pmtud_probe: false,
594 };
595
596 r.on_packet_sent(
597 pkt,
598 packet::Epoch::Application,
599 HandshakeStatus::default(),
600 now,
601 "",
602 );
603
604 pn += 1;
605 }
606
607 let rtt = Duration::from_millis(50);
608 let now = now + rtt;
609
610 let mut acked = ranges::RangeSet::default();
611
612 acked.insert(0..pn - 6);
614
615 assert_eq!(
616 r.on_ack_received(
617 &acked,
618 25,
619 packet::Epoch::Application,
620 HandshakeStatus::default(),
621 now,
622 None,
623 "",
624 )
625 .unwrap(),
626 OnAckReceivedOutcome {
627 lost_packets: 0,
628 lost_bytes: 0,
629 acked_bytes: mss,
630 spurious_losses: 0,
631 },
632 );
633
634 assert!(r.congestion.bbr_state.filled_pipe);
636 assert_eq!(r.congestion.bbr_state.state, BBRStateMachine::Drain);
637 assert!(r.congestion.bbr_state.pacing_gain < 1.0);
638 }
639
640 #[test]
641 fn bbr_probe_bw() {
642 let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
643 cfg.set_cc_algorithm(CongestionControlAlgorithm::BBR);
644
645 let mut r = LegacyRecovery::new(&cfg);
646 let now = Instant::now();
647 let mss = r.max_datagram_size;
648
649 for (pn, _) in (0..4).enumerate() {
653 let pkt = Sent {
654 pkt_num: pn as u64,
655 frames: smallvec![],
656 time_sent: now,
657 time_acked: None,
658 time_lost: None,
659 size: mss,
660 ack_eliciting: true,
661 in_flight: true,
662 delivered: r.congestion.delivery_rate.delivered(),
663 delivered_time: now,
664 first_sent_time: now,
665 is_app_limited: false,
666 tx_in_flight: 0,
667 lost: 0,
668 has_data: false,
669 is_pmtud_probe: false,
670 };
671
672 r.on_packet_sent(
673 pkt,
674 packet::Epoch::Application,
675 HandshakeStatus::default(),
676 now,
677 "",
678 );
679
680 let rtt = Duration::from_millis(50);
681 let now = now + rtt;
682
683 let mut acked = ranges::RangeSet::default();
684 acked.insert(0..pn as u64 + 1);
685
686 assert_eq!(
687 r.on_ack_received(
688 &acked,
689 25,
690 packet::Epoch::Application,
691 HandshakeStatus::default(),
692 now,
693 None,
694 "",
695 )
696 .unwrap(),
697 OnAckReceivedOutcome {
698 lost_packets: 0,
699 lost_bytes: 0,
700 acked_bytes: mss,
701 spurious_losses: 0,
702 },
703 );
704 }
705
706 assert!(r.congestion.bbr_state.filled_pipe);
708 assert_eq!(r.congestion.bbr_state.state, BBRStateMachine::ProbeBW);
709
710 assert!(r.congestion.bbr_state.pacing_gain >= 1.0);
712 }
713
714 #[test]
715 fn bbr_probe_rtt() {
716 let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
717 cfg.set_cc_algorithm(CongestionControlAlgorithm::BBR);
718
719 let mut r = LegacyRecovery::new(&cfg);
720 let now = Instant::now();
721 let mss = r.max_datagram_size;
722
723 let mut pn = 0;
724
725 for _ in 0..4 {
729 let pkt = Sent {
730 pkt_num: pn,
731 frames: smallvec![],
732 time_sent: now,
733 time_acked: None,
734 time_lost: None,
735 size: mss,
736 ack_eliciting: true,
737 in_flight: true,
738 delivered: r.congestion.delivery_rate.delivered(),
739 delivered_time: now,
740 first_sent_time: now,
741 is_app_limited: false,
742 tx_in_flight: 0,
743 lost: 0,
744 has_data: false,
745 is_pmtud_probe: false,
746 };
747
748 r.on_packet_sent(
749 pkt,
750 packet::Epoch::Application,
751 HandshakeStatus::default(),
752 now,
753 "",
754 );
755
756 pn += 1;
757
758 let rtt = Duration::from_millis(50);
759 let now = now + rtt;
760
761 let mut acked = ranges::RangeSet::default();
762 acked.insert(0..pn);
763
764 assert_eq!(
765 r.on_ack_received(
766 &acked,
767 25,
768 packet::Epoch::Application,
769 HandshakeStatus::default(),
770 now,
771 None,
772 "",
773 )
774 .unwrap(),
775 OnAckReceivedOutcome {
776 lost_packets: 0,
777 lost_bytes: 0,
778 acked_bytes: mss,
779 spurious_losses: 0,
780 },
781 );
782 }
783
784 assert_eq!(r.congestion.bbr_state.state, BBRStateMachine::ProbeBW);
786
787 let now = now + RTPROP_FILTER_LEN;
789
790 let pkt = Sent {
791 pkt_num: pn,
792 frames: smallvec![],
793 time_sent: now,
794 time_acked: None,
795 time_lost: None,
796 size: mss,
797 ack_eliciting: true,
798 in_flight: true,
799 delivered: r.congestion.delivery_rate.delivered(),
800 delivered_time: now,
801 first_sent_time: now,
802 is_app_limited: false,
803 tx_in_flight: 0,
804 lost: 0,
805 has_data: false,
806 is_pmtud_probe: false,
807 };
808
809 r.on_packet_sent(
810 pkt,
811 packet::Epoch::Application,
812 HandshakeStatus::default(),
813 now,
814 "",
815 );
816
817 pn += 1;
818
819 let rtt = Duration::from_millis(100);
822 let now = now + rtt;
823
824 let mut acked = ranges::RangeSet::default();
825 acked.insert(0..pn);
826
827 assert_eq!(
828 r.on_ack_received(
829 &acked,
830 25,
831 packet::Epoch::Application,
832 HandshakeStatus::default(),
833 now,
834 None,
835 "",
836 )
837 .unwrap(),
838 OnAckReceivedOutcome {
839 lost_packets: 0,
840 lost_bytes: 0,
841 acked_bytes: mss,
842 spurious_losses: 0,
843 },
844 );
845
846 assert_eq!(r.congestion.bbr_state.state, BBRStateMachine::ProbeRTT);
847 assert_eq!(r.congestion.bbr_state.pacing_gain, 1.0);
848 }
849}
850
851mod init;
852mod pacing;
853mod per_ack;
854mod per_transmit;