quiche/recovery/gcongestion/bbr2/
startup.rs1use std::time::Instant;
32
33use crate::recovery::gcongestion::bbr2::Params;
34use crate::recovery::RecoveryStats;
35use crate::recovery::StartupExit;
36use crate::recovery::StartupExitReason;
37
38use super::mode::Mode;
39use super::mode::ModeImpl;
40use super::network_model::BBRv2NetworkModel;
41use super::Acked;
42use super::BBRv2CongestionEvent;
43use super::Limits;
44use super::Lost;
45
46#[derive(Debug)]
47pub(super) struct Startup {
48 pub(super) model: BBRv2NetworkModel,
49}
50
51impl ModeImpl for Startup {
52 #[cfg(feature = "qlog")]
53 fn state_str(&self) -> &'static str {
54 "bbr_startup"
55 }
56
57 fn is_probing_for_bandwidth(&self) -> bool {
58 true
59 }
60
61 fn on_congestion_event(
62 mut self, _prior_in_flight: usize, event_time: Instant,
63 _acked_packets: &[Acked], _lost_packets: &[Lost],
64 congestion_event: &mut BBRv2CongestionEvent,
65 _target_bytes_inflight: usize, params: &Params,
66 recovery_stats: &mut RecoveryStats, cwnd: usize,
67 ) -> Mode {
68 if self.model.full_bandwidth_reached() {
69 return self.into_drain(event_time, Some(congestion_event), params);
70 }
71
72 if !congestion_event.end_of_round_trip {
73 return Mode::Startup(self);
74 }
75
76 let has_bandwidth_growth =
77 self.model.has_bandwidth_growth(congestion_event, params);
78 if self.model.full_bandwidth_reached() {
79 recovery_stats.set_startup_exit(StartupExit::new(
80 cwnd,
81 Some(self.model.max_bandwidth()),
82 StartupExitReason::BandwidthPlateau,
83 ));
84 }
85
86 let check_persisten_queue =
87 params.max_startup_queue_rounds > 0 && !has_bandwidth_growth;
88 if check_persisten_queue {
89 self.model.check_persistent_queue(1.75, params);
94 if self.model.full_bandwidth_reached() {
95 recovery_stats.set_startup_exit(StartupExit::new(
96 cwnd,
97 Some(self.model.max_bandwidth()),
98 StartupExitReason::PersistentQueue,
99 ));
100 }
101 }
102
103 let check_for_excessive_loss = !congestion_event.last_packet_send_state.is_app_limited &&
107 !has_bandwidth_growth &&
108 !self.model.full_bandwidth_reached();
110
111 if check_for_excessive_loss {
112 self.check_excessive_losses(congestion_event, params);
113
114 if self.model.full_bandwidth_reached() {
115 recovery_stats.set_startup_exit(StartupExit::new(
116 cwnd,
117 Some(self.model.max_bandwidth()),
118 StartupExitReason::Loss,
119 ));
120 }
121 }
122
123 if self.model.full_bandwidth_reached() {
124 self.into_drain(event_time, Some(congestion_event), params)
125 } else {
126 Mode::Startup(self)
127 }
128 }
129
130 fn get_cwnd_limits(&self, _params: &Params) -> Limits<usize> {
131 Limits {
132 lo: 0,
133 hi: self.model.inflight_lo(),
134 }
135 }
136
137 fn on_exit_quiescence(
138 self, _now: Instant, _quiescence_start_time: Instant, _params: &Params,
139 ) -> Mode {
140 Mode::Startup(self)
141 }
142
143 fn enter(
144 &mut self, _now: Instant,
145 _congestion_event: Option<&BBRv2CongestionEvent>, _params: &Params,
146 ) {
147 unreachable!("Enter should never be called for startup")
148 }
149
150 fn leave(
151 &mut self, _now: Instant,
152 _congestion_event: Option<&BBRv2CongestionEvent>,
153 ) {
154 self.model.clear_bandwidth_lo();
156 }
157}
158
159impl Startup {
160 fn into_drain(
161 mut self, now: Instant, congestion_event: Option<&BBRv2CongestionEvent>,
162 params: &Params,
163 ) -> Mode {
164 self.leave(now, congestion_event);
165 let mut next_mode = Mode::drain(self.model);
166 next_mode.enter(now, congestion_event, params);
167 next_mode
168 }
169
170 fn check_excessive_losses(
171 &mut self, congestion_event: &mut BBRv2CongestionEvent, params: &Params,
172 ) {
173 if self.model.is_inflight_too_high(
175 congestion_event,
176 params.startup_full_loss_count,
177 params,
178 ) {
179 let mut new_inflight_hi = self.model.bdp0();
180
181 if params.startup_loss_exit_use_max_delivered_for_inflight_hi {
182 new_inflight_hi = new_inflight_hi
183 .max(self.model.max_bytes_delivered_in_round());
184 }
185
186 self.model.set_inflight_hi(new_inflight_hi);
187 self.model.set_full_bandwidth_reached();
188 }
189 }
190}