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 
27 use super::*;
28 use crate::rand;
29 use crate::recovery;
30 
31 use std::cmp;
32 use std::time::Instant;
33 
34 /// 1.2Mbps in bytes/sec
35 const PACING_RATE_1_2MBPS: u64 = 1200 * 1000 / 8;
36 
37 /// 24Mbps in bytes/sec
38 const PACING_RATE_24MBPS: u64 = 24 * 1000 * 1000 / 8;
39 
40 /// The minimal cwnd value BBR tries to target, in bytes
41 #[inline]
bbr_min_pipe_cwnd(r: &mut Recovery) -> usize42 fn bbr_min_pipe_cwnd(r: &mut Recovery) -> usize {
43     BBR_MIN_PIPE_CWND_PKTS * r.max_datagram_size
44 }
45 
46 // BBR Functions when ACK is received.
47 //
bbr_update_model_and_state( r: &mut Recovery, packet: &Acked, now: Instant, )48 pub fn bbr_update_model_and_state(
49     r: &mut Recovery, packet: &Acked, now: Instant,
50 ) {
51     bbr_update_btlbw(r, packet);
52     bbr_check_cycle_phase(r, now);
53     bbr_check_full_pipe(r);
54     bbr_check_drain(r, now);
55     bbr_update_rtprop(r, now);
56     bbr_check_probe_rtt(r, now);
57 }
58 
bbr_update_control_parameters(r: &mut Recovery, now: Instant)59 pub fn bbr_update_control_parameters(r: &mut Recovery, now: Instant) {
60     pacing::bbr_set_pacing_rate(r);
61     bbr_set_send_quantum(r);
62 
63     // Set outgoing packet pacing rate
64     // It is called here because send_quantum may be updated too.
65     r.set_pacing_rate(r.bbr_state.pacing_rate, now);
66 
67     bbr_set_cwnd(r);
68 }
69 
70 // BBR Functions while processing ACKs.
71 //
72 
73 // 4.1.1.5.  Updating the BBR.BtlBw Max Filter
bbr_update_btlbw(r: &mut Recovery, packet: &Acked)74 fn bbr_update_btlbw(r: &mut Recovery, packet: &Acked) {
75     bbr_update_round(r, packet);
76 
77     if r.delivery_rate() >= r.bbr_state.btlbw ||
78         !r.delivery_rate.sample_is_app_limited()
79     {
80         // Since minmax filter is based on time,
81         // start_time + (round_count as seconds) is used instead.
82         r.bbr_state.btlbw = r.bbr_state.btlbwfilter.running_max(
83             BTLBW_FILTER_LEN,
84             r.bbr_state.start_time + Duration::from_secs(r.bbr_state.round_count),
85             r.delivery_rate(),
86         );
87     }
88 }
89 
90 // 4.1.1.3 Tracking Time for the BBR.BtlBw Max Filter
bbr_update_round(r: &mut Recovery, packet: &Acked)91 fn bbr_update_round(r: &mut Recovery, packet: &Acked) {
92     let bbr = &mut r.bbr_state;
93 
94     if packet.delivered >= bbr.next_round_delivered {
95         bbr.next_round_delivered = r.delivery_rate.delivered();
96         bbr.round_count += 1;
97         bbr.round_start = true;
98         bbr.packet_conservation = false;
99     } else {
100         bbr.round_start = false;
101     }
102 }
103 
104 // 4.1.2.3. Updating the BBR.RTprop Min Filter
bbr_update_rtprop(r: &mut Recovery, now: Instant)105 fn bbr_update_rtprop(r: &mut Recovery, now: Instant) {
106     let bbr = &mut r.bbr_state;
107     let rs_rtt = r.delivery_rate.sample_rtt();
108 
109     bbr.rtprop_expired = now > bbr.rtprop_stamp + RTPROP_FILTER_LEN;
110 
111     if !rs_rtt.is_zero() && (rs_rtt <= bbr.rtprop || bbr.rtprop_expired) {
112         bbr.rtprop = rs_rtt;
113         bbr.rtprop_stamp = now;
114     }
115 }
116 
117 // 4.2.2 Send Quantum
bbr_set_send_quantum(r: &mut Recovery)118 fn bbr_set_send_quantum(r: &mut Recovery) {
119     let rate = r.bbr_state.pacing_rate;
120 
121     r.send_quantum = match rate {
122         rate if rate < PACING_RATE_1_2MBPS => r.max_datagram_size,
123 
124         rate if rate < PACING_RATE_24MBPS => 2 * r.max_datagram_size,
125 
126         _ => cmp::min((rate / 1000_u64) as usize, 64 * 1024),
127     }
128 }
129 
130 // 4.2.3.2 Target cwnd
bbr_inflight(r: &mut Recovery, gain: f64) -> usize131 fn bbr_inflight(r: &mut Recovery, gain: f64) -> usize {
132     let bbr = &mut r.bbr_state;
133 
134     if bbr.rtprop == Duration::MAX {
135         return r.max_datagram_size * INITIAL_WINDOW_PACKETS;
136     }
137 
138     let quanta = 3 * r.send_quantum;
139     let estimated_bdp = bbr.btlbw as f64 * bbr.rtprop.as_secs_f64();
140 
141     (gain * estimated_bdp) as usize + quanta
142 }
143 
bbr_update_target_cwnd(r: &mut Recovery)144 fn bbr_update_target_cwnd(r: &mut Recovery) {
145     r.bbr_state.target_cwnd = bbr_inflight(r, r.bbr_state.cwnd_gain);
146 }
147 
148 // 4.2.3.4 Modulating cwnd in Loss Recovery
bbr_save_cwnd(r: &mut Recovery) -> usize149 pub fn bbr_save_cwnd(r: &mut Recovery) -> usize {
150     if !r.bbr_state.in_recovery && r.bbr_state.state != BBRStateMachine::ProbeRTT
151     {
152         r.congestion_window
153     } else {
154         r.congestion_window.max(r.bbr_state.prior_cwnd)
155     }
156 }
157 
bbr_restore_cwnd(r: &mut Recovery)158 pub fn bbr_restore_cwnd(r: &mut Recovery) {
159     r.congestion_window = r.congestion_window.max(r.bbr_state.prior_cwnd);
160 }
161 
bbr_modulate_cwnd_for_recovery(r: &mut Recovery)162 fn bbr_modulate_cwnd_for_recovery(r: &mut Recovery) {
163     let acked_bytes = r.bbr_state.newly_acked_bytes;
164     let lost_bytes = r.bbr_state.newly_lost_bytes;
165 
166     if lost_bytes > 0 {
167         // QUIC mininum cwnd is 2 x MSS.
168         r.congestion_window = r
169             .congestion_window
170             .saturating_sub(lost_bytes)
171             .max(r.max_datagram_size * recovery::MINIMUM_WINDOW_PACKETS);
172     }
173 
174     if r.bbr_state.packet_conservation {
175         r.congestion_window =
176             r.congestion_window.max(r.bytes_in_flight + acked_bytes);
177     }
178 }
179 
180 // 4.2.3.5 Modulating cwnd in ProbeRTT
bbr_modulate_cwnd_for_probe_rtt(r: &mut Recovery)181 fn bbr_modulate_cwnd_for_probe_rtt(r: &mut Recovery) {
182     if r.bbr_state.state == BBRStateMachine::ProbeRTT {
183         r.congestion_window = r.congestion_window.min(bbr_min_pipe_cwnd(r))
184     }
185 }
186 
187 // 4.2.3.6 Core cwnd Adjustment Mechanism
bbr_set_cwnd(r: &mut Recovery)188 fn bbr_set_cwnd(r: &mut Recovery) {
189     let acked_bytes = r.bbr_state.newly_acked_bytes;
190 
191     bbr_update_target_cwnd(r);
192     bbr_modulate_cwnd_for_recovery(r);
193 
194     if !r.bbr_state.packet_conservation {
195         if r.bbr_state.filled_pipe {
196             r.congestion_window = cmp::min(
197                 r.congestion_window + acked_bytes,
198                 r.bbr_state.target_cwnd,
199             )
200         } else if r.congestion_window < r.bbr_state.target_cwnd ||
201             r.delivery_rate.delivered() <
202                 r.max_datagram_size * INITIAL_WINDOW_PACKETS
203         {
204             r.congestion_window += acked_bytes;
205         }
206 
207         r.congestion_window = r.congestion_window.max(bbr_min_pipe_cwnd(r))
208     }
209 
210     bbr_modulate_cwnd_for_probe_rtt(r);
211 }
212 
213 // 4.3.2.2.  Estimating When Startup has Filled the Pipe
bbr_check_full_pipe(r: &mut Recovery)214 fn bbr_check_full_pipe(r: &mut Recovery) {
215     // No need to check for a full pipe now.
216     if r.bbr_state.filled_pipe ||
217         !r.bbr_state.round_start ||
218         r.delivery_rate.sample_is_app_limited()
219     {
220         return;
221     }
222 
223     // BBR.BtlBw still growing?
224     if r.bbr_state.btlbw >=
225         (r.bbr_state.full_bw as f64 * BTLBW_GROWTH_TARGET) as u64
226     {
227         // record new baseline level
228         r.bbr_state.full_bw = r.bbr_state.btlbw;
229         r.bbr_state.full_bw_count = 0;
230         return;
231     }
232 
233     // another round w/o much growth
234     r.bbr_state.full_bw_count += 1;
235 
236     if r.bbr_state.full_bw_count >= 3 {
237         r.bbr_state.filled_pipe = true;
238     }
239 }
240 
241 // 4.3.3.  Drain
bbr_enter_drain(r: &mut Recovery)242 fn bbr_enter_drain(r: &mut Recovery) {
243     let bbr = &mut r.bbr_state;
244 
245     bbr.state = BBRStateMachine::Drain;
246 
247     // pace slowly
248     bbr.pacing_gain = 1.0 / BBR_HIGH_GAIN;
249 
250     // maintain cwnd
251     bbr.cwnd_gain = BBR_HIGH_GAIN;
252 }
253 
bbr_check_drain(r: &mut Recovery, now: Instant)254 fn bbr_check_drain(r: &mut Recovery, now: Instant) {
255     if r.bbr_state.state == BBRStateMachine::Startup && r.bbr_state.filled_pipe {
256         bbr_enter_drain(r);
257     }
258 
259     if r.bbr_state.state == BBRStateMachine::Drain &&
260         r.bytes_in_flight <= bbr_inflight(r, 1.0)
261     {
262         // we estimate queue is drained
263         bbr_enter_probe_bw(r, now);
264     }
265 }
266 
267 // 4.3.4.3.  Gain Cycling Algorithm
bbr_enter_probe_bw(r: &mut Recovery, now: Instant)268 fn bbr_enter_probe_bw(r: &mut Recovery, now: Instant) {
269     let bbr = &mut r.bbr_state;
270 
271     bbr.state = BBRStateMachine::ProbeBW;
272     bbr.pacing_gain = 1.0;
273     bbr.cwnd_gain = 2.0;
274 
275     // cycle_index will be one of (1, 2, 3, 4, 5, 6, 7). Since
276     // bbr_advance_cycle_phase() is called right next and it will
277     // increase cycle_index by 1, the actual cycle_index in the
278     // beginning of ProbeBW will be one of (2, 3, 4, 5, 6, 7, 0)
279     // to avoid index 1 (pacing_gain=3/4). See 4.3.4.2 for details.
280     bbr.cycle_index = BBR_GAIN_CYCLE_LEN -
281         1 -
282         (rand::rand_u64_uniform(BBR_GAIN_CYCLE_LEN as u64 - 1) as usize);
283 
284     bbr_advance_cycle_phase(r, now);
285 }
286 
bbr_check_cycle_phase(r: &mut Recovery, now: Instant)287 fn bbr_check_cycle_phase(r: &mut Recovery, now: Instant) {
288     let bbr = &mut r.bbr_state;
289 
290     if bbr.state == BBRStateMachine::ProbeBW && bbr_is_next_cycle_phase(r, now) {
291         bbr_advance_cycle_phase(r, now);
292     }
293 }
294 
bbr_advance_cycle_phase(r: &mut Recovery, now: Instant)295 fn bbr_advance_cycle_phase(r: &mut Recovery, now: Instant) {
296     let bbr = &mut r.bbr_state;
297 
298     bbr.cycle_stamp = now;
299     bbr.cycle_index = (bbr.cycle_index + 1) % BBR_GAIN_CYCLE_LEN;
300     bbr.pacing_gain = PACING_GAIN_CYCLE[bbr.cycle_index];
301 }
302 
bbr_is_next_cycle_phase(r: &mut Recovery, now: Instant) -> bool303 fn bbr_is_next_cycle_phase(r: &mut Recovery, now: Instant) -> bool {
304     let bbr = &mut r.bbr_state;
305     let lost_bytes = bbr.newly_lost_bytes;
306     let pacing_gain = bbr.pacing_gain;
307     let prior_in_flight = bbr.prior_bytes_in_flight;
308 
309     let is_full_length = (now - bbr.cycle_stamp) > bbr.rtprop;
310 
311     // pacing_gain == 1.0
312     if (pacing_gain - 1.0).abs() < f64::EPSILON {
313         return is_full_length;
314     }
315 
316     if pacing_gain > 1.0 {
317         return is_full_length &&
318             (lost_bytes > 0 ||
319                 prior_in_flight >= bbr_inflight(r, pacing_gain));
320     }
321 
322     is_full_length || prior_in_flight <= bbr_inflight(r, 1.0)
323 }
324 
325 // 4.3.5.  ProbeRTT
bbr_check_probe_rtt(r: &mut Recovery, now: Instant)326 fn bbr_check_probe_rtt(r: &mut Recovery, now: Instant) {
327     if r.bbr_state.state != BBRStateMachine::ProbeRTT &&
328         r.bbr_state.rtprop_expired &&
329         !r.bbr_state.idle_restart
330     {
331         bbr_enter_probe_rtt(r);
332 
333         r.bbr_state.prior_cwnd = bbr_save_cwnd(r);
334         r.bbr_state.probe_rtt_done_stamp = None;
335     }
336 
337     if r.bbr_state.state == BBRStateMachine::ProbeRTT {
338         bbr_handle_probe_rtt(r, now);
339     }
340 
341     r.bbr_state.idle_restart = false;
342 }
343 
bbr_enter_probe_rtt(r: &mut Recovery)344 fn bbr_enter_probe_rtt(r: &mut Recovery) {
345     let bbr = &mut r.bbr_state;
346 
347     bbr.state = BBRStateMachine::ProbeRTT;
348     bbr.pacing_gain = 1.0;
349     bbr.cwnd_gain = 1.0;
350 }
351 
bbr_handle_probe_rtt(r: &mut Recovery, now: Instant)352 fn bbr_handle_probe_rtt(r: &mut Recovery, now: Instant) {
353     // Ignore low rate samples during ProbeRTT.
354     r.delivery_rate.update_app_limited(true);
355 
356     if let Some(probe_rtt_done_stamp) = r.bbr_state.probe_rtt_done_stamp {
357         if r.bbr_state.round_start {
358             r.bbr_state.probe_rtt_round_done = true;
359         }
360 
361         if r.bbr_state.probe_rtt_round_done && now > probe_rtt_done_stamp {
362             r.bbr_state.rtprop_stamp = now;
363 
364             bbr_restore_cwnd(r);
365             bbr_exit_probe_rtt(r, now);
366         }
367     } else if r.bytes_in_flight <= bbr_min_pipe_cwnd(r) {
368         r.bbr_state.probe_rtt_done_stamp = Some(now + PROBE_RTT_DURATION);
369         r.bbr_state.probe_rtt_round_done = false;
370         r.bbr_state.next_round_delivered = r.delivery_rate.delivered();
371     }
372 }
373 
bbr_exit_probe_rtt(r: &mut Recovery, now: Instant)374 fn bbr_exit_probe_rtt(r: &mut Recovery, now: Instant) {
375     if r.bbr_state.filled_pipe {
376         bbr_enter_probe_bw(r, now);
377     } else {
378         init::bbr_enter_startup(r);
379     }
380 }
381