Skip to main content

quiche/recovery/gcongestion/
bbr2.rs

1// Copyright (c) 2015 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Copyright (C) 2023, Cloudflare, Inc.
6// All rights reserved.
7//
8// Redistribution and use in source and binary forms, with or without
9// modification, are permitted provided that the following conditions are
10// met:
11//
12//     * Redistributions of source code must retain the above copyright notice,
13//       this list of conditions and the following disclaimer.
14//
15//     * Redistributions in binary form must reproduce the above copyright
16//       notice, this list of conditions and the following disclaimer in the
17//       documentation and/or other materials provided with the distribution.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31mod 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 parameters.
62    /// The gain for CWND in startup.
63    startup_cwnd_gain: f32,
64
65    startup_pacing_gain: f32,
66
67    /// STARTUP or PROBE_UP are exited if the total bandwidth growth is less
68    /// than `full_bw_threshold` in the last `startup_full_bw_rounds`` round
69    /// trips.
70    full_bw_threshold: f32,
71
72    /// The number of rounds to stay in  STARTUP before exiting due to
73    /// bandwidth plateau.
74    startup_full_bw_rounds: usize,
75
76    /// Number of rounds to stay in STARTUP when there's a sufficient queue that
77    /// bytes_in_flight never drops below the target (1.75 * BDP).  0 indicates
78    /// the feature is disabled and we never exit due to queueing.
79    max_startup_queue_rounds: usize,
80
81    /// The minimum number of loss marking events to exit STARTUP.
82    startup_full_loss_count: usize,
83
84    /// DRAIN parameters.
85    drain_cwnd_gain: f32,
86
87    drain_pacing_gain: f32,
88
89    // PROBE_BW parameters.
90    /// Max number of rounds before probing for Reno-coexistence.
91    probe_bw_probe_max_rounds: usize,
92
93    enable_reno_coexistence: bool,
94
95    /// Multiplier to get Reno-style probe epoch duration as: k * BDP round
96    /// trips. If zero, disables Reno-style BDP-scaled coexistence mechanism.
97    probe_bw_probe_reno_gain: f32,
98
99    /// Minimum duration for BBR-native probes.
100    probe_bw_probe_base_duration: Duration,
101
102    /// The minimum number of loss marking events to exit the PROBE_UP phase.
103    probe_bw_full_loss_count: usize,
104
105    /// Pacing gains.
106    probe_bw_probe_up_pacing_gain: f32,
107    probe_bw_probe_down_pacing_gain: f32,
108    probe_bw_default_pacing_gain: f32,
109
110    /// cwnd_gain for probe bw phases other than ProbeBW_UP
111    probe_bw_cwnd_gain: f32,
112
113    /// cwnd_gain for ProbeBW_UP
114    probe_bw_up_cwnd_gain: f32,
115
116    // PROBE_UP parameters.
117    probe_up_ignore_inflight_hi: bool,
118
119    /// Number of rounds to stay in PROBE_UP when there's a sufficient queue
120    /// that bytes_in_flight never drops below the target.  0 indicates the
121    /// feature is disabled and we never exit due to queueing.
122    // TODO(vlad):
123    max_probe_up_queue_rounds: usize,
124
125    // PROBE_RTT parameters.
126    probe_rtt_inflight_target_bdp_fraction: f32,
127
128    /// The default period for entering PROBE_RTT
129    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    // Parameters used by multiple modes.
137    /// The initial value of the max ack height filter's window length.
138    initial_max_ack_height_filter_window: usize,
139
140    /// The default fraction of unutilized headroom to try to leave in path
141    /// upon high loss.
142    inflight_hi_headroom: f32,
143
144    /// Estimate startup/bw probing has gone too far if loss rate exceeds this.
145    loss_threshold: f32,
146
147    /// A common factor for multiplicative decreases. Used for adjusting
148    /// `bandwidth_lo``, `inflight_lo`` and `inflight_hi`` upon losses.
149    beta: f32,
150
151    // Experimental flags.
152    add_ack_height_to_queueing_threshold: bool,
153
154    /// Don't run PROBE_RTT on the regular schedule
155    avoid_unnecessary_probe_rtt: bool,
156
157    /// When exiting STARTUP due to loss, set `inflight_hi`` to the max of bdp
158    /// and max bytes delivered in round.
159    limit_inflight_hi_by_max_delivered: bool,
160
161    startup_loss_exit_use_max_delivered_for_inflight_hi: bool,
162
163    /// Increase `inflight_hi`` based on delievered, not inflight.
164    use_bytes_delivered_for_inflight_hi: bool,
165
166    /// Set the pacing gain to 25% larger than the recent BW increase in
167    /// STARTUP.
168    decrease_startup_pacing_at_end_of_round: bool,
169
170    /// Avoid Overestimation in Bandwidth Sampler with ack aggregation.
171    /// This is an old experiment that we have found to under-perform the
172    /// algorithm described in the spec.  Use is not recommended.
173    enable_overestimate_avoidance: bool,
174
175    /// If true, apply the fix to A0 point selection logic so the
176    /// implementation is consistent with the behavior of the
177    /// google/quiche implementation.
178    choose_a0_point_fix: bool,
179
180    /// Controls the behavior of BBRAdaptLowerBoundsFromCongestion().
181    bw_lo_mode: BwLoMode,
182
183    /// Determines whether app limited rounds with no bandwidth growth count
184    /// towards the rounds threshold to exit startup.
185    ignore_app_limited_for_no_bandwidth_growth: bool,
186
187    /// Initial pacing rate for a new connection before an RTT
188    /// estimate is available.  This rate serves as an upper bound on
189    /// the initial pacing rate, which is calculated by dividing the
190    /// initial cwnd by the first RTT estimate.
191    initial_pacing_rate_bytes_per_second: Option<u64>,
192
193    /// If true, scale the pacing rate when updating mss when doing pmtud.
194    scale_pacing_rate_by_mss: bool,
195
196    /// Disable `has_stayed_long_enough_in_probe_down` which can cause ProbeDown
197    /// to exit early.
198    disable_probe_down_early_exit: bool,
199
200    /// Set the expected send time for packets when using BBR to `now`
201    /// instead of `get_next_release_time()`.  Setting the time based
202    /// on `get_next_release_time()` can result in artificially low
203    /// RTT measurements due to the pacer's use of burst_tokens to
204    /// make up for lost time.  BBR has significant problems when
205    /// minRTT is under estimated, so it is better to have the RTT be
206    /// slightly over estimated.  The pacer can only schedule packets
207    /// 1/8th of an RTT into the future, so the error introduced by
208    /// setting `time_sent` to `now` is bounded.
209    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, // BBRv3
294
295    probe_bw_default_pacing_gain: 1.0,
296
297    probe_bw_cwnd_gain: 2.0, // BBRv3
298
299    probe_bw_up_cwnd_gain: 2.25, // BBRv3
300
301    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    /// Mode that implements the BBRAdaptLowerBoundsFromCongestion()
355    /// behavior described in the BBR RFC draft.
356    Default,
357
358    /// BBRAdaptLowerBoundsFromCongestion experiment that reduces
359    /// bw_lo by bytes_lost/min_rtt.
360    ///
361    /// Not recommended.
362    MinRttReduction,
363
364    /// BBRAdaptLowerBoundsFromCongestion experiment that reduces
365    /// bw_lo by bw_lo * bytes_lost/inflight.
366    ///
367    /// Not recommended.
368    InflightReduction,
369
370    /// BBRAdaptLowerBoundsFromCongestion experiment that reduces
371    /// bw_lo by bw_lo * bytes_lost/cwnd
372    ///
373    /// Not recommended.
374    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    /// The congestion window prior to the processing of the ack/loss events.
447    prior_cwnd: usize,
448    /// Total bytes inflight before the processing of the ack/loss events.
449    prior_bytes_in_flight: usize,
450
451    /// Total bytes inflight after the processing of the ack/loss events.
452    bytes_in_flight: usize,
453    /// Total bytes acked from acks in this event.
454    bytes_acked: usize,
455    /// Total bytes lost from losses in this event.
456    bytes_lost: usize,
457
458    /// Whether acked_packets indicates the end of a round trip.
459    end_of_round_trip: bool,
460    // When the event happened, whether the sender is probing for bandwidth.
461    is_probing_for_bandwidth: bool,
462
463    // Maximum bandwidth of all bandwidth samples from acked_packets.
464    // This sample may be app-limited, and will be None if there are no newly
465    // acknowledged inflight packets.
466    sample_max_bandwidth: Option<Bandwidth>,
467
468    /// Minimum rtt of all bandwidth samples from acked_packets.
469    /// None if acked_packets is empty.
470    sample_min_rtt: Option<Duration>,
471
472    /// The send state of the largest packet in acked_packets, unless it is
473    /// empty. If acked_packets is empty, it's the send state of the largest
474    /// packet in lost_packets.
475    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(&params, smoothed_rtt)),
515            cwnd,
516            pacing_rate: initial_pacing_rate(cwnd, smoothed_rtt, &params),
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            // After the first ACK, cwnd is still the initial congestion window.
560            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                // Do not allow the pacing rate calculated from the first RTT
569                // measurement to be higher than the configured initial pacing
570                // rate.
571                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        // By default, the pacing rate never decreases in STARTUP.
600        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        // Number of mode changes allowed for this congestion event.
702        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        // TODO(vlad): is this true?
749        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}