Skip to main content

quiche/recovery/gcongestion/
recovery.rs

1use crate::packet;
2use crate::recovery::OnLossDetectionTimeoutOutcome;
3use crate::recovery::INITIAL_TIME_THRESHOLD_OVERHEAD;
4use crate::recovery::TIME_THRESHOLD_OVERHEAD_MULTIPLIER;
5use crate::Error;
6use crate::Result;
7
8use std::collections::VecDeque;
9use std::time::Duration;
10use std::time::Instant;
11
12use smallvec::SmallVec;
13
14#[cfg(feature = "qlog")]
15use qlog::events::EventData;
16
17#[cfg(feature = "qlog")]
18use crate::recovery::QlogMetrics;
19
20use crate::frame;
21
22use crate::recovery::bytes_in_flight::BytesInFlight;
23use crate::recovery::gcongestion::Bandwidth;
24use crate::recovery::rtt::RttStats;
25use crate::recovery::CongestionControlAlgorithm;
26use crate::recovery::HandshakeStatus;
27use crate::recovery::LossDetectionTimer;
28use crate::recovery::OnAckReceivedOutcome;
29use crate::recovery::RangeSet;
30use crate::recovery::RecoveryConfig;
31use crate::recovery::RecoveryOps;
32use crate::recovery::RecoveryStats;
33use crate::recovery::ReleaseDecision;
34use crate::recovery::Sent;
35use crate::recovery::StartupExit;
36use crate::recovery::GRANULARITY;
37use crate::recovery::INITIAL_PACKET_THRESHOLD;
38use crate::recovery::INITIAL_TIME_THRESHOLD;
39use crate::recovery::MAX_OUTSTANDING_NON_ACK_ELICITING;
40use crate::recovery::MAX_PACKET_THRESHOLD;
41use crate::recovery::MAX_PTO_PROBES_COUNT;
42use crate::recovery::PACKET_REORDER_TIME_THRESHOLD;
43
44use super::bbr2::BBRv2;
45use super::pacer::Pacer;
46use super::Acked;
47use super::Lost;
48
49// Congestion Control
50const MAX_WINDOW_PACKETS: usize = 20_000;
51
52#[derive(Debug)]
53struct SentPacket {
54    pkt_num: u64,
55    status: SentStatus,
56}
57
58#[derive(Debug)]
59enum SentStatus {
60    Sent {
61        time_sent: Instant,
62        ack_eliciting: bool,
63        in_flight: bool,
64        has_data: bool,
65        is_pmtud_probe: bool,
66        sent_bytes: usize,
67        frames: SmallVec<[frame::Frame; 1]>,
68    },
69    Acked,
70    Lost,
71}
72
73impl SentStatus {
74    fn ack(&mut self) -> Self {
75        std::mem::replace(self, SentStatus::Acked)
76    }
77
78    fn lose(&mut self) -> Self {
79        if !matches!(self, SentStatus::Acked) {
80            std::mem::replace(self, SentStatus::Lost)
81        } else {
82            SentStatus::Acked
83        }
84    }
85}
86
87#[derive(Default)]
88struct RecoveryEpoch {
89    /// The time the most recent ack-eliciting packet was sent.
90    time_of_last_ack_eliciting_packet: Option<Instant>,
91
92    /// The largest packet number acknowledged in the packet number space so
93    /// far.
94    largest_acked_packet: Option<u64>,
95
96    /// The time at which the next packet in that packet number space can be
97    /// considered lost based on exceeding the reordering window in time.
98    loss_time: Option<Instant>,
99
100    /// An association of packet numbers in a packet number space to information
101    /// about them.
102    sent_packets: VecDeque<SentPacket>,
103
104    loss_probes: usize,
105    pkts_in_flight: usize,
106
107    acked_frames: VecDeque<frame::Frame>,
108
109    // Frames scheduled for retransmission due to PTO are tracked
110    // separately so we can check that frames were drained before
111    // generating more PTO probes.
112    lost_frames_ack: VecDeque<frame::Frame>,
113    lost_frames_pto: VecDeque<frame::Frame>,
114
115    /// The largest packet number sent in the packet number space so far.
116    #[allow(dead_code)]
117    test_largest_sent_pkt_num_on_path: Option<u64>,
118}
119
120struct AckedDetectionResult {
121    acked_bytes: usize,
122    spurious_losses: usize,
123    spurious_pkt_thresh: Option<u64>,
124    has_ack_eliciting: bool,
125}
126
127struct LossDetectionResult {
128    lost_bytes: usize,
129    lost_packets: usize,
130
131    pmtud_lost_bytes: usize,
132    pmtud_lost_packets: SmallVec<[u64; 1]>,
133}
134
135impl RecoveryEpoch {
136    /// Discard the Epoch state and return the total size of unacked packets
137    /// that were discarded
138    fn discard(&mut self, cc: &mut Pacer) -> usize {
139        let unacked_bytes = self
140            .sent_packets
141            .drain(..)
142            .map(|p| {
143                if let SentPacket {
144                    status:
145                        SentStatus::Sent {
146                            in_flight,
147                            sent_bytes,
148                            ..
149                        },
150                    pkt_num,
151                } = p
152                {
153                    cc.on_packet_neutered(pkt_num);
154                    if in_flight {
155                        return sent_bytes;
156                    }
157                }
158                0
159            })
160            .sum();
161
162        std::mem::take(&mut self.sent_packets);
163        self.clear_lost_frames();
164        std::mem::take(&mut self.acked_frames);
165        self.time_of_last_ack_eliciting_packet = None;
166        self.loss_time = None;
167        self.loss_probes = 0;
168        self.pkts_in_flight = 0;
169
170        unacked_bytes
171    }
172
173    // `peer_sent_ack_ranges` should not be used without validation.
174    fn detect_and_remove_acked_packets(
175        &mut self, peer_sent_ack_ranges: &RangeSet, newly_acked: &mut Vec<Acked>,
176        skip_pn: Option<u64>, trace_id: &str,
177    ) -> Result<AckedDetectionResult> {
178        newly_acked.clear();
179
180        let mut acked_bytes = 0;
181        let mut spurious_losses = 0;
182        let mut spurious_pkt_thresh = None;
183        let mut has_ack_eliciting = false;
184
185        let largest_ack_received = peer_sent_ack_ranges.last().unwrap();
186        let largest_acked = self
187            .largest_acked_packet
188            .unwrap_or(0)
189            .max(largest_ack_received);
190
191        for peer_sent_range in peer_sent_ack_ranges.iter() {
192            if skip_pn.is_some_and(|skip_pn| peer_sent_range.contains(&skip_pn)) {
193                // https://www.rfc-editor.org/rfc/rfc9000#section-13.1
194                // An endpoint SHOULD treat receipt of an acknowledgment
195                // for a packet it did not send as
196                // a connection error of type PROTOCOL_VIOLATION
197                return Err(Error::OptimisticAckDetected);
198            }
199
200            // Because packets always have incrementing numbers, they are always
201            // in sorted order.
202            let start = if self
203                .sent_packets
204                .front()
205                .filter(|e| e.pkt_num >= peer_sent_range.start)
206                .is_some()
207            {
208                // Usually it will be the first packet.
209                0
210            } else {
211                self.sent_packets
212                    .binary_search_by_key(&peer_sent_range.start, |p| p.pkt_num)
213                    .unwrap_or_else(|e| e)
214            };
215
216            for SentPacket { pkt_num, status } in
217                self.sent_packets.range_mut(start..)
218            {
219                if *pkt_num < peer_sent_range.end {
220                    match status.ack() {
221                        SentStatus::Sent {
222                            time_sent,
223                            in_flight,
224                            sent_bytes,
225                            frames,
226                            ack_eliciting,
227                            ..
228                        } => {
229                            if in_flight {
230                                self.pkts_in_flight -= 1;
231                                acked_bytes += sent_bytes;
232                            }
233                            newly_acked.push(Acked {
234                                pkt_num: *pkt_num,
235                                time_sent,
236                            });
237
238                            self.acked_frames.extend(frames);
239
240                            has_ack_eliciting |= ack_eliciting;
241
242                            trace!("{trace_id} packet newly acked {pkt_num}");
243                        },
244
245                        SentStatus::Acked => {},
246                        SentStatus::Lost => {
247                            // An acked packet was already declared lost
248                            spurious_losses += 1;
249                            spurious_pkt_thresh
250                                .get_or_insert(largest_acked - *pkt_num + 1);
251                        },
252                    }
253                } else {
254                    break;
255                }
256            }
257        }
258
259        self.drain_acked_and_lost_packets();
260
261        Ok(AckedDetectionResult {
262            acked_bytes,
263            spurious_losses,
264            spurious_pkt_thresh,
265            has_ack_eliciting,
266        })
267    }
268
269    fn detect_and_remove_lost_packets(
270        &mut self, loss_delay: Duration, pkt_thresh: Option<u64>, now: Instant,
271        newly_lost: &mut Vec<Lost>,
272    ) -> LossDetectionResult {
273        newly_lost.clear();
274        let mut lost_bytes = 0;
275        self.loss_time = None;
276
277        let lost_send_time = now.checked_sub(loss_delay).unwrap();
278        let largest_acked = self.largest_acked_packet.unwrap_or(0);
279        let mut pmtud_lost_bytes = 0;
280        let mut pmtud_lost_packets = SmallVec::new();
281
282        for SentPacket { pkt_num, status } in &mut self.sent_packets {
283            if *pkt_num > largest_acked {
284                break;
285            }
286
287            if let SentStatus::Sent { time_sent, .. } = status {
288                let loss_by_time = *time_sent <= lost_send_time;
289                let loss_by_pkt = match pkt_thresh {
290                    Some(pkt_thresh) => largest_acked >= *pkt_num + pkt_thresh,
291                    None => false,
292                };
293
294                if loss_by_time || loss_by_pkt {
295                    if let SentStatus::Sent {
296                        in_flight,
297                        sent_bytes,
298                        frames,
299                        is_pmtud_probe,
300                        ..
301                    } = status.lose()
302                    {
303                        self.lost_frames_ack.extend(frames);
304
305                        if in_flight {
306                            self.pkts_in_flight -= 1;
307
308                            if is_pmtud_probe {
309                                pmtud_lost_bytes += sent_bytes;
310                                pmtud_lost_packets.push(*pkt_num);
311                                // Do not track PMTUD probes losses
312                                continue;
313                            }
314
315                            lost_bytes += sent_bytes;
316                        }
317
318                        newly_lost.push(Lost {
319                            packet_number: *pkt_num,
320                            bytes_lost: sent_bytes,
321                        });
322                    }
323                } else {
324                    self.loss_time = Some(*time_sent + loss_delay);
325                    break;
326                }
327            }
328        }
329
330        LossDetectionResult {
331            lost_bytes,
332            lost_packets: newly_lost.len(),
333
334            pmtud_lost_bytes,
335            pmtud_lost_packets,
336        }
337    }
338
339    /// Remove packets that were already handled from the front of the queue,
340    /// but avoid removing packets from the middle of the queue to avoid
341    /// compaction
342    fn drain_acked_and_lost_packets(&mut self) {
343        while let Some(SentPacket {
344            status: SentStatus::Acked | SentStatus::Lost,
345            ..
346        }) = self.sent_packets.front()
347        {
348            self.sent_packets.pop_front();
349        }
350    }
351
352    fn least_unacked(&self) -> u64 {
353        for pkt in self.sent_packets.iter() {
354            if let SentPacket {
355                pkt_num,
356                status: SentStatus::Sent { .. },
357            } = pkt
358            {
359                return *pkt_num;
360            }
361        }
362
363        self.largest_acked_packet.unwrap_or(0) + 1
364    }
365
366    /// Returns the next lost frame, trying ACK-based lost frames first,
367    /// then PTO-based lost frames.
368    fn next_lost_frame(&mut self) -> Option<frame::Frame> {
369        self.lost_frames_ack
370            .pop_front()
371            .or_else(|| self.lost_frames_pto.pop_front())
372    }
373
374    /// Returns true if there are any lost frames (ACK or PTO).
375    fn has_lost_frames(&self) -> bool {
376        !self.lost_frames_ack.is_empty() || !self.lost_frames_pto.is_empty()
377    }
378
379    /// Returns the total count of lost frames (ACK + PTO).
380    #[cfg(test)]
381    fn lost_frames_count(&self) -> usize {
382        self.lost_frames_ack.len() + self.lost_frames_pto.len()
383    }
384
385    /// Clears all lost frames (both ACK and PTO).
386    fn clear_lost_frames(&mut self) {
387        self.lost_frames_ack.clear();
388        self.lost_frames_pto.clear();
389    }
390}
391
392struct LossThreshold {
393    pkt_thresh: Option<u64>,
394    time_thresh: f64,
395
396    // # Experiment: enable_relaxed_loss_threshold
397    //
398    // If `Some` this will disable pkt_thresh on the first loss and then double
399    // time_thresh on subsequent loss.
400    //
401    // The actual threshold is calcualted as `1.0 +
402    // INITIAL_TIME_THRESHOLD_OVERHEAD` and equivalent to the initial value
403    // of INITIAL_TIME_THRESHOLD.
404    time_thresh_overhead: Option<f64>,
405}
406
407impl LossThreshold {
408    fn new(recovery_config: &RecoveryConfig) -> Self {
409        let time_thresh_overhead =
410            if recovery_config.enable_relaxed_loss_threshold {
411                Some(INITIAL_TIME_THRESHOLD_OVERHEAD)
412            } else {
413                None
414            };
415        LossThreshold {
416            pkt_thresh: Some(INITIAL_PACKET_THRESHOLD),
417            time_thresh: INITIAL_TIME_THRESHOLD,
418            time_thresh_overhead,
419        }
420    }
421
422    fn pkt_thresh(&self) -> Option<u64> {
423        self.pkt_thresh
424    }
425
426    fn time_thresh(&self) -> f64 {
427        self.time_thresh
428    }
429
430    fn on_spurious_loss(&mut self, new_pkt_thresh: u64) {
431        match &mut self.time_thresh_overhead {
432            Some(time_thresh_overhead) => {
433                if self.pkt_thresh.is_some() {
434                    // Disable packet threshold on first spurious loss.
435                    self.pkt_thresh = None;
436                } else {
437                    // Double time threshold but cap it at `1.0`, which ends up
438                    // being 2x the RTT.
439                    *time_thresh_overhead *= TIME_THRESHOLD_OVERHEAD_MULTIPLIER;
440                    *time_thresh_overhead = time_thresh_overhead.min(1.0);
441
442                    self.time_thresh = 1.0 + *time_thresh_overhead;
443                }
444            },
445            None => {
446                let new_packet_threshold = self
447                    .pkt_thresh
448                    .expect("packet threshold should always be Some when `enable_relaxed_loss_threshold` is false")
449                    .max(new_pkt_thresh.min(MAX_PACKET_THRESHOLD));
450                self.pkt_thresh = Some(new_packet_threshold);
451
452                self.time_thresh = PACKET_REORDER_TIME_THRESHOLD;
453            },
454        }
455    }
456}
457
458pub struct GRecovery {
459    epochs: [RecoveryEpoch; packet::Epoch::count()],
460
461    loss_timer: LossDetectionTimer,
462
463    pto_count: u32,
464
465    rtt_stats: RttStats,
466
467    recovery_stats: RecoveryStats,
468
469    pub lost_count: usize,
470
471    pub lost_spurious_count: usize,
472
473    loss_thresh: LossThreshold,
474
475    bytes_in_flight: BytesInFlight,
476
477    bytes_sent: usize,
478
479    pub bytes_lost: u64,
480
481    max_datagram_size: usize,
482    time_sent_set_to_now: bool,
483
484    #[cfg(feature = "qlog")]
485    qlog_metrics: QlogMetrics,
486
487    #[cfg(feature = "qlog")]
488    qlog_prev_cc_state: &'static str,
489
490    /// How many non-ack-eliciting packets have been sent.
491    outstanding_non_ack_eliciting: usize,
492
493    /// A resusable list of acks.
494    newly_acked: Vec<Acked>,
495
496    /// A [`Vec`] that can be reused for calls of
497    /// [`Self::detect_and_remove_lost_packets`] to avoid allocations
498    lost_reuse: Vec<Lost>,
499
500    pacer: Pacer,
501}
502
503impl GRecovery {
504    #[cfg(feature = "qlog")]
505    fn send_rate(&self) -> Bandwidth {
506        self.pacer.send_rate().unwrap_or(Bandwidth::zero())
507    }
508
509    #[cfg(feature = "qlog")]
510    fn ack_rate(&self) -> Bandwidth {
511        self.pacer.ack_rate().unwrap_or(Bandwidth::zero())
512    }
513
514    pub fn new(recovery_config: &RecoveryConfig) -> Option<Self> {
515        let cc = match recovery_config.cc_algorithm {
516            CongestionControlAlgorithm::Bbr2Gcongestion => BBRv2::new(
517                recovery_config.initial_congestion_window_packets,
518                MAX_WINDOW_PACKETS,
519                recovery_config.max_send_udp_payload_size,
520                recovery_config.initial_rtt,
521                recovery_config.custom_bbr_params.as_ref(),
522            ),
523            _ => return None,
524        };
525
526        Some(Self {
527            epochs: Default::default(),
528            rtt_stats: RttStats::new(
529                recovery_config.initial_rtt,
530                recovery_config.max_ack_delay,
531            ),
532            recovery_stats: RecoveryStats::default(),
533            loss_timer: Default::default(),
534            pto_count: 0,
535
536            lost_count: 0,
537            lost_spurious_count: 0,
538
539            loss_thresh: LossThreshold::new(recovery_config),
540            bytes_in_flight: Default::default(),
541            bytes_sent: 0,
542            bytes_lost: 0,
543
544            max_datagram_size: recovery_config.max_send_udp_payload_size,
545            time_sent_set_to_now: cc.time_sent_set_to_now(),
546
547            #[cfg(feature = "qlog")]
548            qlog_metrics: QlogMetrics::default(),
549
550            #[cfg(feature = "qlog")]
551            qlog_prev_cc_state: "",
552
553            outstanding_non_ack_eliciting: 0,
554
555            pacer: Pacer::new(
556                recovery_config.pacing,
557                cc,
558                recovery_config
559                    .max_pacing_rate
560                    .map(Bandwidth::from_mbits_per_second),
561            ),
562
563            newly_acked: Vec::new(),
564            lost_reuse: Vec::new(),
565        })
566    }
567
568    fn detect_and_remove_lost_packets(
569        &mut self, epoch: packet::Epoch, now: Instant,
570    ) -> (usize, usize) {
571        let loss_delay =
572            self.rtt_stats.loss_delay(self.loss_thresh.time_thresh());
573        let lost = &mut self.lost_reuse;
574
575        let LossDetectionResult {
576            lost_bytes,
577            lost_packets,
578            pmtud_lost_bytes,
579            pmtud_lost_packets,
580        } = self.epochs[epoch].detect_and_remove_lost_packets(
581            loss_delay,
582            self.loss_thresh.pkt_thresh(),
583            now,
584            lost,
585        );
586
587        self.bytes_in_flight
588            .saturating_subtract(lost_bytes + pmtud_lost_bytes, now);
589
590        for pkt in pmtud_lost_packets {
591            self.pacer.on_packet_neutered(pkt);
592        }
593
594        (lost_bytes, lost_packets)
595    }
596
597    fn loss_time_and_space(&self) -> (Option<Instant>, packet::Epoch) {
598        let mut epoch = packet::Epoch::Initial;
599        let mut time = self.epochs[epoch].loss_time;
600
601        // Iterate over all packet number spaces starting from Handshake.
602        for e in [packet::Epoch::Handshake, packet::Epoch::Application] {
603            let new_time = self.epochs[e].loss_time;
604            if time.is_none() || new_time < time {
605                time = new_time;
606                epoch = e;
607            }
608        }
609
610        (time, epoch)
611    }
612
613    fn pto_time_and_space(
614        &self, handshake_status: HandshakeStatus, now: Instant,
615    ) -> (Option<Instant>, packet::Epoch) {
616        let mut duration = self.pto() * 2_u32.saturating_pow(self.pto_count);
617
618        // Arm PTO from now when there are no inflight packets.
619        if self.bytes_in_flight.is_zero() {
620            if handshake_status.has_handshake_keys {
621                return (Some(now + duration), packet::Epoch::Handshake);
622            } else {
623                return (Some(now + duration), packet::Epoch::Initial);
624            }
625        }
626
627        let mut pto_timeout = None;
628        let mut pto_space = packet::Epoch::Initial;
629
630        // Iterate over all packet number spaces.
631        for &e in packet::Epoch::epochs(
632            packet::Epoch::Initial..=packet::Epoch::Application,
633        ) {
634            if self.epochs[e].pkts_in_flight == 0 {
635                continue;
636            }
637
638            if e == packet::Epoch::Application {
639                // Skip Application Data until handshake completes.
640                if !handshake_status.completed {
641                    return (pto_timeout, pto_space);
642                }
643
644                // Include max_ack_delay and backoff for Application Data.
645                duration += self.rtt_stats.max_ack_delay *
646                    2_u32.saturating_pow(self.pto_count);
647            }
648
649            let new_time = self.epochs[e]
650                .time_of_last_ack_eliciting_packet
651                .map(|t| t + duration);
652
653            if pto_timeout.is_none() || new_time < pto_timeout {
654                pto_timeout = new_time;
655                pto_space = e;
656            }
657        }
658
659        (pto_timeout, pto_space)
660    }
661
662    fn set_loss_detection_timer(
663        &mut self, handshake_status: HandshakeStatus, now: Instant,
664    ) {
665        if let (Some(earliest_loss_time), _) = self.loss_time_and_space() {
666            // Time threshold loss detection.
667            self.loss_timer.update(earliest_loss_time);
668            return;
669        }
670
671        if self.bytes_in_flight.is_zero() &&
672            handshake_status.peer_verified_address
673        {
674            self.loss_timer.clear();
675            return;
676        }
677
678        // PTO timer.
679        if let (Some(timeout), _) = self.pto_time_and_space(handshake_status, now)
680        {
681            self.loss_timer.update(timeout);
682        } else {
683            self.loss_timer.clear();
684        }
685    }
686}
687
688impl RecoveryOps for GRecovery {
689    fn lost_count(&self) -> usize {
690        self.lost_count
691    }
692
693    fn bytes_lost(&self) -> u64 {
694        self.bytes_lost
695    }
696
697    fn should_elicit_ack(&self, epoch: packet::Epoch) -> bool {
698        self.epochs[epoch].loss_probes > 0 ||
699            self.outstanding_non_ack_eliciting >=
700                MAX_OUTSTANDING_NON_ACK_ELICITING
701    }
702
703    fn next_acked_frame(&mut self, epoch: packet::Epoch) -> Option<frame::Frame> {
704        self.epochs[epoch].acked_frames.pop_front()
705    }
706
707    fn next_lost_frame(&mut self, epoch: packet::Epoch) -> Option<frame::Frame> {
708        self.epochs[epoch].next_lost_frame()
709    }
710
711    fn get_largest_acked_on_epoch(&self, epoch: packet::Epoch) -> Option<u64> {
712        self.epochs[epoch].largest_acked_packet
713    }
714
715    fn has_lost_frames(&self, epoch: packet::Epoch) -> bool {
716        self.epochs[epoch].has_lost_frames()
717    }
718
719    fn loss_probes(&self, epoch: packet::Epoch) -> usize {
720        self.epochs[epoch].loss_probes
721    }
722
723    #[cfg(test)]
724    fn inc_loss_probes(&mut self, epoch: packet::Epoch) {
725        self.epochs[epoch].loss_probes += 1;
726    }
727
728    #[cfg(test)]
729    fn lost_frames_count(&self, epoch: packet::Epoch) -> usize {
730        self.epochs[epoch].lost_frames_count()
731    }
732
733    fn ping_sent(&mut self, epoch: packet::Epoch) {
734        self.epochs[epoch].loss_probes =
735            self.epochs[epoch].loss_probes.saturating_sub(1);
736    }
737
738    fn on_packet_sent(
739        &mut self, pkt: Sent, epoch: packet::Epoch,
740        handshake_status: HandshakeStatus, now: Instant, trace_id: &str,
741    ) {
742        let time_sent = if self.time_sent_set_to_now {
743            now
744        } else {
745            self.get_next_release_time().time(now).unwrap_or(now)
746        };
747
748        let epoch = &mut self.epochs[epoch];
749
750        let ack_eliciting = pkt.ack_eliciting;
751        let in_flight = pkt.in_flight;
752        let is_pmtud_probe = pkt.is_pmtud_probe;
753        let pkt_num = pkt.pkt_num;
754        let sent_bytes = pkt.size;
755
756        if let Some(SentPacket { pkt_num, .. }) = epoch.sent_packets.back() {
757            assert!(*pkt_num < pkt.pkt_num, "Packet numbers must increase");
758        }
759
760        let status = SentStatus::Sent {
761            time_sent,
762            ack_eliciting,
763            in_flight,
764            is_pmtud_probe,
765            has_data: pkt.has_data,
766            sent_bytes,
767            frames: pkt.frames,
768        };
769
770        #[cfg(test)]
771        {
772            epoch.test_largest_sent_pkt_num_on_path = epoch
773                .test_largest_sent_pkt_num_on_path
774                .max(Some(pkt.pkt_num));
775        }
776
777        epoch.sent_packets.push_back(SentPacket { pkt_num, status });
778
779        if ack_eliciting {
780            epoch.time_of_last_ack_eliciting_packet = Some(time_sent);
781            self.outstanding_non_ack_eliciting = 0;
782        } else {
783            self.outstanding_non_ack_eliciting += 1;
784        }
785
786        if in_flight {
787            self.pacer.on_packet_sent(
788                time_sent,
789                self.bytes_in_flight.get(),
790                pkt_num,
791                sent_bytes,
792                pkt.has_data,
793                &self.rtt_stats,
794            );
795
796            self.bytes_in_flight.add(sent_bytes, now);
797            epoch.pkts_in_flight += 1;
798            self.set_loss_detection_timer(handshake_status, time_sent);
799        }
800
801        self.bytes_sent += sent_bytes;
802
803        trace!("{trace_id} {self:?}");
804    }
805
806    fn get_packet_send_time(&self, now: Instant) -> Instant {
807        self.pacer.get_next_release_time().time(now).unwrap_or(now)
808    }
809
810    // `peer_sent_ack_ranges` should not be used without validation.
811    fn on_ack_received(
812        &mut self, peer_sent_ack_ranges: &RangeSet, ack_delay: u64,
813        epoch: packet::Epoch, handshake_status: HandshakeStatus, now: Instant,
814        skip_pn: Option<u64>, trace_id: &str,
815    ) -> Result<OnAckReceivedOutcome> {
816        let prior_in_flight = self.bytes_in_flight.get();
817
818        let AckedDetectionResult {
819            acked_bytes,
820            spurious_losses,
821            spurious_pkt_thresh,
822            has_ack_eliciting,
823        } = self.epochs[epoch].detect_and_remove_acked_packets(
824            peer_sent_ack_ranges,
825            &mut self.newly_acked,
826            skip_pn,
827            trace_id,
828        )?;
829
830        self.lost_spurious_count += spurious_losses;
831        if let Some(thresh) = spurious_pkt_thresh {
832            self.loss_thresh.on_spurious_loss(thresh);
833        }
834
835        if self.newly_acked.is_empty() {
836            return Ok(OnAckReceivedOutcome {
837                acked_bytes,
838                spurious_losses,
839                ..Default::default()
840            });
841        }
842
843        self.bytes_in_flight.saturating_subtract(acked_bytes, now);
844
845        let largest_newly_acked = self.newly_acked.last().unwrap();
846
847        // Update `largest_acked_packet` based on the validated `newly_acked`
848        // value.
849        let largest_acked_pkt_num = self.epochs[epoch]
850            .largest_acked_packet
851            .unwrap_or(0)
852            .max(largest_newly_acked.pkt_num);
853        self.epochs[epoch].largest_acked_packet = Some(largest_acked_pkt_num);
854
855        // Check if largest packet is newly acked.
856        let update_rtt = largest_newly_acked.pkt_num == largest_acked_pkt_num &&
857            has_ack_eliciting;
858        if update_rtt {
859            let latest_rtt = now - largest_newly_acked.time_sent;
860            self.rtt_stats.update_rtt(
861                latest_rtt,
862                Duration::from_micros(ack_delay),
863                now,
864                handshake_status.completed,
865            );
866        }
867
868        let (lost_bytes, lost_packets) =
869            self.detect_and_remove_lost_packets(epoch, now);
870
871        self.pacer.on_congestion_event(
872            update_rtt,
873            prior_in_flight,
874            self.bytes_in_flight.get(),
875            now,
876            &self.newly_acked,
877            &self.lost_reuse,
878            self.epochs[epoch].least_unacked(),
879            &self.rtt_stats,
880            &mut self.recovery_stats,
881        );
882
883        self.pto_count = 0;
884        self.lost_count += lost_packets;
885
886        self.set_loss_detection_timer(handshake_status, now);
887
888        trace!("{trace_id} {self:?}");
889
890        Ok(OnAckReceivedOutcome {
891            lost_packets,
892            lost_bytes,
893            acked_bytes,
894            spurious_losses,
895        })
896    }
897
898    fn on_loss_detection_timeout(
899        &mut self, handshake_status: HandshakeStatus, now: Instant,
900        trace_id: &str,
901    ) -> OnLossDetectionTimeoutOutcome {
902        let (earliest_loss_time, epoch) = self.loss_time_and_space();
903
904        if earliest_loss_time.is_some() {
905            let prior_in_flight = self.bytes_in_flight.get();
906
907            let (lost_bytes, lost_packets) =
908                self.detect_and_remove_lost_packets(epoch, now);
909
910            self.pacer.on_congestion_event(
911                false,
912                prior_in_flight,
913                self.bytes_in_flight.get(),
914                now,
915                &[],
916                &self.lost_reuse,
917                self.epochs[epoch].least_unacked(),
918                &self.rtt_stats,
919                &mut self.recovery_stats,
920            );
921
922            self.lost_count += lost_packets;
923
924            self.set_loss_detection_timer(handshake_status, now);
925
926            trace!("{trace_id} {self:?}");
927            return OnLossDetectionTimeoutOutcome {
928                lost_packets,
929                lost_bytes,
930            };
931        }
932
933        let epoch = if self.bytes_in_flight.get() > 0 {
934            // Send new data if available, else retransmit old data. If neither
935            // is available, send a single PING frame.
936            let (_, e) = self.pto_time_and_space(handshake_status, now);
937
938            e
939        } else {
940            // Client sends an anti-deadlock packet: Initial is padded to earn
941            // more anti-amplification credit, a Handshake packet proves address
942            // ownership.
943            if handshake_status.has_handshake_keys {
944                packet::Epoch::Handshake
945            } else {
946                packet::Epoch::Initial
947            }
948        };
949
950        self.pto_count += 1;
951
952        let epoch = &mut self.epochs[epoch];
953
954        epoch.loss_probes = MAX_PTO_PROBES_COUNT.min(self.pto_count as usize);
955
956        let sent_packets_iter_limit = if !epoch.lost_frames_pto.is_empty() {
957            // Skip the search for frames to add to PTO probes if frames
958            // added in a prior PTO haven't been processed yet.
959            0
960        } else {
961            usize::MAX
962        };
963
964        // Skip packets that have already been acked or lost, and packets
965        // that don't contain either CRYPTO or STREAM frames and only return as
966        // many packets as the number of probe packets that will be sent.
967        let unacked_frames = epoch
968            .sent_packets
969            .iter()
970            .take(sent_packets_iter_limit)
971            .filter_map(|p| {
972                if let SentStatus::Sent {
973                    has_data: true,
974                    frames,
975                    ..
976                } = &p.status
977                {
978                    Some(frames)
979                } else {
980                    None
981                }
982            })
983            .take(epoch.loss_probes)
984            .flatten()
985            .filter(|f| !matches!(f, frame::Frame::DatagramHeader { .. }));
986
987        // Retransmit the frames from the oldest sent packets on PTO. However
988        // the packets are not actually declared lost (so there is no effect to
989        // congestion control), we just reschedule the data they carried.
990        //
991        // This will also trigger sending an ACK and retransmitting frames like
992        // HANDSHAKE_DONE and MAX_DATA / MAX_STREAM_DATA as well, in addition
993        // to CRYPTO and STREAM, if the original packet carried them.
994        epoch.lost_frames_pto.extend(unacked_frames.cloned());
995
996        self.pacer
997            .on_retransmission_timeout(epoch.has_lost_frames());
998
999        self.set_loss_detection_timer(handshake_status, now);
1000
1001        trace!("{trace_id} {self:?}");
1002        OnLossDetectionTimeoutOutcome {
1003            lost_packets: 0,
1004            lost_bytes: 0,
1005        }
1006    }
1007
1008    fn on_pkt_num_space_discarded(
1009        &mut self, epoch: packet::Epoch, handshake_status: HandshakeStatus,
1010        now: Instant,
1011    ) {
1012        let epoch = &mut self.epochs[epoch];
1013        self.bytes_in_flight
1014            .saturating_subtract(epoch.discard(&mut self.pacer), now);
1015        self.set_loss_detection_timer(handshake_status, now);
1016    }
1017
1018    fn on_path_change(
1019        &mut self, epoch: packet::Epoch, now: Instant, _trace_id: &str,
1020    ) -> (usize, usize) {
1021        let (lost_bytes, lost_packets) =
1022            self.detect_and_remove_lost_packets(epoch, now);
1023
1024        (lost_packets, lost_bytes)
1025    }
1026
1027    fn loss_detection_timer(&self) -> Option<Instant> {
1028        self.loss_timer.time
1029    }
1030
1031    fn cwnd(&self) -> usize {
1032        self.pacer.get_congestion_window()
1033    }
1034
1035    fn cwnd_available(&self) -> usize {
1036        // Ignore cwnd when sending probe packets.
1037        if self.epochs.iter().any(|e| e.loss_probes > 0) {
1038            return usize::MAX;
1039        }
1040
1041        self.cwnd().saturating_sub(self.bytes_in_flight.get())
1042    }
1043
1044    fn rtt(&self) -> Duration {
1045        self.rtt_stats.rtt()
1046    }
1047
1048    fn min_rtt(&self) -> Option<Duration> {
1049        self.rtt_stats.min_rtt()
1050    }
1051
1052    fn max_rtt(&self) -> Option<Duration> {
1053        self.rtt_stats.max_rtt()
1054    }
1055
1056    fn rttvar(&self) -> Duration {
1057        self.rtt_stats.rttvar()
1058    }
1059
1060    fn pto(&self) -> Duration {
1061        let r = &self.rtt_stats;
1062        r.rtt() + (r.rttvar() * 4).max(GRANULARITY)
1063    }
1064
1065    /// The most recent data delivery rate estimate.
1066    fn delivery_rate(&self) -> Bandwidth {
1067        self.pacer.bandwidth_estimate(&self.rtt_stats)
1068    }
1069
1070    fn max_bandwidth(&self) -> Option<Bandwidth> {
1071        Some(self.pacer.max_bandwidth())
1072    }
1073
1074    fn rtt_persistent_jump_count(&self) -> u64 {
1075        self.pacer.rtt_persistent_jump_count()
1076    }
1077
1078    /// Statistics from when a CCA first exited the startup phase.
1079    fn startup_exit(&self) -> Option<StartupExit> {
1080        self.recovery_stats.startup_exit
1081    }
1082
1083    fn max_datagram_size(&self) -> usize {
1084        self.max_datagram_size
1085    }
1086
1087    fn pmtud_update_max_datagram_size(&mut self, new_max_datagram_size: usize) {
1088        self.max_datagram_size = new_max_datagram_size;
1089        self.pacer.update_mss(self.max_datagram_size);
1090    }
1091
1092    fn update_max_datagram_size(&mut self, new_max_datagram_size: usize) {
1093        self.pmtud_update_max_datagram_size(
1094            self.max_datagram_size.min(new_max_datagram_size),
1095        )
1096    }
1097
1098    // FIXME only used by gcongestion
1099    fn on_app_limited(&mut self) {
1100        self.pacer.on_app_limited(self.bytes_in_flight.get())
1101    }
1102
1103    #[cfg(test)]
1104    fn sent_packets_len(&self, epoch: packet::Epoch) -> usize {
1105        self.epochs[epoch].sent_packets.len()
1106    }
1107
1108    #[cfg(test)]
1109    fn in_flight_count(&self, epoch: packet::Epoch) -> usize {
1110        self.epochs[epoch].pkts_in_flight
1111    }
1112
1113    fn bytes_in_flight(&self) -> usize {
1114        self.bytes_in_flight.get()
1115    }
1116
1117    fn bytes_in_flight_duration(&self) -> Duration {
1118        self.bytes_in_flight.get_duration()
1119    }
1120
1121    #[cfg(test)]
1122    fn pacing_rate(&self) -> u64 {
1123        self.pacer
1124            .pacing_rate(self.bytes_in_flight.get(), &self.rtt_stats)
1125            .to_bytes_per_period(Duration::from_secs(1))
1126    }
1127
1128    #[cfg(test)]
1129    fn pto_count(&self) -> u32 {
1130        self.pto_count
1131    }
1132
1133    #[cfg(test)]
1134    fn pkt_thresh(&self) -> Option<u64> {
1135        self.loss_thresh.pkt_thresh()
1136    }
1137
1138    #[cfg(test)]
1139    fn time_thresh(&self) -> f64 {
1140        self.loss_thresh.time_thresh()
1141    }
1142
1143    #[cfg(test)]
1144    fn lost_spurious_count(&self) -> usize {
1145        self.lost_spurious_count
1146    }
1147
1148    #[cfg(test)]
1149    fn detect_lost_packets_for_test(
1150        &mut self, epoch: packet::Epoch, now: Instant,
1151    ) -> (usize, usize) {
1152        let ret = self.detect_and_remove_lost_packets(epoch, now);
1153        self.epochs[epoch].drain_acked_and_lost_packets();
1154        ret
1155    }
1156
1157    #[cfg(test)]
1158    fn largest_sent_pkt_num_on_path(&self, epoch: packet::Epoch) -> Option<u64> {
1159        self.epochs[epoch].test_largest_sent_pkt_num_on_path
1160    }
1161
1162    #[cfg(test)]
1163    fn app_limited(&self) -> bool {
1164        self.pacer.is_app_limited(self.bytes_in_flight.get())
1165    }
1166
1167    // FIXME only used by congestion
1168    fn update_app_limited(&mut self, _v: bool) {
1169        // TODO
1170    }
1171
1172    // FIXME only used by congestion
1173    fn delivery_rate_update_app_limited(&mut self, _v: bool) {
1174        // TODO
1175    }
1176
1177    fn update_max_ack_delay(&mut self, max_ack_delay: Duration) {
1178        self.rtt_stats.max_ack_delay = max_ack_delay;
1179    }
1180
1181    fn get_next_release_time(&self) -> ReleaseDecision {
1182        self.pacer.get_next_release_time()
1183    }
1184
1185    fn gcongestion_enabled(&self) -> bool {
1186        true
1187    }
1188
1189    #[cfg(feature = "qlog")]
1190    fn state_str(&self, _now: Instant) -> &'static str {
1191        self.pacer.state_str()
1192    }
1193
1194    #[cfg(feature = "qlog")]
1195    fn get_updated_qlog_event_data(&mut self) -> Option<EventData> {
1196        let qlog_metrics = QlogMetrics {
1197            min_rtt: *self.rtt_stats.min_rtt,
1198            smoothed_rtt: self.rtt(),
1199            latest_rtt: self.rtt_stats.latest_rtt(),
1200            rttvar: self.rtt_stats.rttvar(),
1201            cwnd: self.cwnd() as u64,
1202            bytes_in_flight: self.bytes_in_flight.get() as u64,
1203            ssthresh: self.pacer.ssthresh(),
1204
1205            pacing_rate: Some(
1206                self.pacer
1207                    .pacing_rate(self.bytes_in_flight.get(), &self.rtt_stats)
1208                    .to_bytes_per_second(),
1209            ),
1210            delivery_rate: Some(self.delivery_rate().to_bytes_per_second()),
1211            send_rate: Some(self.send_rate().to_bytes_per_second()),
1212            ack_rate: Some(self.ack_rate().to_bytes_per_second()),
1213            lost_packets: Some(self.lost_count as u64),
1214            lost_bytes: Some(self.bytes_lost),
1215            pto_count: Some(self.pto_count),
1216        };
1217
1218        self.qlog_metrics.maybe_update(qlog_metrics)
1219    }
1220
1221    #[cfg(feature = "qlog")]
1222    fn get_updated_qlog_cc_state(
1223        &mut self, now: Instant,
1224    ) -> Option<&'static str> {
1225        let cc_state = self.state_str(now);
1226        if cc_state != self.qlog_prev_cc_state {
1227            self.qlog_prev_cc_state = cc_state;
1228            Some(cc_state)
1229        } else {
1230            None
1231        }
1232    }
1233
1234    fn send_quantum(&self) -> usize {
1235        let pacing_rate = self
1236            .pacer
1237            .pacing_rate(self.bytes_in_flight.get(), &self.rtt_stats);
1238
1239        let floor = if pacing_rate < Bandwidth::from_kbits_per_second(1200) {
1240            self.max_datagram_size
1241        } else {
1242            2 * self.max_datagram_size
1243        };
1244
1245        pacing_rate
1246            .to_bytes_per_period(ReleaseDecision::EQUAL_THRESHOLD)
1247            .min(64 * 1024)
1248            .max(floor as u64) as usize
1249    }
1250}
1251
1252impl std::fmt::Debug for GRecovery {
1253    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1254        write!(f, "timer={:?} ", self.loss_detection_timer())?;
1255        write!(f, "rtt_stats={:?} ", self.rtt_stats)?;
1256        write!(f, "bytes_in_flight={} ", self.bytes_in_flight.get())?;
1257        write!(f, "{:?} ", self.pacer)?;
1258        Ok(())
1259    }
1260}
1261
1262#[cfg(test)]
1263mod tests {
1264    use super::*;
1265    use crate::Config;
1266
1267    #[test]
1268    fn loss_threshold() {
1269        let config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1270        let recovery_config = RecoveryConfig::from_config(&config);
1271        assert!(!recovery_config.enable_relaxed_loss_threshold);
1272
1273        let mut loss_thresh = LossThreshold::new(&recovery_config);
1274        assert_eq!(loss_thresh.time_thresh_overhead, None);
1275        assert_eq!(loss_thresh.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
1276        assert_eq!(loss_thresh.time_thresh(), INITIAL_TIME_THRESHOLD);
1277
1278        // First spurious loss.
1279        loss_thresh.on_spurious_loss(INITIAL_PACKET_THRESHOLD);
1280        assert_eq!(loss_thresh.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
1281        assert_eq!(loss_thresh.time_thresh(), PACKET_REORDER_TIME_THRESHOLD);
1282
1283        // Packet gaps < INITIAL_PACKET_THRESHOLD will NOT change packet
1284        // threshold.
1285        for packet_gap in 0..INITIAL_PACKET_THRESHOLD {
1286            loss_thresh.on_spurious_loss(packet_gap);
1287
1288            // Packet threshold only increases once the packet gap increases.
1289            assert_eq!(
1290                loss_thresh.pkt_thresh().unwrap(),
1291                INITIAL_PACKET_THRESHOLD
1292            );
1293            assert_eq!(loss_thresh.time_thresh(), PACKET_REORDER_TIME_THRESHOLD);
1294        }
1295
1296        // Subsequent spurious loss with packet_gaps > INITIAL_PACKET_THRESHOLD.
1297        // Test values much larger than MAX_PACKET_THRESHOLD, i.e.
1298        // `MAX_PACKET_THRESHOLD * 2`
1299        for packet_gap in INITIAL_PACKET_THRESHOLD + 1..MAX_PACKET_THRESHOLD * 2 {
1300            loss_thresh.on_spurious_loss(packet_gap);
1301
1302            // Packet threshold is equal to packet gap beyond
1303            // INITIAL_PACKET_THRESHOLD, but capped
1304            // at MAX_PACKET_THRESHOLD.
1305            let new_packet_threshold = if packet_gap < MAX_PACKET_THRESHOLD {
1306                packet_gap
1307            } else {
1308                MAX_PACKET_THRESHOLD
1309            };
1310            assert_eq!(loss_thresh.pkt_thresh().unwrap(), new_packet_threshold);
1311            assert_eq!(loss_thresh.time_thresh(), PACKET_REORDER_TIME_THRESHOLD);
1312        }
1313        // Packet threshold is capped at MAX_PACKET_THRESHOLD
1314        assert_eq!(loss_thresh.pkt_thresh().unwrap(), MAX_PACKET_THRESHOLD);
1315        assert_eq!(loss_thresh.time_thresh(), PACKET_REORDER_TIME_THRESHOLD);
1316
1317        // Packet threshold is monotonically increasing
1318        loss_thresh.on_spurious_loss(INITIAL_PACKET_THRESHOLD);
1319        assert_eq!(loss_thresh.pkt_thresh().unwrap(), MAX_PACKET_THRESHOLD);
1320        assert_eq!(loss_thresh.time_thresh(), PACKET_REORDER_TIME_THRESHOLD);
1321    }
1322
1323    #[test]
1324    fn relaxed_loss_threshold() {
1325        // The max time threshold when operating in relaxed loss mode.
1326        const MAX_TIME_THRESHOLD: f64 = 2.0;
1327
1328        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1329        config.set_enable_relaxed_loss_threshold(true);
1330        let recovery_config = RecoveryConfig::from_config(&config);
1331        assert!(recovery_config.enable_relaxed_loss_threshold);
1332
1333        let mut loss_thresh = LossThreshold::new(&recovery_config);
1334        assert_eq!(
1335            loss_thresh.time_thresh_overhead,
1336            Some(INITIAL_TIME_THRESHOLD_OVERHEAD)
1337        );
1338        assert_eq!(loss_thresh.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
1339        assert_eq!(loss_thresh.time_thresh(), INITIAL_TIME_THRESHOLD);
1340
1341        // First spurious loss.
1342        loss_thresh.on_spurious_loss(INITIAL_PACKET_THRESHOLD);
1343        assert_eq!(loss_thresh.pkt_thresh(), None);
1344        assert_eq!(loss_thresh.time_thresh(), INITIAL_TIME_THRESHOLD);
1345
1346        // Subsequent spurious loss.
1347        for subsequent_loss_count in 1..100 {
1348            // Double the overhead until it caps at `2.0`.
1349            //
1350            // It takes `3` rounds of doubling for INITIAL_TIME_THRESHOLD_OVERHEAD
1351            // to equal `1.0`.
1352            let new_time_threshold = if subsequent_loss_count <= 3 {
1353                1.0 + INITIAL_TIME_THRESHOLD_OVERHEAD *
1354                    2_f64.powi(subsequent_loss_count as i32)
1355            } else {
1356                2.0
1357            };
1358
1359            loss_thresh.on_spurious_loss(subsequent_loss_count);
1360            assert_eq!(loss_thresh.pkt_thresh(), None);
1361            assert_eq!(loss_thresh.time_thresh(), new_time_threshold);
1362        }
1363        // Time threshold is capped at 2.0.
1364        assert_eq!(loss_thresh.pkt_thresh(), None);
1365        assert_eq!(loss_thresh.time_thresh(), MAX_TIME_THRESHOLD);
1366    }
1367
1368    #[test]
1369    fn test_high_pto_count_no_panic() {
1370        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1371        config.set_cc_algorithm(CongestionControlAlgorithm::Bbr2Gcongestion);
1372        let recovery_config = RecoveryConfig::from_config(&config);
1373        let mut r = GRecovery::new(&recovery_config).unwrap();
1374
1375        r.pto_count = 99999;
1376
1377        let handshake_status = HandshakeStatus {
1378            completed: true,
1379            has_handshake_keys: true,
1380            peer_verified_address: true,
1381        };
1382        let now = Instant::now();
1383
1384        let _ = r.pto_time_and_space(handshake_status, now);
1385    }
1386}