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