quiche/recovery/congestion/bbr2/
per_loss.rs

1// Copyright (C) 2022, Cloudflare, Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//
11//     * Redistributions in binary form must reproduce the above copyright
12//       notice, this list of conditions and the following disclaimer in the
13//       documentation and/or other materials provided with the distribution.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27use super::*;
28
29// BBR2 Functions on every packet loss event.
30//
31// 4.2.4.  Per-Loss Steps
32pub fn bbr2_update_on_loss(
33    r: &mut Congestion, packet: &Sent, lost_bytes: usize, now: Instant,
34) {
35    bbr2_handle_lost_packet(r, packet, lost_bytes, now);
36}
37
38// 4.5.6.  Updating the Model Upon Packet Loss
39// 4.5.6.2.  Probing for Bandwidth In ProbeBW
40pub fn bbr2_check_inflight_too_high(r: &mut Congestion, now: Instant) -> bool {
41    if bbr2_is_inflight_too_high(r) {
42        if r.bbr2_state.bw_probe_samples {
43            bbr2_handle_inflight_too_high(r, now);
44        }
45
46        // inflight too high.
47        return true;
48    }
49
50    // inflight not too high.
51    false
52}
53
54pub fn bbr2_is_inflight_too_high(r: &mut Congestion) -> bool {
55    r.bbr2_state.lost > (r.bbr2_state.tx_in_flight as f64 * LOSS_THRESH) as usize
56}
57
58fn bbr2_handle_inflight_too_high(r: &mut Congestion, now: Instant) {
59    // Only react once per bw probe.
60    r.bbr2_state.bw_probe_samples = false;
61
62    if !r.delivery_rate.sample_is_app_limited() {
63        r.bbr2_state.inflight_hi = r
64            .bbr2_state
65            .tx_in_flight
66            .max((per_ack::bbr2_target_inflight(r) as f64 * BETA) as usize);
67    }
68
69    if r.bbr2_state.state == BBR2StateMachine::ProbeBWUP {
70        per_ack::bbr2_start_probe_bw_down(r, now);
71    }
72}
73
74fn bbr2_handle_lost_packet(
75    r: &mut Congestion, packet: &Sent, lost_bytes: usize, now: Instant,
76) {
77    if !r.bbr2_state.bw_probe_samples {
78        return;
79    }
80
81    r.bbr2_state.tx_in_flight = packet.tx_in_flight;
82    r.bbr2_state.lost = lost_bytes;
83
84    r.delivery_rate.update_app_limited(packet.is_app_limited);
85
86    if bbr2_is_inflight_too_high(r) {
87        r.bbr2_state.tx_in_flight = bbr2_inflight_hi_from_lost_packet(r, packet);
88
89        bbr2_handle_inflight_too_high(r, now);
90    }
91}
92
93fn bbr2_inflight_hi_from_lost_packet(r: &mut Congestion, packet: &Sent) -> usize {
94    let size = packet.size;
95    let inflight_prev = r.bbr2_state.tx_in_flight - size;
96    let lost_prev = r.bbr2_state.lost - size;
97    let lost_prefix = (LOSS_THRESH * inflight_prev as f64 - lost_prev as f64) /
98        (1.0 - LOSS_THRESH);
99
100    inflight_prev + lost_prefix as usize
101}
102
103// 4.5.6.3.  When not Probing for Bandwidth
104pub fn bbr2_update_latest_delivery_signals(r: &mut Congestion) {
105    let bbr = &mut r.bbr2_state;
106
107    // Near start of ACK processing.
108    bbr.loss_round_start = false;
109    bbr.bw_latest = bbr
110        .bw_latest
111        .max(r.delivery_rate.sample_delivery_rate().to_bytes_per_second());
112    bbr.inflight_latest =
113        bbr.inflight_latest.max(r.delivery_rate.sample_delivered());
114
115    if r.delivery_rate.sample_prior_delivered() >= bbr.loss_round_delivered {
116        bbr.loss_round_delivered = r.delivery_rate.delivered();
117        bbr.loss_round_start = true;
118    }
119}
120
121pub fn bbr2_advance_latest_delivery_signals(r: &mut Congestion) {
122    let bbr = &mut r.bbr2_state;
123
124    // Near end of ACK processing.
125    if bbr.loss_round_start {
126        bbr.bw_latest =
127            r.delivery_rate.sample_delivery_rate().to_bytes_per_second();
128        bbr.inflight_latest = r.delivery_rate.sample_delivered();
129    }
130}
131
132pub fn bbr2_reset_congestion_signals(r: &mut Congestion) {
133    let bbr = &mut r.bbr2_state;
134
135    bbr.loss_in_round = false;
136    bbr.loss_events_in_round = 0;
137    bbr.bw_latest = 0;
138    bbr.inflight_latest = 0;
139}
140
141pub fn bbr2_update_congestion_signals(r: &mut Congestion, packet: &Acked) {
142    // Update congestion state on every ACK.
143    per_ack::bbr2_update_max_bw(r, packet);
144
145    if r.bbr2_state.lost > 0 {
146        r.bbr2_state.loss_in_round = true;
147        r.bbr2_state.loss_events_in_round += 1;
148    }
149
150    if !r.bbr2_state.loss_round_start {
151        // Wait until end of round trip.
152        return;
153    }
154
155    bbr2_adapt_lower_bounds_from_congestion(r);
156
157    r.bbr2_state.loss_in_round = false;
158    r.bbr2_state.loss_events_in_round = 0;
159}
160
161fn bbr2_adapt_lower_bounds_from_congestion(r: &mut Congestion) {
162    // Once per round-trip respond to congestion.
163    if bbr2_is_probing_bw(r) {
164        return;
165    }
166
167    if r.bbr2_state.loss_in_round {
168        bbr2_init_lower_bounds(r);
169        bbr2_loss_lower_bounds(r);
170    }
171}
172
173fn bbr2_init_lower_bounds(r: &mut Congestion) {
174    let bbr = &mut r.bbr2_state;
175
176    // Handle the first congestion episode in this cycle.
177    if bbr.bw_lo == u64::MAX {
178        bbr.bw_lo = bbr.max_bw;
179    }
180
181    if bbr.inflight_lo == usize::MAX {
182        bbr.inflight_lo = r.congestion_window;
183    }
184}
185
186fn bbr2_loss_lower_bounds(r: &mut Congestion) {
187    let bbr = &mut r.bbr2_state;
188
189    // Adjust model once per round based on loss.
190    bbr.bw_lo = bbr.bw_latest.max((bbr.bw_lo as f64 * BETA) as u64);
191    bbr.inflight_lo = bbr
192        .inflight_latest
193        .max((bbr.inflight_lo as f64 * BETA) as usize);
194}
195
196pub fn bbr2_reset_lower_bounds(r: &mut Congestion) {
197    let bbr = &mut r.bbr2_state;
198
199    bbr.bw_lo = u64::MAX;
200    bbr.inflight_lo = usize::MAX;
201}
202
203pub fn bbr2_bound_bw_for_model(r: &mut Congestion) {
204    let bbr = &mut r.bbr2_state;
205
206    bbr.bw = bbr.max_bw.min(bbr.bw_lo.min(bbr.bw_hi));
207}
208
209// This function is not defined in the draft but used.
210fn bbr2_is_probing_bw(r: &mut Congestion) -> bool {
211    let state = r.bbr2_state.state;
212
213    state == BBR2StateMachine::Startup ||
214        state == BBR2StateMachine::ProbeBWREFILL ||
215        state == BBR2StateMachine::ProbeBWUP
216}