1 // Copyright (C) 2022, Cloudflare, Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 //
11 // * Redistributions in binary form must reproduce the above copyright
12 // notice, this list of conditions and the following disclaimer in the
13 // documentation and/or other materials provided with the distribution.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16 // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 //! BBR Congestion Control
28 //!
29 //! This implementation is based on the following draft:
30 //! <https://tools.ietf.org/html/draft-cardwell-iccrg-bbr-congestion-control-00>
31
32 use crate::minmax::Minmax;
33 use crate::packet;
34 use crate::recovery::*;
35
36 use std::time::Duration;
37 use std::time::Instant;
38
39 pub static BBR: CongestionControlOps = CongestionControlOps {
40 on_init,
41 reset,
42 on_packet_sent,
43 on_packets_acked,
44 congestion_event,
45 collapse_cwnd,
46 checkpoint,
47 rollback,
48 has_custom_pacing,
49 debug_fmt,
50 };
51
52 /// A constant specifying the length of the BBR.BtlBw max filter window for
53 /// BBR.BtlBwFilter, BtlBwFilterLen is 10 packet-timed round trips.
54 const BTLBW_FILTER_LEN: Duration = Duration::from_secs(10);
55
56 /// A constant specifying the minimum time interval between ProbeRTT states: 10
57 /// secs.
58 const PROBE_RTT_INTERVAL: Duration = Duration::from_secs(10);
59
60 /// A constant specifying the length of the RTProp min filter window.
61 const RTPROP_FILTER_LEN: Duration = PROBE_RTT_INTERVAL;
62
63 /// A constant specifying the minimum gain value that will allow the sending
64 /// rate to double each round (2/ln(2) ~= 2.89), used in Startup mode for both
65 /// BBR.pacing_gain and BBR.cwnd_gain.
66 const BBR_HIGH_GAIN: f64 = 2.89;
67
68 /// The minimal cwnd value BBR tries to target using: 4 packets, or 4 * SMSS
69 const BBR_MIN_PIPE_CWND_PKTS: usize = 4;
70
71 /// The number of phases in the BBR ProbeBW gain cycle: 8.
72 const BBR_GAIN_CYCLE_LEN: usize = 8;
73
74 /// A constant specifying the minimum duration for which ProbeRTT state holds
75 /// inflight to BBRMinPipeCwnd or fewer packets: 200 ms.
76 const PROBE_RTT_DURATION: Duration = Duration::from_millis(200);
77
78 /// Pacing Gain Cycle.
79 const 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
82 /// A constant to check BBR.BtlBW is still growing.
83 const BTLBW_GROWTH_TARGET: f64 = 1.25;
84
85 /// BBR Internal State Machine.
86 #[derive(Debug, PartialEq, Eq)]
87 enum BBRStateMachine {
88 Startup,
89 Drain,
90 ProbeBW,
91 ProbeRTT,
92 }
93
94 /// BBR Specific State Variables.
95 pub struct State {
96 // The current state of a BBR flow in the BBR state machine.
97 state: BBRStateMachine,
98
99 // The current pacing rate for a BBR flow, which controls inter-packet
100 // spacing.
101 pacing_rate: u64,
102
103 // BBR's estimated bottleneck bandwidth available to the transport flow,
104 // estimated from the maximum delivery rate sample in a sliding window.
105 btlbw: u64,
106
107 // The max filter used to estimate BBR.BtlBw.
108 btlbwfilter: Minmax<u64>,
109
110 // BBR's estimated two-way round-trip propagation delay of the path,
111 // estimated from the windowed minimum recent round-trip delay sample.
112 rtprop: Duration,
113
114 // The wall clock time at which the current BBR.RTProp sample was obtained.
115 rtprop_stamp: Instant,
116
117 // A boolean recording whether the BBR.RTprop has expired and is due for a
118 // refresh with an application idle period or a transition into ProbeRTT
119 // state.
120 rtprop_expired: bool,
121
122 // The dynamic gain factor used to scale BBR.BtlBw to produce
123 // BBR.pacing_rate.
124 pacing_gain: f64,
125
126 // The dynamic gain factor used to scale the estimated BDP to produce a
127 // congestion window (cwnd).
128 cwnd_gain: f64,
129
130 // A boolean that records whether BBR estimates that it has ever fully
131 // utilized its available bandwidth ("filled the pipe").
132 filled_pipe: bool,
133
134 // Count of packet-timed round trips elapsed so far.
135 round_count: u64,
136
137 // A boolean that BBR sets to true once per packet-timed round trip,
138 // on ACKs that advance BBR.round_count.
139 round_start: bool,
140
141 // packet.delivered value denoting the end of a packet-timed round trip.
142 next_round_delivered: usize,
143
144 // Timestamp when ProbeRTT state ends.
145 probe_rtt_done_stamp: Option<Instant>,
146
147 // Checking if a roundtrip in ProbeRTT state ends.
148 probe_rtt_round_done: bool,
149
150 // Checking if in the packet conservation mode during recovery.
151 packet_conservation: bool,
152
153 // Saved cwnd before loss recovery.
154 prior_cwnd: usize,
155
156 // Checking if restarting from idle.
157 idle_restart: bool,
158
159 // Baseline level delivery rate for full pipe estimator.
160 full_bw: u64,
161
162 // The number of round for full pipe estimator without much growth.
163 full_bw_count: usize,
164
165 // Last time cycle_index is updated.
166 cycle_stamp: Instant,
167
168 // Current index of pacing_gain_cycle[].
169 cycle_index: usize,
170
171 // The upper bound on the volume of data BBR allows in flight.
172 target_cwnd: usize,
173
174 // Whether in the recovery episode.
175 in_recovery: bool,
176
177 // Start time of the connection.
178 start_time: Instant,
179
180 // Newly marked lost data size in bytes.
181 newly_lost_bytes: usize,
182
183 // Newly acked data size in bytes.
184 newly_acked_bytes: usize,
185
186 // bytes_in_flight before processing this ACK.
187 prior_bytes_in_flight: usize,
188 }
189
190 impl State {
new() -> Self191 pub fn new() -> Self {
192 let now = Instant::now();
193
194 State {
195 state: BBRStateMachine::Startup,
196
197 pacing_rate: 0,
198
199 btlbw: 0,
200
201 btlbwfilter: Minmax::new(0),
202
203 rtprop: Duration::ZERO,
204
205 rtprop_stamp: now,
206
207 rtprop_expired: false,
208
209 pacing_gain: 0.0,
210
211 cwnd_gain: 0.0,
212
213 filled_pipe: false,
214
215 round_count: 0,
216
217 round_start: false,
218
219 next_round_delivered: 0,
220
221 probe_rtt_done_stamp: None,
222
223 probe_rtt_round_done: false,
224
225 packet_conservation: false,
226
227 prior_cwnd: 0,
228
229 idle_restart: false,
230
231 full_bw: 0,
232
233 full_bw_count: 0,
234
235 cycle_stamp: now,
236
237 cycle_index: 0,
238
239 target_cwnd: 0,
240
241 in_recovery: false,
242
243 start_time: now,
244
245 newly_lost_bytes: 0,
246
247 newly_acked_bytes: 0,
248
249 prior_bytes_in_flight: 0,
250 }
251 }
252 }
253
254 // When entering the recovery episode.
bbr_enter_recovery(r: &mut Recovery, now: Instant)255 fn bbr_enter_recovery(r: &mut Recovery, now: Instant) {
256 r.bbr_state.prior_cwnd = per_ack::bbr_save_cwnd(r);
257
258 r.congestion_window = r.bytes_in_flight +
259 r.bbr_state.newly_acked_bytes.max(r.max_datagram_size);
260 r.congestion_recovery_start_time = Some(now);
261
262 r.bbr_state.packet_conservation = true;
263 r.bbr_state.in_recovery = true;
264
265 // Start round now.
266 r.bbr_state.next_round_delivered = r.delivery_rate.delivered();
267 }
268
269 // When exiting the recovery episode.
bbr_exit_recovery(r: &mut Recovery)270 fn bbr_exit_recovery(r: &mut Recovery) {
271 r.congestion_recovery_start_time = None;
272
273 r.bbr_state.packet_conservation = false;
274 r.bbr_state.in_recovery = false;
275
276 per_ack::bbr_restore_cwnd(r);
277 }
278
279 // Congestion Control Hooks.
280 //
on_init(r: &mut Recovery)281 fn on_init(r: &mut Recovery) {
282 init::bbr_init(r);
283 }
284
reset(r: &mut Recovery)285 fn reset(r: &mut Recovery) {
286 r.bbr_state = State::new();
287
288 init::bbr_init(r);
289 }
290
on_packet_sent(r: &mut Recovery, sent_bytes: usize, _now: Instant)291 fn on_packet_sent(r: &mut Recovery, sent_bytes: usize, _now: Instant) {
292 r.bytes_in_flight += sent_bytes;
293
294 per_transmit::bbr_on_transmit(r);
295 }
296
on_packets_acked( r: &mut Recovery, packets: &[Acked], _epoch: packet::Epoch, now: Instant, )297 fn on_packets_acked(
298 r: &mut Recovery, packets: &[Acked], _epoch: packet::Epoch, now: Instant,
299 ) {
300 r.bbr_state.newly_acked_bytes = packets.iter().fold(0, |acked_bytes, p| {
301 r.bbr_state.prior_bytes_in_flight = r.bytes_in_flight;
302
303 per_ack::bbr_update_model_and_state(r, p, now);
304
305 r.bytes_in_flight = r.bytes_in_flight.saturating_sub(p.size);
306
307 acked_bytes + p.size
308 });
309
310 if let Some(pkt) = packets.last() {
311 if !r.in_congestion_recovery(pkt.time_sent) {
312 // Upon exiting loss recovery.
313 bbr_exit_recovery(r);
314 }
315 }
316
317 per_ack::bbr_update_control_parameters(r, now);
318
319 r.bbr_state.newly_lost_bytes = 0;
320 }
321
congestion_event( r: &mut Recovery, lost_bytes: usize, time_sent: Instant, _epoch: packet::Epoch, now: Instant, )322 fn congestion_event(
323 r: &mut Recovery, lost_bytes: usize, time_sent: Instant,
324 _epoch: packet::Epoch, now: Instant,
325 ) {
326 r.bbr_state.newly_lost_bytes = lost_bytes;
327
328 // Upon entering Fast Recovery.
329 if !r.in_congestion_recovery(time_sent) {
330 // Upon entering Fast Recovery.
331 bbr_enter_recovery(r, now);
332 }
333 }
334
collapse_cwnd(r: &mut Recovery)335 fn collapse_cwnd(r: &mut Recovery) {
336 r.bbr_state.prior_cwnd = per_ack::bbr_save_cwnd(r);
337
338 reno::collapse_cwnd(r);
339 }
340
checkpoint(_r: &mut Recovery)341 fn checkpoint(_r: &mut Recovery) {}
342
rollback(_r: &mut Recovery) -> bool343 fn rollback(_r: &mut Recovery) -> bool {
344 false
345 }
346
has_custom_pacing() -> bool347 fn has_custom_pacing() -> bool {
348 true
349 }
350
debug_fmt(r: &Recovery, f: &mut std::fmt::Formatter) -> std::fmt::Result351 fn debug_fmt(r: &Recovery, f: &mut std::fmt::Formatter) -> std::fmt::Result {
352 let bbr = &r.bbr_state;
353
354 write!(
355 f,
356 "bbr={{ state={:?} btlbw={} rtprop={:?} pacing_rate={} pacing_gain={} cwnd_gain={} target_cwnd={} send_quantum={} filled_pipe={} round_count={} }}",
357 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
358 )
359 }
360
361 #[cfg(test)]
362 mod tests {
363 use super::*;
364
365 use crate::recovery;
366
367 use smallvec::smallvec;
368
369 #[test]
bbr_init()370 fn bbr_init() {
371 let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
372 cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::BBR);
373
374 let mut r = Recovery::new(&cfg);
375
376 // on_init() is called in Connection::new(), so it need to be
377 // called manually here.
378 r.on_init();
379
380 assert_eq!(r.cwnd(), r.max_datagram_size * INITIAL_WINDOW_PACKETS);
381 assert_eq!(r.bytes_in_flight, 0);
382
383 assert_eq!(r.bbr_state.state, BBRStateMachine::Startup);
384 }
385
386 #[test]
bbr_send()387 fn bbr_send() {
388 let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
389 cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::BBR);
390
391 let mut r = Recovery::new(&cfg);
392 let now = Instant::now();
393
394 r.on_init();
395 r.on_packet_sent_cc(1000, now);
396
397 assert_eq!(r.bytes_in_flight, 1000);
398 }
399
400 #[test]
bbr_startup()401 fn bbr_startup() {
402 let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
403 cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::BBR);
404
405 let mut r = Recovery::new(&cfg);
406 let now = Instant::now();
407 let mss = r.max_datagram_size;
408
409 r.on_init();
410
411 // Send 5 packets.
412 for pn in 0..5 {
413 let pkt = Sent {
414 pkt_num: pn,
415 frames: smallvec![],
416 time_sent: now,
417 time_acked: None,
418 time_lost: None,
419 size: mss,
420 ack_eliciting: true,
421 in_flight: true,
422 delivered: 0,
423 delivered_time: now,
424 first_sent_time: now,
425 is_app_limited: false,
426 has_data: false,
427 };
428
429 r.on_packet_sent(
430 pkt,
431 packet::Epoch::Application,
432 HandshakeStatus::default(),
433 now,
434 "",
435 );
436 }
437
438 let rtt = Duration::from_millis(50);
439 let now = now + rtt;
440 let cwnd_prev = r.cwnd();
441
442 let mut acked = ranges::RangeSet::default();
443 acked.insert(0..5);
444
445 assert_eq!(
446 r.on_ack_received(
447 &acked,
448 25,
449 packet::Epoch::Application,
450 HandshakeStatus::default(),
451 now,
452 "",
453 ),
454 Ok((0, 0)),
455 );
456
457 assert_eq!(r.bbr_state.state, BBRStateMachine::Startup);
458 assert_eq!(r.cwnd(), cwnd_prev + mss * 5);
459 assert_eq!(r.bytes_in_flight, 0);
460 assert_eq!(
461 r.delivery_rate(),
462 ((mss * 5) as f64 / rtt.as_secs_f64()) as u64
463 );
464 assert_eq!(r.bbr_state.btlbw, r.delivery_rate());
465 }
466
467 #[test]
bbr_congestion_event()468 fn bbr_congestion_event() {
469 let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
470 cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::BBR);
471
472 let mut r = Recovery::new(&cfg);
473 let now = Instant::now();
474 let mss = r.max_datagram_size;
475
476 r.on_init();
477
478 // Send 5 packets.
479 for pn in 0..5 {
480 let pkt = Sent {
481 pkt_num: pn,
482 frames: smallvec![],
483 time_sent: now,
484 time_acked: None,
485 time_lost: None,
486 size: mss,
487 ack_eliciting: true,
488 in_flight: true,
489 delivered: 0,
490 delivered_time: now,
491 first_sent_time: now,
492 is_app_limited: false,
493 has_data: false,
494 };
495
496 r.on_packet_sent(
497 pkt,
498 packet::Epoch::Application,
499 HandshakeStatus::default(),
500 now,
501 "",
502 );
503 }
504
505 let rtt = Duration::from_millis(50);
506 let now = now + rtt;
507
508 // Make a packet loss to trigger a congestion event.
509 let mut acked = ranges::RangeSet::default();
510 acked.insert(4..5);
511
512 // 2 acked, 2 x MSS lost.
513 assert_eq!(
514 r.on_ack_received(
515 &acked,
516 25,
517 packet::Epoch::Application,
518 HandshakeStatus::default(),
519 now,
520 "",
521 ),
522 Ok((2, 2400)),
523 );
524
525 // Sent: 0, 1, 2, 3, 4, Acked 4.
526 assert_eq!(r.cwnd(), mss * 4);
527 // Stil in flight: 2, 3.
528 assert_eq!(r.bytes_in_flight, mss * 2);
529 }
530
531 #[test]
bbr_drain()532 fn bbr_drain() {
533 let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
534 cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::BBR);
535
536 let mut r = Recovery::new(&cfg);
537 let now = Instant::now();
538 let mss = r.max_datagram_size;
539
540 r.on_init();
541
542 let mut pn = 0;
543
544 // Stop right before filled_pipe=true.
545 for _ in 0..3 {
546 let pkt = Sent {
547 pkt_num: pn,
548 frames: smallvec![],
549 time_sent: now,
550 time_acked: None,
551 time_lost: None,
552 size: mss,
553 ack_eliciting: true,
554 in_flight: true,
555 delivered: r.delivery_rate.delivered(),
556 delivered_time: now,
557 first_sent_time: now,
558 is_app_limited: false,
559 has_data: false,
560 };
561
562 r.on_packet_sent(
563 pkt,
564 packet::Epoch::Application,
565 HandshakeStatus::default(),
566 now,
567 "",
568 );
569
570 pn += 1;
571
572 let rtt = Duration::from_millis(50);
573
574 let now = now + rtt;
575
576 let mut acked = ranges::RangeSet::default();
577 acked.insert(0..pn);
578
579 assert_eq!(
580 r.on_ack_received(
581 &acked,
582 25,
583 packet::Epoch::Application,
584 HandshakeStatus::default(),
585 now,
586 "",
587 ),
588 Ok((0, 0)),
589 );
590 }
591
592 // Stop at right before filled_pipe=true.
593 for _ in 0..5 {
594 let pkt = Sent {
595 pkt_num: pn,
596 frames: smallvec![],
597 time_sent: now,
598 time_acked: None,
599 time_lost: None,
600 size: mss,
601 ack_eliciting: true,
602 in_flight: true,
603 delivered: r.delivery_rate.delivered(),
604 delivered_time: now,
605 first_sent_time: now,
606 is_app_limited: false,
607 has_data: false,
608 };
609
610 r.on_packet_sent(
611 pkt,
612 packet::Epoch::Application,
613 HandshakeStatus::default(),
614 now,
615 "",
616 );
617
618 pn += 1;
619 }
620
621 let rtt = Duration::from_millis(50);
622 let now = now + rtt;
623
624 let mut acked = ranges::RangeSet::default();
625
626 // We sent 5 packets, but ack only one, to stay
627 // in Drain state.
628 acked.insert(0..pn - 4);
629
630 assert_eq!(
631 r.on_ack_received(
632 &acked,
633 25,
634 packet::Epoch::Application,
635 HandshakeStatus::default(),
636 now,
637 "",
638 ),
639 Ok((0, 0)),
640 );
641
642 // Now we are in Drain state.
643 assert_eq!(r.bbr_state.filled_pipe, true);
644 assert_eq!(r.bbr_state.state, BBRStateMachine::Drain);
645 assert!(r.bbr_state.pacing_gain < 1.0);
646 }
647
648 #[test]
bbr_probe_bw()649 fn bbr_probe_bw() {
650 let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
651 cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::BBR);
652
653 let mut r = Recovery::new(&cfg);
654 let now = Instant::now();
655 let mss = r.max_datagram_size;
656
657 r.on_init();
658
659 let mut pn = 0;
660
661 // At 4th roundtrip, filled_pipe=true and switch to Drain,
662 // but move to ProbeBW immediately because bytes_in_flight is
663 // smaller than BBRInFlight(1).
664 for _ in 0..4 {
665 let pkt = Sent {
666 pkt_num: pn,
667 frames: smallvec![],
668 time_sent: now,
669 time_acked: None,
670 time_lost: None,
671 size: mss,
672 ack_eliciting: true,
673 in_flight: true,
674 delivered: r.delivery_rate.delivered(),
675 delivered_time: now,
676 first_sent_time: now,
677 is_app_limited: false,
678 has_data: false,
679 };
680
681 r.on_packet_sent(
682 pkt,
683 packet::Epoch::Application,
684 HandshakeStatus::default(),
685 now,
686 "",
687 );
688
689 pn += 1;
690
691 let rtt = Duration::from_millis(50);
692 let now = now + rtt;
693
694 let mut acked = ranges::RangeSet::default();
695 acked.insert(0..pn);
696
697 assert_eq!(
698 r.on_ack_received(
699 &acked,
700 25,
701 packet::Epoch::Application,
702 HandshakeStatus::default(),
703 now,
704 "",
705 ),
706 Ok((0, 0)),
707 );
708 }
709
710 // Now we are in ProbeBW state.
711 assert_eq!(r.bbr_state.filled_pipe, true);
712 assert_eq!(r.bbr_state.state, BBRStateMachine::ProbeBW);
713
714 // In the first ProbeBW cycle, pacing_gain should be >= 1.0.
715 assert!(r.bbr_state.pacing_gain >= 1.0);
716 }
717
718 #[test]
bbr_probe_rtt()719 fn bbr_probe_rtt() {
720 let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
721 cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::BBR);
722
723 let mut r = Recovery::new(&cfg);
724 let now = Instant::now();
725 let mss = r.max_datagram_size;
726
727 r.on_init();
728
729 let mut pn = 0;
730
731 // At 4th roundtrip, filled_pipe=true and switch to Drain,
732 // but move to ProbeBW immediately because bytes_in_flight is
733 // smaller than BBRInFlight(1).
734 for _ in 0..4 {
735 let pkt = Sent {
736 pkt_num: pn,
737 frames: smallvec![],
738 time_sent: now,
739 time_acked: None,
740 time_lost: None,
741 size: mss,
742 ack_eliciting: true,
743 in_flight: true,
744 delivered: r.delivery_rate.delivered(),
745 delivered_time: now,
746 first_sent_time: now,
747 is_app_limited: false,
748 has_data: false,
749 };
750
751 r.on_packet_sent(
752 pkt,
753 packet::Epoch::Application,
754 HandshakeStatus::default(),
755 now,
756 "",
757 );
758
759 pn += 1;
760
761 let rtt = Duration::from_millis(50);
762 let now = now + rtt;
763
764 let mut acked = ranges::RangeSet::default();
765 acked.insert(0..pn);
766
767 assert_eq!(
768 r.on_ack_received(
769 &acked,
770 25,
771 packet::Epoch::Application,
772 HandshakeStatus::default(),
773 now,
774 "",
775 ),
776 Ok((0, 0)),
777 );
778 }
779
780 // Now we are in ProbeBW state.
781 assert_eq!(r.bbr_state.state, BBRStateMachine::ProbeBW);
782
783 // After RTPROP_FILTER_LEN (10s), switch to ProbeRTT.
784 let now = now + RTPROP_FILTER_LEN;
785
786 let pkt = Sent {
787 pkt_num: pn,
788 frames: smallvec![],
789 time_sent: now,
790 time_acked: None,
791 time_lost: None,
792 size: mss,
793 ack_eliciting: true,
794 in_flight: true,
795 delivered: r.delivery_rate.delivered(),
796 delivered_time: now,
797 first_sent_time: now,
798 is_app_limited: false,
799 has_data: false,
800 };
801
802 r.on_packet_sent(
803 pkt,
804 packet::Epoch::Application,
805 HandshakeStatus::default(),
806 now,
807 "",
808 );
809
810 pn += 1;
811
812 // Don't update rtprop by giving larger rtt than before.
813 // If rtprop is updated, rtprop expiry check is reset.
814 let rtt = Duration::from_millis(100);
815 let now = now + rtt;
816
817 let mut acked = ranges::RangeSet::default();
818 acked.insert(0..pn);
819
820 assert_eq!(
821 r.on_ack_received(
822 &acked,
823 25,
824 packet::Epoch::Application,
825 HandshakeStatus::default(),
826 now,
827 "",
828 ),
829 Ok((0, 0)),
830 );
831
832 assert_eq!(r.bbr_state.state, BBRStateMachine::ProbeRTT);
833 assert_eq!(r.bbr_state.pacing_gain, 1.0);
834 }
835 }
836
837 mod init;
838 mod pacing;
839 mod per_ack;
840 mod per_transmit;
841