Skip to content

Commit 21dd3b4

Browse files
committed
feat(tcp): Implement TCP pacing for rate-based congestion control
1 parent c4e7f96 commit 21dd3b4

File tree

5 files changed

+1251
-1
lines changed

5 files changed

+1251
-1
lines changed

.cargo/config.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[env]
2+
SMOLTCP_IFACE_MAX_ADDR_COUNT = "3"
3+
RUST_LOG = "off"

src/socket/tcp.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,10 @@ pub struct Socket<'a> {
598598
/// The congestion control algorithm.
599599
congestion_controller: congestion::AnyController,
600600

601+
/// Pacing: next time a packet can be sent (for rate limiting).
602+
/// If None, pacing is not active.
603+
pacing_next_send_at: Option<Instant>,
604+
601605
/// tsval generator - if some, tcp timestamp is enabled
602606
tsval_generator: Option<TcpTimestampGenerator>,
603607

@@ -670,6 +674,7 @@ impl<'a> Socket<'a> {
670674
tsval_generator: None,
671675
last_remote_tsval: 0,
672676
congestion_controller: congestion::AnyController::new(),
677+
pacing_next_send_at: None,
673678

674679
#[cfg(feature = "async")]
675680
rx_waker: WakerRegistration::new(),
@@ -2479,6 +2484,15 @@ impl<'a> Socket<'a> {
24792484
return Ok(());
24802485
}
24812486

2487+
// Check if pacing delays transmission
2488+
if let Some(next_send_at) = self.pacing_next_send_at {
2489+
if cx.now() < next_send_at && self.seq_to_transmit(cx) {
2490+
// Pacing prevents us from sending data right now
2491+
tcp_trace!("pacing delayed until {:?}", next_send_at);
2492+
return Ok(());
2493+
}
2494+
}
2495+
24822496
// Decide whether we're sending a packet.
24832497
if self.seq_to_transmit(cx) {
24842498
// If we have data to transmit and it fits into partner's window, do it.
@@ -2720,6 +2734,26 @@ impl<'a> Socket<'a> {
27202734
self.congestion_controller
27212735
.inner_mut()
27222736
.post_transmit(cx.now(), repr.segment_len());
2737+
2738+
// Update pacing: calculate when the next packet can be sent
2739+
let pacing_rate = self.congestion_controller.inner_mut().pacing_rate();
2740+
if pacing_rate > 0 {
2741+
// Calculate delay: (packet_size_bytes * 1_000_000) / pacing_rate_bytes_per_sec
2742+
// This gives us microseconds until the next packet can be sent
2743+
let packet_size = repr.segment_len() as u64;
2744+
let delay_micros = (packet_size * 1_000_000) / pacing_rate;
2745+
let delay = crate::time::Duration::from_micros(delay_micros);
2746+
self.pacing_next_send_at = Some(cx.now() + delay);
2747+
tcp_trace!(
2748+
"pacing: sent {} bytes at rate {} bytes/s, next send at {:?}",
2749+
packet_size,
2750+
pacing_rate,
2751+
self.pacing_next_send_at
2752+
);
2753+
} else {
2754+
// No pacing
2755+
self.pacing_next_send_at = None;
2756+
}
27232757
}
27242758

27252759
if repr.segment_len() > 0 && !self.timer.is_retransmit() {
@@ -2757,7 +2791,11 @@ impl<'a> Socket<'a> {
27572791
PollAt::Now
27582792
} else if self.seq_to_transmit(cx) {
27592793
// We have a data or flag packet to transmit.
2760-
PollAt::Now
2794+
// Check if pacing delays it.
2795+
match self.pacing_next_send_at {
2796+
Some(next_send_at) if cx.now() < next_send_at => PollAt::Time(next_send_at),
2797+
_ => PollAt::Now,
2798+
}
27612799
} else if self.window_to_update() {
27622800
// The receive window has been raised significantly.
27632801
PollAt::Now

src/socket/tcp/congestion.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ pub(super) trait Controller {
3939
/// This allows the congestion controller to track whether the application
4040
/// is app-limited (not enough data to send) or cwnd-limited.
4141
fn on_send_ready(&mut self, now: Instant, bytes_available: usize) {}
42+
43+
/// Returns the pacing rate in bytes per second.
44+
/// Returns 0 if pacing is not supported or not active.
45+
fn pacing_rate(&self) -> u64 {
46+
0
47+
}
4248
}
4349

4450
#[derive(Debug)]

src/socket/tcp/congestion/bbr.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,10 @@ impl Controller for Bbr {
702702
let cwnd = self.window();
703703
self.app_limited = bytes_available < cwnd;
704704
}
705+
706+
fn pacing_rate(&self) -> u64 {
707+
self.pacing_rate
708+
}
705709
}
706710

707711
#[derive(Debug, Clone, Copy, PartialEq, Eq)]

0 commit comments

Comments
 (0)