quiche/recovery/gcongestion/bbr2/
mode.rs1use 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 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}