quiche/recovery/congestion/
mod.rs1use std::str::FromStr;
28use std::time::Instant;
29
30use super::rtt::RttStats;
31use super::Acked;
32use super::RecoveryConfig;
33use super::Sent;
34
35pub const PACING_MULTIPLIER: f64 = 1.25;
36pub struct Congestion {
37 pub(crate) cc_ops: &'static CongestionControlOps,
39
40 cubic_state: cubic::State,
41
42 pub(crate) hystart: hystart::Hystart,
44
45 pub(crate) pacer: pacer::Pacer,
47
48 pub(crate) prr: prr::PRR,
50
51 send_quantum: usize,
54
55 bbr_state: bbr::State,
57
58 bbr2_state: bbr2::State,
60
61 pub(crate) congestion_window: usize,
62
63 pub(crate) ssthresh: usize,
64
65 bytes_acked_sl: usize,
66
67 bytes_acked_ca: usize,
68
69 pub(crate) congestion_recovery_start_time: Option<Instant>,
70
71 pub(crate) app_limited: bool,
72
73 pub(crate) delivery_rate: delivery_rate::Rate,
74
75 pub(crate) initial_congestion_window_packets: usize,
77
78 max_datagram_size: usize,
79
80 pub(crate) lost_count: usize,
81}
82
83impl Congestion {
84 pub(crate) fn from_config(recovery_config: &RecoveryConfig) -> Self {
85 let initial_congestion_window = recovery_config.max_send_udp_payload_size *
86 recovery_config.initial_congestion_window_packets;
87
88 let mut cc = Congestion {
89 congestion_window: initial_congestion_window,
90
91 ssthresh: usize::MAX,
92
93 bytes_acked_sl: 0,
94
95 bytes_acked_ca: 0,
96
97 congestion_recovery_start_time: None,
98
99 cc_ops: recovery_config.cc_algorithm.into(),
100
101 cubic_state: cubic::State::default(),
102
103 app_limited: false,
104
105 lost_count: 0,
106
107 initial_congestion_window_packets: recovery_config
108 .initial_congestion_window_packets,
109
110 max_datagram_size: recovery_config.max_send_udp_payload_size,
111
112 send_quantum: initial_congestion_window,
113
114 delivery_rate: delivery_rate::Rate::default(),
115
116 hystart: hystart::Hystart::new(recovery_config.hystart),
117
118 pacer: pacer::Pacer::new(
119 recovery_config.pacing,
120 initial_congestion_window,
121 0,
122 recovery_config.max_send_udp_payload_size,
123 recovery_config.max_pacing_rate,
124 ),
125
126 prr: prr::PRR::default(),
127
128 bbr_state: bbr::State::new(),
129
130 bbr2_state: bbr2::State::new(),
131 };
132
133 (cc.cc_ops.on_init)(&mut cc);
134
135 cc
136 }
137
138 pub(crate) fn in_congestion_recovery(&self, sent_time: Instant) -> bool {
139 match self.congestion_recovery_start_time {
140 Some(congestion_recovery_start_time) =>
141 sent_time <= congestion_recovery_start_time,
142
143 None => false,
144 }
145 }
146
147 pub(crate) fn delivery_rate(&self) -> u64 {
148 self.delivery_rate.sample_delivery_rate()
149 }
150
151 pub(crate) fn send_quantum(&self) -> usize {
152 self.send_quantum
153 }
154
155 pub(crate) fn set_pacing_rate(&mut self, rate: u64, now: Instant) {
156 self.pacer.update(self.send_quantum, rate, now);
157 }
158
159 pub(crate) fn congestion_window(&self) -> usize {
160 self.congestion_window
161 }
162
163 fn update_app_limited(&mut self, v: bool) {
164 self.app_limited = v;
165 }
166
167 #[allow(clippy::too_many_arguments)]
168 pub(crate) fn on_packet_sent(
169 &mut self, bytes_in_flight: usize, sent_bytes: usize, now: Instant,
170 pkt: &mut Sent, rtt_stats: &RttStats, bytes_lost: u64, in_flight: bool,
171 ) {
172 if in_flight {
173 self.update_app_limited(
174 (bytes_in_flight + sent_bytes) < self.congestion_window,
175 );
176
177 (self.cc_ops.on_packet_sent)(self, sent_bytes, bytes_in_flight, now);
178
179 self.prr.on_packet_sent(sent_bytes);
180
181 if self.hystart.enabled() && self.congestion_window < self.ssthresh {
183 self.hystart.start_round(pkt.pkt_num);
184 }
185 }
186
187 if !(self.cc_ops.has_custom_pacing)() &&
189 rtt_stats.first_rtt_sample.is_some()
190 {
191 let rate = PACING_MULTIPLIER * self.congestion_window as f64 /
192 rtt_stats.smoothed_rtt.as_secs_f64();
193 self.set_pacing_rate(rate as u64, now);
194 }
195
196 self.schedule_next_packet(now, sent_bytes);
197
198 pkt.time_sent = self.get_packet_send_time();
199
200 self.delivery_rate
202 .on_packet_sent(pkt, bytes_in_flight, bytes_lost);
203 }
204
205 pub(crate) fn on_packets_acked(
206 &mut self, bytes_in_flight: usize, acked: &mut Vec<Acked>,
207 rtt_stats: &RttStats, now: Instant,
208 ) {
209 for pkt in acked.iter() {
211 self.delivery_rate.update_rate_sample(pkt, now);
212 }
213
214 self.delivery_rate.generate_rate_sample(*rtt_stats.min_rtt);
216
217 (self.cc_ops.on_packets_acked)(
219 self,
220 bytes_in_flight,
221 acked,
222 now,
223 rtt_stats,
224 );
225 }
226
227 fn schedule_next_packet(&mut self, now: Instant, packet_size: usize) {
228 let in_initcwnd = self.congestion_window <
233 self.max_datagram_size * self.initial_congestion_window_packets;
234
235 let sent_bytes = if !self.pacer.enabled() || in_initcwnd {
236 0
237 } else {
238 packet_size
239 };
240
241 self.pacer.send(sent_bytes, now);
242 }
243
244 pub(crate) fn get_packet_send_time(&self) -> Instant {
245 self.pacer.next_time()
246 }
247}
248
249#[derive(Debug, Copy, Clone, PartialEq, Eq)]
254#[repr(C)]
255pub enum CongestionControlAlgorithm {
256 Reno = 0,
258 CUBIC = 1,
260 BBR = 2,
262 BBR2 = 3,
264}
265
266impl FromStr for CongestionControlAlgorithm {
267 type Err = crate::Error;
268
269 fn from_str(name: &str) -> std::result::Result<Self, Self::Err> {
273 match name {
274 "reno" => Ok(CongestionControlAlgorithm::Reno),
275 "cubic" => Ok(CongestionControlAlgorithm::CUBIC),
276 "bbr" => Ok(CongestionControlAlgorithm::BBR),
277 "bbr2" => Ok(CongestionControlAlgorithm::BBR2),
278
279 _ => Err(crate::Error::CongestionControl),
280 }
281 }
282}
283
284pub(crate) struct CongestionControlOps {
285 pub on_init: fn(r: &mut Congestion),
286
287 pub on_packet_sent: fn(
288 r: &mut Congestion,
289 sent_bytes: usize,
290 bytes_in_flight: usize,
291 now: Instant,
292 ),
293
294 pub on_packets_acked: fn(
295 r: &mut Congestion,
296 bytes_in_flight: usize,
297 packets: &mut Vec<Acked>,
298 now: Instant,
299 rtt_stats: &RttStats,
300 ),
301
302 pub congestion_event: fn(
303 r: &mut Congestion,
304 bytes_in_flight: usize,
305 lost_bytes: usize,
306 largest_lost_packet: &Sent,
307 now: Instant,
308 ),
309
310 pub checkpoint: fn(r: &mut Congestion),
311
312 pub rollback: fn(r: &mut Congestion) -> bool,
313
314 pub has_custom_pacing: fn() -> bool,
315
316 pub debug_fmt: fn(
317 r: &Congestion,
318 formatter: &mut std::fmt::Formatter,
319 ) -> std::fmt::Result,
320}
321
322impl From<CongestionControlAlgorithm> for &'static CongestionControlOps {
323 fn from(algo: CongestionControlAlgorithm) -> Self {
324 match algo {
325 CongestionControlAlgorithm::Reno => &reno::RENO,
326 CongestionControlAlgorithm::CUBIC => &cubic::CUBIC,
327 CongestionControlAlgorithm::BBR => &bbr::BBR,
328 CongestionControlAlgorithm::BBR2 => &bbr2::BBR2,
329 }
330 }
331}
332
333mod bbr;
334mod bbr2;
335mod cubic;
336mod delivery_rate;
337mod hystart;
338pub(crate) mod pacer;
339mod prr;
340mod reno;
341
342#[cfg(test)]
343mod test_sender;