Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions noq-proto/src/congestion/bbr3/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,12 @@ pub struct Bbr3 {
/// equivalent to BBR.full_bw_reached: A boolean that records whether BBR estimates that it has
/// ever fully utilized its available bandwidth over the lifetime of the connection.
full_bw_reached: bool,
/// Whether the pacing rate has been re-derived from a measured RTT. At construction no SRTT
/// exists, so the initial pacing rate is InitialCwnd / 1 ms (~266 Mbps for a 12 kB window)
/// and, until startup exits, BBRSetPacingRate only ever raises it. A path that stays
/// app-limited never exits startup and would therefore never be paced at all (Linux BBR:
/// `has_seen_rtt` / `bbr_init_pacing_rate_from_rtt`).
has_seen_rtt: bool,
/// equivalent to BBR.full_bw_now: A boolean that records whether BBR estimates that it has
/// fully utilized its available bandwidth since it most recetly started looking.
full_bw_now: bool,
Expand Down Expand Up @@ -659,6 +665,7 @@ impl Bbr3 {
extra_acked_delivered: 0,
extra_acked_filter: MaxFilter::new(EXTRA_ACKED_FILTER_LEN as u64),
full_bw_reached: false,
has_seen_rtt: false,
full_bw_now: false,
full_bw: 0.0,
full_bw_count: 0,
Expand Down Expand Up @@ -1663,6 +1670,15 @@ impl Bbr3 {
_app_limited: bool,
rtt: &RttEstimator,
) {
if !self.has_seen_rtt && rtt.has_sample() {
// equivalent to BBRInitPacingRate with a real SRTT: the 1 ms placeholder used at
// construction is replaced by InitialCwnd / SRTT once the path has an RTT sample
self.has_seen_rtt = true;
let srtt = rtt.get().as_secs_f64().max(0.001);
let nominal_bandwidth = self.initial_cwnd as f64 / srtt;
self.pacing_rate = self.startup_pacing_gain * nominal_bandwidth;
self.set_send_quantum();
}
self.check_recovery_done(sent);
self.delivered += bytes;
self.delivered_time = Some(now);
Expand Down Expand Up @@ -2232,6 +2248,50 @@ mod test {
assert_eq!(bbr3.reno_rounds_bound, 63);
}

/// The initial pacing rate is a placeholder computed with a 1 ms RTT (no SRTT exists at
/// construction), and `BBRSetPacingRate` only ever raises the rate before startup exits. A
/// flow that stays application-limited never exits startup, so without re-deriving the rate
/// from the first RTT sample it is never paced at all. Mirrors Linux BBR's
/// `bbr_init_pacing_rate_from_rtt` on `has_seen_rtt`.
#[test]
fn initial_pacing_rate_follows_first_rtt_sample() {
let config = Bbr3Config {
initial_window: 12_000,
probe_rng_seed: None,
startup_pacing_gain: None,
default_pacing_gain: None,
probe_bw_down_pacing_gain: None,
probe_bw_up_pacing_gain: None,
probe_bw_up_cwnd_gain: None,
probe_rtt_cwnd_gain: None,
drain_pacing_gain: None,
pacing_margin_percent: None,
default_cwnd_gain: None,
};
let mut bbr3 = Bbr3::new(Arc::new(config), 1200);
// placeholder: InitialCwnd / 1 ms
let placeholder = STARTUP_PACING_GAIN * 12_000.0 / 0.001;
assert!((bbr3.pacing_rate - placeholder).abs() < 1.0);

// one packet sent and acknowledged with a measured 40 ms RTT
let t0 = Instant::now();
let mut rtt = RttEstimator::new(Duration::from_millis(333));
rtt.update(Duration::ZERO, Duration::from_millis(40));
bbr3.on_packet_sent(t0, 1200, 0, SpaceKind::Data);
let t1 = t0 + Duration::from_millis(40);
bbr3.on_ack(t1, t0, 1200, 0, SpaceKind::Data, false, &rtt);

// re-derived from the sample: InitialCwnd / SRTT, far below the placeholder
let expected = STARTUP_PACING_GAIN * 12_000.0 / rtt.get().as_secs_f64();
assert!(
(bbr3.pacing_rate - expected).abs() / expected < 0.01,
"pacing_rate {} B/s, expected {} B/s (placeholder was {})",
bbr3.pacing_rate,
expected,
placeholder
);
}

/// A.1: Exiting STARTUP on a bandwidth plateau.
/// equivalent to: <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-06.html#name-exiting-startup-on-bandwidt>
/// Drives a flow through the real `on_packet_sent`/`on_ack`/`on_end_acks`
Expand Down
6 changes: 6 additions & 0 deletions noq-proto/src/connection/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,12 @@ impl RttEstimator {
}
}

/// Whether at least one RTT sample has been taken (before that, [`Self::get`] returns the
/// configured initial estimate).
pub fn has_sample(&self) -> bool {
self.smoothed.is_some()
}

/// The current best RTT estimation.
pub fn get(&self) -> Duration {
self.smoothed.unwrap_or(self.latest)
Expand Down