quiche/recovery/gcongestion/bbr2/
mode.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
31use std::fmt::Debug;
32use std::ops::Deref;
33use std::ops::DerefMut;
34use std::time::Duration;
35use std::time::Instant;
36
37use crate::recovery::gcongestion::bbr2::Params;
38use crate::recovery::gcongestion::Lost;
39use crate::recovery::RecoveryStats;
40
41use super::drain::Drain;
42use super::network_model::BBRv2NetworkModel;
43use super::probe_bw::ProbeBW;
44use super::probe_rtt::ProbeRTT;
45use super::startup::Startup;
46use super::Acked;
47use super::BBRv2CongestionEvent;
48use super::Limits;
49
50#[derive(Debug, Default, PartialEq)]
51pub(super) enum CyclePhase {
52    #[default]
53    NotStarted,
54    Up,
55    Down,
56    Cruise,
57    Refill,
58}
59
60impl CyclePhase {
61    pub(super) fn gain(&self, params: &Params) -> f32 {
62        match self {
63            CyclePhase::Up => params.probe_bw_probe_up_pacing_gain,
64            CyclePhase::Down => params.probe_bw_probe_down_pacing_gain,
65            _ => params.probe_bw_default_pacing_gain,
66        }
67    }
68}
69
70#[derive(Debug)]
71pub(super) struct Cycle {
72    pub(super) start_time: Instant,
73    pub(super) phase: CyclePhase,
74    pub(super) rounds_in_phase: usize,
75    pub(super) phase_start_time: Instant,
76    pub(super) rounds_since_probe: usize,
77    pub(super) probe_wait_time: Option<Duration>,
78    pub(super) probe_up_rounds: usize,
79    pub(super) probe_up_bytes: Option<usize>,
80    pub(super) probe_up_acked: usize,
81    pub(super) probe_up_app_limited_since_inflight_hi_limited: bool,
82    // Whether max bandwidth filter window has advanced in this cycle. It is
83    // advanced once per cycle.
84    pub(super) has_advanced_max_bw: bool,
85    pub(super) is_sample_from_probing: bool,
86
87    pub(super) last_cycle_probed_too_high: bool,
88    pub(super) last_cycle_stopped_risky_probe: bool,
89}
90
91impl Default for Cycle {
92    fn default() -> Self {
93        let now = Instant::now();
94
95        Cycle {
96            start_time: now,
97            phase_start_time: now,
98
99            phase: CyclePhase::NotStarted,
100            rounds_in_phase: 0,
101            rounds_since_probe: 0,
102            probe_wait_time: None,
103            probe_up_rounds: 0,
104            probe_up_bytes: None,
105            probe_up_acked: 0,
106            probe_up_app_limited_since_inflight_hi_limited: false,
107            has_advanced_max_bw: false,
108            is_sample_from_probing: false,
109            last_cycle_probed_too_high: false,
110            last_cycle_stopped_risky_probe: false,
111        }
112    }
113}
114
115#[enum_dispatch::enum_dispatch]
116pub(super) trait ModeImpl: Debug {
117    #[cfg(feature = "qlog")]
118    fn state_str(&self) -> &'static str;
119
120    fn enter(
121        &mut self, now: Instant, congestion_event: Option<&BBRv2CongestionEvent>,
122        params: &Params,
123    );
124
125    fn leave(
126        &mut self, now: Instant, congestion_event: Option<&BBRv2CongestionEvent>,
127    );
128
129    fn is_probing_for_bandwidth(&self) -> bool;
130
131    #[allow(clippy::too_many_arguments)]
132    fn on_congestion_event(
133        self, prior_in_flight: usize, event_time: Instant,
134        acked_packets: &[Acked], lost_packets: &[Lost],
135        congestion_event: &mut BBRv2CongestionEvent,
136        target_bytes_inflight: usize, params: &Params,
137        recovery_stats: &mut RecoveryStats, cwnd: usize,
138    ) -> Mode;
139
140    fn get_cwnd_limits(&self, params: &Params) -> Limits<usize>;
141
142    fn on_exit_quiescence(
143        self, now: Instant, quiescence_start_time: Instant, params: &Params,
144    ) -> Mode;
145}
146
147#[enum_dispatch::enum_dispatch(ModeImpl)]
148#[derive(Debug)]
149pub(super) enum Mode {
150    Startup(Startup),
151    Drain(Drain),
152    ProbeBW(ProbeBW),
153    ProbeRTT(ProbeRTT),
154    Placheolder(Placeholder),
155}
156
157impl Default for Mode {
158    fn default() -> Self {
159        Mode::Placheolder(Placeholder {})
160    }
161}
162
163impl Mode {
164    pub(super) fn startup(model: BBRv2NetworkModel) -> Self {
165        Mode::Startup(Startup { model })
166    }
167
168    pub(super) fn drain(model: BBRv2NetworkModel) -> Self {
169        Mode::Drain(Drain {
170            model,
171            cycle: Default::default(),
172        })
173    }
174
175    pub(super) fn probe_bw(model: BBRv2NetworkModel, cycle: Cycle) -> Self {
176        Mode::ProbeBW(ProbeBW { model, cycle })
177    }
178
179    pub(super) fn probe_rtt(model: BBRv2NetworkModel, cycle: Cycle) -> Self {
180        Mode::ProbeRTT(ProbeRTT::new(model, cycle))
181    }
182
183    #[allow(clippy::too_many_arguments)]
184    pub(super) fn do_on_congestion_event(
185        &mut self, prior_in_flight: usize, event_time: Instant,
186        acked_packets: &[Acked], lost_packets: &[Lost],
187        congestion_event: &mut BBRv2CongestionEvent,
188        target_bytes_inflight: usize, params: &Params,
189        recovery_stats: &mut RecoveryStats, cwnd: usize,
190    ) -> bool {
191        let mode_before = std::mem::discriminant(self);
192
193        *self = std::mem::take(self).on_congestion_event(
194            prior_in_flight,
195            event_time,
196            acked_packets,
197            lost_packets,
198            congestion_event,
199            target_bytes_inflight,
200            params,
201            recovery_stats,
202            cwnd,
203        );
204
205        let mode_after = std::mem::discriminant(self);
206
207        mode_before != mode_after
208    }
209
210    pub(super) fn do_on_exit_quiescence(
211        &mut self, now: Instant, quiescence_start_time: Instant, params: &Params,
212    ) {
213        *self = std::mem::take(self).on_exit_quiescence(
214            now,
215            quiescence_start_time,
216            params,
217        )
218    }
219}
220
221impl Deref for Mode {
222    type Target = BBRv2NetworkModel;
223
224    fn deref(&self) -> &Self::Target {
225        match self {
226            Mode::Startup(Startup { model }) => model,
227            Mode::Drain(Drain { model, .. }) => model,
228            Mode::ProbeBW(ProbeBW { model, .. }) => model,
229            Mode::ProbeRTT(ProbeRTT { model, .. }) => model,
230            Mode::Placheolder(_) => unreachable!(),
231        }
232    }
233}
234
235impl DerefMut for Mode {
236    fn deref_mut(&mut self) -> &mut Self::Target {
237        match self {
238            Mode::Startup(Startup { model }) => model,
239            Mode::Drain(Drain { model, .. }) => model,
240            Mode::ProbeBW(ProbeBW { model, .. }) => model,
241            Mode::ProbeRTT(ProbeRTT { model, .. }) => model,
242            Mode::Placheolder(_) => unreachable!(),
243        }
244    }
245}
246
247#[derive(Debug, Default)]
248pub(super) struct Placeholder {}
249
250impl ModeImpl for Placeholder {
251    #[cfg(feature = "qlog")]
252    fn state_str(&self) -> &'static str {
253        unreachable!()
254    }
255
256    fn enter(
257        &mut self, _: Instant, _: Option<&BBRv2CongestionEvent>, _params: &Params,
258    ) {
259        unreachable!()
260    }
261
262    fn leave(&mut self, _: Instant, _: Option<&BBRv2CongestionEvent>) {
263        unreachable!()
264    }
265
266    fn is_probing_for_bandwidth(&self) -> bool {
267        unreachable!()
268    }
269
270    fn on_congestion_event(
271        self, _: usize, _: Instant, _: &[Acked], _: &[Lost],
272        _: &mut BBRv2CongestionEvent, _: usize, _params: &Params,
273        _recovery_stats: &mut RecoveryStats, _cwnd: usize,
274    ) -> Mode {
275        unreachable!()
276    }
277
278    fn get_cwnd_limits(&self, _params: &Params) -> Limits<usize> {
279        unreachable!()
280    }
281
282    fn on_exit_quiescence(
283        self, _: Instant, _: Instant, _params: &Params,
284    ) -> Mode {
285        unreachable!()
286    }
287}