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 rtt_jump_detector;
37mod startup;
38
39use std::time::Duration;
40use std::time::Instant;
41
42use network_model::BBRv2NetworkModel;
43
44use crate::recovery::gcongestion::Bandwidth;
45use crate::recovery::RecoveryStats;
46
47use self::mode::Mode;
48use self::mode::ModeImpl;
49
50use super::bbr::SendTimeState;
51use super::Acked;
52use super::BbrBwLoReductionStrategy;
53use super::BbrParams;
54use super::BbrRttJumpDetector;
55use super::CongestionControl;
56use super::Lost;
57use super::RttStats;
58
59const MAX_MODE_CHANGES_PER_CONGESTION_EVENT: usize = 4;
60
61#[derive(Debug)]
62struct Params {
63    // STARTUP parameters.
64    /// The gain for CWND in startup.
65    startup_cwnd_gain: f32,
66
67    startup_pacing_gain: f32,
68
69    /// STARTUP or PROBE_UP are exited if the total bandwidth growth is less
70    /// than `full_bw_threshold` in the last `startup_full_bw_rounds`` round
71    /// trips.
72    full_bw_threshold: f32,
73
74    /// The number of rounds to stay in  STARTUP before exiting due to
75    /// bandwidth plateau.
76    startup_full_bw_rounds: usize,
77
78    /// Number of rounds to stay in STARTUP when there's a sufficient queue that
79    /// bytes_in_flight never drops below the target (1.75 * BDP).  0 indicates
80    /// the feature is disabled and we never exit due to queueing.
81    max_startup_queue_rounds: usize,
82
83    /// The minimum number of loss marking events to exit STARTUP.
84    startup_full_loss_count: usize,
85
86    /// DRAIN parameters.
87    drain_cwnd_gain: f32,
88
89    drain_pacing_gain: f32,
90
91    // PROBE_BW parameters.
92    /// Max number of rounds before probing for Reno-coexistence.
93    probe_bw_probe_max_rounds: usize,
94
95    enable_reno_coexistence: bool,
96
97    /// Multiplier to get Reno-style probe epoch duration as: k * BDP round
98    /// trips. If zero, disables Reno-style BDP-scaled coexistence mechanism.
99    probe_bw_probe_reno_gain: f32,
100
101    /// Minimum duration for BBR-native probes.
102    probe_bw_probe_base_duration: Duration,
103
104    /// The minimum number of loss marking events to exit the PROBE_UP phase.
105    probe_bw_full_loss_count: usize,
106
107    /// Pacing gains.
108    probe_bw_probe_up_pacing_gain: f32,
109    probe_bw_probe_down_pacing_gain: f32,
110    probe_bw_default_pacing_gain: f32,
111
112    /// cwnd_gain for probe bw phases other than ProbeBW_UP
113    probe_bw_cwnd_gain: f32,
114
115    /// cwnd_gain for ProbeBW_UP
116    probe_bw_up_cwnd_gain: f32,
117
118    // PROBE_UP parameters.
119    probe_up_ignore_inflight_hi: bool,
120
121    /// Number of rounds to stay in PROBE_UP when there's a sufficient queue
122    /// that bytes_in_flight never drops below the target.  0 indicates the
123    /// feature is disabled and we never exit due to queueing.
124    // TODO(vlad):
125    max_probe_up_queue_rounds: usize,
126
127    // PROBE_RTT parameters.
128    probe_rtt_inflight_target_bdp_fraction: f32,
129
130    /// The default period for entering PROBE_RTT
131    probe_rtt_period: Duration,
132
133    probe_rtt_duration: Duration,
134
135    probe_rtt_pacing_gain: f32,
136    probe_rtt_cwnd_gain: f32,
137
138    // Parameters used by multiple modes.
139    /// The initial value of the max ack height filter's window length.
140    initial_max_ack_height_filter_window: usize,
141
142    /// The default fraction of unutilized headroom to try to leave in path
143    /// upon high loss.
144    inflight_hi_headroom: f32,
145
146    /// Estimate startup/bw probing has gone too far if loss rate exceeds this.
147    loss_threshold: f32,
148
149    /// A common factor for multiplicative decreases. Used for adjusting
150    /// `bandwidth_lo``, `inflight_lo`` and `inflight_hi`` upon losses.
151    beta: f32,
152
153    // Experimental flags.
154    add_ack_height_to_queueing_threshold: bool,
155
156    /// Don't run PROBE_RTT on the regular schedule
157    avoid_unnecessary_probe_rtt: bool,
158
159    /// When exiting STARTUP due to loss, set `inflight_hi`` to the max of bdp
160    /// and max bytes delivered in round.
161    limit_inflight_hi_by_max_delivered: bool,
162
163    startup_loss_exit_use_max_delivered_for_inflight_hi: bool,
164
165    /// Increase `inflight_hi`` based on delievered, not inflight.
166    use_bytes_delivered_for_inflight_hi: bool,
167
168    /// Set the pacing gain to 25% larger than the recent BW increase in
169    /// STARTUP.
170    decrease_startup_pacing_at_end_of_round: bool,
171
172    /// Avoid Overestimation in Bandwidth Sampler with ack aggregation.
173    /// This is an old experiment that we have found to under-perform the
174    /// algorithm described in the spec.  Use is not recommended.
175    enable_overestimate_avoidance: bool,
176
177    /// If true, apply the fix to A0 point selection logic so the
178    /// implementation is consistent with the behavior of the
179    /// google/quiche implementation.
180    choose_a0_point_fix: bool,
181
182    /// Controls the behavior of BBRAdaptLowerBoundsFromCongestion().
183    bw_lo_mode: BwLoMode,
184
185    /// Determines whether app limited rounds with no bandwidth growth count
186    /// towards the rounds threshold to exit startup.
187    ignore_app_limited_for_no_bandwidth_growth: bool,
188
189    /// Initial pacing rate for a new connection before an RTT
190    /// estimate is available.  This rate serves as an upper bound on
191    /// the initial pacing rate, which is calculated by dividing the
192    /// initial cwnd by the first RTT estimate.
193    initial_pacing_rate_bytes_per_second: Option<u64>,
194
195    /// Lower bound on the congestion window in packets.  If not set,
196    /// the initial congestion window is used as the lower bound.
197    min_cwnd_packets: Option<usize>,
198
199    /// If true, scale the pacing rate when updating mss when doing pmtud.
200    scale_pacing_rate_by_mss: bool,
201
202    /// Disable `has_stayed_long_enough_in_probe_down` which can cause ProbeDown
203    /// to exit early.
204    disable_probe_down_early_exit: bool,
205
206    /// Set the expected send time for packets when using BBR to `now`
207    /// instead of `get_next_release_time()`.  Setting the time based
208    /// on `get_next_release_time()` can result in artificially low
209    /// RTT measurements due to the pacer's use of burst_tokens to
210    /// make up for lost time.  BBR has significant problems when
211    /// minRTT is under estimated, so it is better to have the RTT be
212    /// slightly over estimated.  The pacer can only schedule packets
213    /// 1/8th of an RTT into the future, so the error introduced by
214    /// setting `time_sent` to `now` is bounded.
215    time_sent_set_to_now: bool,
216
217    /// Selects the RTT jump detector implementation.
218    rtt_jump_detector: BbrRttJumpDetector,
219}
220
221impl Params {
222    fn with_overrides(mut self, custom_bbr_settings: &BbrParams) -> Self {
223        macro_rules! apply_override {
224            ($field:ident) => {
225                if let Some(custom_value) = custom_bbr_settings.$field {
226                    self.$field = custom_value;
227                }
228            };
229        }
230
231        macro_rules! apply_optional_override {
232            ($field:ident) => {
233                if let Some(custom_value) = custom_bbr_settings.$field {
234                    self.$field = Some(custom_value);
235                }
236            };
237        }
238
239        apply_override!(startup_cwnd_gain);
240        apply_override!(startup_pacing_gain);
241        apply_override!(full_bw_threshold);
242        apply_override!(startup_full_bw_rounds);
243        apply_override!(startup_full_loss_count);
244        apply_override!(drain_cwnd_gain);
245        apply_override!(drain_pacing_gain);
246        apply_override!(enable_reno_coexistence);
247        apply_override!(enable_overestimate_avoidance);
248        apply_override!(choose_a0_point_fix);
249        apply_override!(probe_bw_probe_up_pacing_gain);
250        apply_override!(probe_bw_probe_down_pacing_gain);
251        apply_override!(probe_bw_cwnd_gain);
252        apply_override!(probe_bw_up_cwnd_gain);
253        apply_override!(probe_rtt_pacing_gain);
254        apply_override!(probe_rtt_cwnd_gain);
255        apply_override!(max_probe_up_queue_rounds);
256        apply_override!(loss_threshold);
257        apply_override!(use_bytes_delivered_for_inflight_hi);
258        apply_override!(decrease_startup_pacing_at_end_of_round);
259        apply_override!(ignore_app_limited_for_no_bandwidth_growth);
260        apply_override!(scale_pacing_rate_by_mss);
261        apply_override!(disable_probe_down_early_exit);
262        apply_override!(time_sent_set_to_now);
263        apply_optional_override!(initial_pacing_rate_bytes_per_second);
264        apply_optional_override!(min_cwnd_packets);
265
266        #[cfg(feature = "internal")]
267        {
268            if let Some(custom_value) = custom_bbr_settings.rtt_jump_detector {
269                self.rtt_jump_detector = custom_value;
270            }
271        }
272
273        if let Some(custom_value) = custom_bbr_settings.bw_lo_reduction_strategy {
274            self.bw_lo_mode = custom_value.into();
275        }
276
277        self
278    }
279}
280
281const DEFAULT_PARAMS: Params = Params {
282    startup_cwnd_gain: 2.0,
283
284    startup_pacing_gain: 2.773,
285
286    full_bw_threshold: 1.25,
287
288    startup_full_bw_rounds: 3,
289
290    max_startup_queue_rounds: 0,
291
292    startup_full_loss_count: 8,
293
294    drain_cwnd_gain: 2.0,
295
296    drain_pacing_gain: 1.0 / 2.885,
297
298    probe_bw_probe_max_rounds: 63,
299
300    enable_reno_coexistence: true,
301
302    probe_bw_probe_reno_gain: 1.0,
303
304    probe_bw_probe_base_duration: Duration::from_millis(2000),
305
306    probe_bw_full_loss_count: 2,
307
308    probe_bw_probe_up_pacing_gain: 1.25,
309
310    probe_bw_probe_down_pacing_gain: 0.9, // BBRv3
311
312    probe_bw_default_pacing_gain: 1.0,
313
314    probe_bw_cwnd_gain: 2.0, // BBRv3
315
316    probe_bw_up_cwnd_gain: 2.25, // BBRv3
317
318    probe_up_ignore_inflight_hi: false,
319
320    max_probe_up_queue_rounds: 2,
321
322    probe_rtt_inflight_target_bdp_fraction: 0.5,
323
324    probe_rtt_period: Duration::from_millis(10000),
325
326    probe_rtt_duration: Duration::from_millis(200),
327
328    probe_rtt_pacing_gain: 1.0,
329
330    probe_rtt_cwnd_gain: 1.0,
331
332    initial_max_ack_height_filter_window: 10,
333
334    inflight_hi_headroom: 0.15,
335
336    loss_threshold: 0.015,
337
338    beta: 0.3,
339
340    add_ack_height_to_queueing_threshold: false,
341
342    avoid_unnecessary_probe_rtt: true,
343
344    limit_inflight_hi_by_max_delivered: true,
345
346    startup_loss_exit_use_max_delivered_for_inflight_hi: true,
347
348    use_bytes_delivered_for_inflight_hi: true,
349
350    decrease_startup_pacing_at_end_of_round: true,
351
352    enable_overestimate_avoidance: false,
353
354    choose_a0_point_fix: false,
355
356    bw_lo_mode: BwLoMode::Default,
357
358    ignore_app_limited_for_no_bandwidth_growth: true,
359
360    initial_pacing_rate_bytes_per_second: None,
361
362    min_cwnd_packets: None,
363
364    scale_pacing_rate_by_mss: false,
365
366    disable_probe_down_early_exit: false,
367
368    time_sent_set_to_now: true,
369
370    rtt_jump_detector: BbrRttJumpDetector::Disabled,
371};
372
373#[derive(Debug, PartialEq)]
374enum BwLoMode {
375    /// Mode that implements the BBRAdaptLowerBoundsFromCongestion()
376    /// behavior described in the BBR RFC draft.
377    Default,
378
379    /// BBRAdaptLowerBoundsFromCongestion experiment that reduces
380    /// bw_lo by bytes_lost/min_rtt.
381    ///
382    /// Not recommended.
383    MinRttReduction,
384
385    /// BBRAdaptLowerBoundsFromCongestion experiment that reduces
386    /// bw_lo by bw_lo * bytes_lost/inflight.
387    ///
388    /// Not recommended.
389    InflightReduction,
390
391    /// BBRAdaptLowerBoundsFromCongestion experiment that reduces
392    /// bw_lo by bw_lo * bytes_lost/cwnd
393    ///
394    /// Not recommended.
395    CwndReduction,
396}
397
398impl From<BbrBwLoReductionStrategy> for BwLoMode {
399    fn from(value: BbrBwLoReductionStrategy) -> Self {
400        match value {
401            BbrBwLoReductionStrategy::Default => BwLoMode::Default,
402            BbrBwLoReductionStrategy::MinRttReduction =>
403                BwLoMode::MinRttReduction,
404            BbrBwLoReductionStrategy::InflightReduction =>
405                BwLoMode::InflightReduction,
406            BbrBwLoReductionStrategy::CwndReduction => BwLoMode::CwndReduction,
407        }
408    }
409}
410
411#[derive(Debug)]
412struct Limits<T: Ord> {
413    lo: T,
414    hi: T,
415}
416
417impl<T: Ord + Clone + Copy> Limits<T> {
418    fn min(&self) -> T {
419        self.lo
420    }
421
422    fn apply_limits(&self, val: T) -> T {
423        val.max(self.lo).min(self.hi)
424    }
425}
426
427impl<T: Ord + Clone + Copy + From<u8>> Limits<T> {
428    pub(crate) fn no_greater_than(val: T) -> Self {
429        Self {
430            lo: T::from(0),
431            hi: val,
432        }
433    }
434}
435
436fn initial_pacing_rate(
437    cwnd_in_bytes: usize, smoothed_rtt: Duration, params: &Params,
438) -> Bandwidth {
439    if let Some(pacing_rate) = params.initial_pacing_rate_bytes_per_second {
440        return Bandwidth::from_bytes_per_second(pacing_rate);
441    }
442
443    Bandwidth::from_bytes_and_time_delta(cwnd_in_bytes, smoothed_rtt) * 2.885
444}
445
446#[derive(Debug)]
447pub(crate) struct BBRv2 {
448    mode: Mode,
449    cwnd: usize,
450    mss: usize,
451
452    pacing_rate: Bandwidth,
453
454    cwnd_limits: Limits<usize>,
455
456    initial_cwnd: usize,
457
458    last_sample_is_app_limited: bool,
459    has_non_app_limited_sample: bool,
460    last_quiescence_start: Option<Instant>,
461    params: Params,
462}
463
464struct BBRv2CongestionEvent {
465    event_time: Instant,
466
467    /// The congestion window prior to the processing of the ack/loss events.
468    prior_cwnd: usize,
469    /// Total bytes inflight before the processing of the ack/loss events.
470    prior_bytes_in_flight: usize,
471
472    /// Total bytes inflight after the processing of the ack/loss events.
473    bytes_in_flight: usize,
474    /// Total bytes acked from acks in this event.
475    bytes_acked: usize,
476    /// Total bytes lost from losses in this event.
477    bytes_lost: usize,
478
479    /// Whether acked_packets indicates the end of a round trip.
480    end_of_round_trip: bool,
481    // When the event happened, whether the sender is probing for bandwidth.
482    is_probing_for_bandwidth: bool,
483
484    // Maximum bandwidth of all bandwidth samples from acked_packets.
485    // This sample may be app-limited, and will be None if there are no newly
486    // acknowledged inflight packets.
487    sample_max_bandwidth: Option<Bandwidth>,
488
489    /// Minimum rtt of all bandwidth samples from acked_packets.
490    /// None if acked_packets is empty.
491    sample_min_rtt: Option<Duration>,
492
493    /// The send state of the largest packet in acked_packets, unless it is
494    /// empty. If acked_packets is empty, it's the send state of the largest
495    /// packet in lost_packets.
496    last_packet_send_state: SendTimeState,
497}
498
499impl BBRv2CongestionEvent {
500    fn new(
501        event_time: Instant, prior_cwnd: usize, prior_bytes_in_flight: usize,
502        is_probing_for_bandwidth: bool,
503    ) -> Self {
504        BBRv2CongestionEvent {
505            event_time,
506            prior_cwnd,
507            prior_bytes_in_flight,
508            is_probing_for_bandwidth,
509            bytes_in_flight: 0,
510            bytes_acked: 0,
511            bytes_lost: 0,
512            end_of_round_trip: false,
513            last_packet_send_state: Default::default(),
514            sample_max_bandwidth: None,
515            sample_min_rtt: None,
516        }
517    }
518}
519
520impl BBRv2 {
521    pub fn new(
522        initial_congestion_window: usize, max_congestion_window: usize,
523        max_segment_size: usize, smoothed_rtt: Duration,
524        custom_bbr_params: Option<&BbrParams>,
525    ) -> Self {
526        let cwnd = initial_congestion_window * max_segment_size;
527
528        let params = if let Some(custom_bbr_settings) = custom_bbr_params {
529            DEFAULT_PARAMS.with_overrides(custom_bbr_settings)
530        } else {
531            DEFAULT_PARAMS
532        };
533
534        let min_cwnd =
535            params.min_cwnd_packets.unwrap_or(initial_congestion_window) *
536                max_segment_size;
537
538        BBRv2 {
539            mode: Mode::startup(BBRv2NetworkModel::new(&params, smoothed_rtt)),
540            cwnd,
541            pacing_rate: initial_pacing_rate(cwnd, smoothed_rtt, &params),
542            cwnd_limits: Limits {
543                lo: min_cwnd,
544                hi: max_congestion_window * max_segment_size,
545            },
546            initial_cwnd: initial_congestion_window * max_segment_size,
547            last_sample_is_app_limited: false,
548            has_non_app_limited_sample: false,
549            last_quiescence_start: None,
550            mss: max_segment_size,
551            params,
552        }
553    }
554
555    pub fn time_sent_set_to_now(&self) -> bool {
556        self.params.time_sent_set_to_now
557    }
558
559    fn on_exit_quiescence(&mut self, now: Instant) {
560        if let Some(last_quiescence_start) = self.last_quiescence_start.take() {
561            self.mode.do_on_exit_quiescence(
562                now,
563                last_quiescence_start,
564                &self.params,
565            )
566        }
567    }
568
569    fn get_target_congestion_window(&self, gain: f32) -> usize {
570        let network_model = self.mode.network_model();
571        network_model
572            .bdp(network_model.bandwidth_estimate(), gain)
573            .max(self.cwnd_limits.min())
574    }
575
576    fn update_pacing_rate(&mut self, bytes_acked: usize) {
577        let network_model = self.mode.network_model();
578        let bandwidth_estimate = match network_model.bandwidth_estimate() {
579            e if e == Bandwidth::zero() => return,
580            e => e,
581        };
582
583        if network_model.total_bytes_acked() == bytes_acked {
584            // After the first ACK, cwnd is still the initial congestion window.
585            self.pacing_rate = Bandwidth::from_bytes_and_time_delta(
586                self.cwnd,
587                network_model.min_rtt(),
588            );
589
590            if let Some(pacing_rate) =
591                self.params.initial_pacing_rate_bytes_per_second
592            {
593                // Do not allow the pacing rate calculated from the first RTT
594                // measurement to be higher than the configured initial pacing
595                // rate.
596                let initial_pacing_rate =
597                    Bandwidth::from_bytes_per_second(pacing_rate);
598                self.pacing_rate = self.pacing_rate.min(initial_pacing_rate);
599            }
600
601            return;
602        }
603
604        let target_rate = bandwidth_estimate * network_model.pacing_gain();
605        if network_model.full_bandwidth_reached() {
606            self.pacing_rate = target_rate;
607            return;
608        }
609
610        if self.params.decrease_startup_pacing_at_end_of_round &&
611            network_model.pacing_gain() < self.params.startup_pacing_gain
612        {
613            self.pacing_rate = target_rate;
614            return;
615        }
616
617        if self.params.bw_lo_mode != BwLoMode::Default &&
618            network_model.loss_events_in_round() > 0
619        {
620            self.pacing_rate = target_rate;
621            return;
622        }
623
624        // By default, the pacing rate never decreases in STARTUP.
625        self.pacing_rate = self.pacing_rate.max(target_rate);
626    }
627
628    fn update_congestion_window(&mut self, bytes_acked: usize) {
629        let network_model = self.mode.network_model();
630        let mut target_cwnd =
631            self.get_target_congestion_window(network_model.cwnd_gain());
632
633        let prior_cwnd = self.cwnd;
634        if network_model.full_bandwidth_reached() {
635            target_cwnd += network_model.max_ack_height();
636            self.cwnd = target_cwnd.min(prior_cwnd + bytes_acked);
637        } else if prior_cwnd < target_cwnd || prior_cwnd < 2 * self.initial_cwnd {
638            self.cwnd = prior_cwnd + bytes_acked;
639        }
640
641        self.cwnd = self
642            .mode
643            .get_cwnd_limits(&self.params)
644            .apply_limits(self.cwnd);
645        self.cwnd = self.cwnd_limits.apply_limits(self.cwnd);
646    }
647
648    fn on_enter_quiescence(&mut self, time: Instant) {
649        self.last_quiescence_start = Some(time);
650    }
651
652    fn target_bytes_inflight(&self) -> usize {
653        let network_model = &self.mode.network_model();
654        let bdp = network_model.bdp1(network_model.bandwidth_estimate());
655        bdp.min(self.get_congestion_window())
656    }
657
658    #[cfg(feature = "qlog")]
659    pub(crate) fn send_rate(&self) -> Option<Bandwidth> {
660        self.mode.network_model().send_rate()
661    }
662
663    #[cfg(feature = "qlog")]
664    pub(crate) fn ack_rate(&self) -> Option<Bandwidth> {
665        self.mode.network_model().ack_rate()
666    }
667
668    pub(crate) fn rtt_persistent_jump_count(&self) -> u64 {
669        self.mode.network_model().rtt_persistent_jump_count()
670    }
671}
672
673impl CongestionControl for BBRv2 {
674    #[cfg(feature = "qlog")]
675    fn state_str(&self) -> &'static str {
676        self.mode.state_str()
677    }
678
679    fn get_congestion_window(&self) -> usize {
680        self.cwnd
681    }
682
683    fn get_congestion_window_in_packets(&self) -> usize {
684        self.cwnd / self.mss
685    }
686
687    fn can_send(&self, bytes_in_flight: usize) -> bool {
688        bytes_in_flight < self.get_congestion_window()
689    }
690
691    fn on_packet_sent(
692        &mut self, sent_time: Instant, bytes_in_flight: usize,
693        packet_number: u64, bytes: usize, is_retransmissible: bool,
694    ) {
695        if bytes_in_flight == 0 && self.params.avoid_unnecessary_probe_rtt {
696            self.on_exit_quiescence(sent_time);
697        }
698
699        let network_model = self.mode.network_model_mut();
700        network_model.on_packet_sent(
701            sent_time,
702            bytes_in_flight,
703            packet_number,
704            bytes,
705            is_retransmissible,
706        );
707    }
708
709    fn on_congestion_event(
710        &mut self, _rtt_updated: bool, prior_in_flight: usize,
711        _bytes_in_flight: usize, event_time: Instant, acked_packets: &[Acked],
712        lost_packets: &[Lost], least_unacked: u64, _rtt_stats: &RttStats,
713        recovery_stats: &mut RecoveryStats,
714    ) {
715        let mut congestion_event = BBRv2CongestionEvent::new(
716            event_time,
717            self.cwnd,
718            prior_in_flight,
719            self.mode.is_probing_for_bandwidth(),
720        );
721
722        let network_model = self.mode.network_model_mut();
723        network_model.on_congestion_event_start(
724            acked_packets,
725            lost_packets,
726            &mut congestion_event,
727            &self.params,
728        );
729
730        // Number of mode changes allowed for this congestion event.
731        let mut mode_changes_allowed = MAX_MODE_CHANGES_PER_CONGESTION_EVENT;
732        while mode_changes_allowed > 0 &&
733            self.mode.do_on_congestion_event(
734                prior_in_flight,
735                event_time,
736                acked_packets,
737                lost_packets,
738                &mut congestion_event,
739                self.target_bytes_inflight(),
740                &self.params,
741                recovery_stats,
742                self.get_congestion_window(),
743            )
744        {
745            mode_changes_allowed -= 1;
746        }
747
748        self.update_pacing_rate(congestion_event.bytes_acked);
749
750        self.update_congestion_window(congestion_event.bytes_acked);
751
752        let network_model = self.mode.network_model_mut();
753        network_model
754            .on_congestion_event_finish(least_unacked, &congestion_event);
755        self.last_sample_is_app_limited =
756            congestion_event.last_packet_send_state.is_app_limited;
757        if !self.last_sample_is_app_limited {
758            self.has_non_app_limited_sample = true;
759        }
760        if congestion_event.bytes_in_flight == 0 &&
761            self.params.avoid_unnecessary_probe_rtt
762        {
763            self.on_enter_quiescence(event_time);
764        }
765    }
766
767    fn on_packet_neutered(&mut self, packet_number: u64) {
768        let network_model = self.mode.network_model_mut();
769        network_model.on_packet_neutered(packet_number);
770    }
771
772    fn on_retransmission_timeout(&mut self, _packets_retransmitted: bool) {}
773
774    fn on_connection_migration(&mut self) {}
775
776    fn is_in_recovery(&self) -> bool {
777        // TODO(vlad): is this true?
778        self.last_quiescence_start.is_none()
779    }
780
781    fn is_cwnd_limited(&self, bytes_in_flight: usize) -> bool {
782        bytes_in_flight >= self.get_congestion_window()
783    }
784
785    fn pacing_rate(
786        &self, _bytes_in_flight: usize, _rtt_stats: &RttStats,
787    ) -> Bandwidth {
788        self.pacing_rate
789    }
790
791    fn bandwidth_estimate(&self, _rtt_stats: &RttStats) -> Bandwidth {
792        let network_model = self.mode.network_model();
793        network_model.bandwidth_estimate()
794    }
795
796    fn max_bandwidth(&self) -> Bandwidth {
797        self.mode.network_model().max_bandwidth()
798    }
799
800    fn update_mss(&mut self, new_mss: usize) {
801        self.cwnd_limits.hi = (self.cwnd_limits.hi as u64 * new_mss as u64 /
802            self.mss as u64) as usize;
803        self.cwnd_limits.lo = (self.cwnd_limits.lo as u64 * new_mss as u64 /
804            self.mss as u64) as usize;
805        self.cwnd =
806            (self.cwnd as u64 * new_mss as u64 / self.mss as u64) as usize;
807        self.initial_cwnd = (self.initial_cwnd as u64 * new_mss as u64 /
808            self.mss as u64) as usize;
809        if self.params.scale_pacing_rate_by_mss {
810            self.pacing_rate =
811                self.pacing_rate * (new_mss as f64 / self.mss as f64);
812        }
813        self.mss = new_mss;
814    }
815
816    fn on_app_limited(&mut self, bytes_in_flight: usize) {
817        if bytes_in_flight >= self.get_congestion_window() {
818            return;
819        }
820
821        let network_model = self.mode.network_model_mut();
822        network_model.on_app_limited()
823    }
824
825    fn limit_cwnd(&mut self, max_cwnd: usize) {
826        self.cwnd_limits.hi = max_cwnd
827    }
828}
829
830#[cfg(test)]
831mod tests {
832    use rstest::rstest;
833
834    use super::*;
835
836    #[rstest]
837    fn update_mss(#[values(false, true)] scale_pacing_rate_by_mss: bool) {
838        const INIT_PACKET_SIZE: usize = 1200;
839        const INIT_WINDOW_PACKETS: usize = 10;
840        const MAX_WINDOW_PACKETS: usize = 10000;
841        const INIT_CWND: usize = INIT_WINDOW_PACKETS * INIT_PACKET_SIZE;
842        const MAX_CWND: usize = MAX_WINDOW_PACKETS * INIT_PACKET_SIZE;
843        let initial_rtt = Duration::from_millis(333);
844        let bbr_params = &BbrParams {
845            scale_pacing_rate_by_mss: Some(scale_pacing_rate_by_mss),
846            ..Default::default()
847        };
848
849        const NEW_PACKET_SIZE: usize = 1450;
850        const NEW_CWND: usize = INIT_WINDOW_PACKETS * NEW_PACKET_SIZE;
851        const NEW_MAX_CWND: usize = MAX_WINDOW_PACKETS * NEW_PACKET_SIZE;
852
853        let mut bbr2 = BBRv2::new(
854            INIT_WINDOW_PACKETS,
855            MAX_WINDOW_PACKETS,
856            INIT_PACKET_SIZE,
857            initial_rtt,
858            Some(bbr_params),
859        );
860
861        assert_eq!(bbr2.cwnd_limits.lo, INIT_CWND);
862        assert_eq!(bbr2.cwnd_limits.hi, MAX_CWND);
863        assert_eq!(bbr2.cwnd, INIT_CWND);
864        assert_eq!(
865            bbr2.pacing_rate.to_bytes_per_period(initial_rtt),
866            (2.88499 * INIT_CWND as f64) as u64
867        );
868
869        bbr2.update_mss(NEW_PACKET_SIZE);
870
871        assert_eq!(bbr2.cwnd_limits.lo, NEW_CWND);
872        assert_eq!(bbr2.cwnd_limits.hi, NEW_MAX_CWND);
873        assert_eq!(bbr2.cwnd, NEW_CWND);
874        let pacing_cwnd = if scale_pacing_rate_by_mss {
875            NEW_CWND
876        } else {
877            INIT_CWND
878        };
879        assert_eq!(
880            bbr2.pacing_rate.to_bytes_per_period(initial_rtt),
881            (2.88499 * pacing_cwnd as f64) as u64
882        );
883    }
884
885    #[rstest]
886    fn min_cwnd_packets_override(
887        #[values(None, Some(4), Some(40))] min_cwnd_packets: Option<usize>,
888    ) {
889        const INIT_PACKET_SIZE: usize = 1200;
890        const INIT_WINDOW_PACKETS: usize = 10;
891        const MAX_WINDOW_PACKETS: usize = 10000;
892        let initial_rtt = Duration::from_millis(333);
893        let bbr_params = &BbrParams {
894            min_cwnd_packets,
895            ..Default::default()
896        };
897
898        let bbr2 = BBRv2::new(
899            INIT_WINDOW_PACKETS,
900            MAX_WINDOW_PACKETS,
901            INIT_PACKET_SIZE,
902            initial_rtt,
903            Some(bbr_params),
904        );
905
906        // If not set, the initial congestion window is the lower bound.
907        let expected_lo =
908            min_cwnd_packets.unwrap_or(INIT_WINDOW_PACKETS) * INIT_PACKET_SIZE;
909        assert_eq!(bbr2.cwnd_limits.lo, expected_lo);
910        assert_eq!(bbr2.cwnd_limits.hi, MAX_WINDOW_PACKETS * INIT_PACKET_SIZE);
911        // The initial cwnd itself is not affected.
912        assert_eq!(bbr2.cwnd, INIT_WINDOW_PACKETS * INIT_PACKET_SIZE);
913    }
914}