Skip to content

Commit

Permalink
fix(tcp): introduce GenericController for congestion control
Browse files Browse the repository at this point in the history
Remove `socket-tcp-cubic` and `socket-tcp-reno` features
for congestion control.
Instead of the features, `set_congestion_control()`
is defined to set an algorithm of congestion control.

Signed-off-by: Yuuki Takano <[email protected]>
  • Loading branch information
ytakano committed Feb 21, 2024
1 parent 179fd7e commit b3e38ee
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 43 deletions.
3 changes: 0 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,6 @@ defmt = ["dep:defmt", "heapless/defmt-03"]

"packetmeta-id" = []

"socket-tcp-cubic" = [] # use Cubic as a default congestion controller
"socket-tcp-reno" = [] # use Reno as a default congestion controller

"async" = []

default = [
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ There are 3 supported mediums.
hop-by-hop option.

#### 6LoWPAN

* Implementation of [RFC6282](https://tools.ietf.org/rfc/rfc6282.txt).
* Fragmentation is supported, as defined in [RFC4944](https://tools.ietf.org/rfc/rfc4944.txt).
* UDP header compression/decompression is supported.
Expand Down Expand Up @@ -229,8 +229,8 @@ They can be set in two ways:
- Via Cargo features: enable a feature like `<name>-<value>`. `name` must be in lowercase and
use dashes instead of underscores. For example. `iface-max-addr-count-3`. Only a selection of values
is available, check `Cargo.toml` for the list.
- Via environment variables at build time: set the variable named `SMOLTCP_<value>`. For example
`SMOLTCP_IFACE_MAX_ADDR_COUNT=3 cargo build`. You can also set them in the `[env]` section of `.cargo/config.toml`.
- Via environment variables at build time: set the variable named `SMOLTCP_<value>`. For example
`SMOLTCP_IFACE_MAX_ADDR_COUNT=3 cargo build`. You can also set them in the `[env]` section of `.cargo/config.toml`.
Any value can be set, unlike with Cargo features.

Environment variables take precedence over Cargo features. If two Cargo features are enabled for the same setting
Expand Down
47 changes: 44 additions & 3 deletions src/socket/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::wire::{
TCP_HEADER_LEN,
};

use self::congestion_controller::{ActiveCC, CongestionController};
use self::congestion_controller::CongestionController;

mod congestion_controller;

Expand Down Expand Up @@ -394,6 +394,14 @@ impl Display for Tuple {
}
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CongestionControl {
None,
Cubic,
Reno,
}

/// A Transmission Control Protocol socket.
///
/// A TCP socket may passively listen for connections or actively connect to another endpoint.
Expand Down Expand Up @@ -469,7 +477,7 @@ pub struct Socket<'a> {
nagle: bool,

/// The congestion control algorithm.
congestion_controller: ActiveCC,
congestion_controller: congestion_controller::GenericController,

#[cfg(feature = "async")]
rx_waker: WakerRegistration,
Expand Down Expand Up @@ -529,7 +537,7 @@ impl<'a> Socket<'a> {
ack_delay_timer: AckDelayTimer::Idle,
challenge_ack_timer: Instant::from_secs(0),
nagle: true,
congestion_controller: ActiveCC::default(),
congestion_controller: congestion_controller::GenericController::default(),

#[cfg(feature = "async")]
rx_waker: WakerRegistration::new(),
Expand All @@ -538,6 +546,39 @@ impl<'a> Socket<'a> {
}
}

/// Set an algorithm for congestion control.
///
/// The default setting is `CongestionControl::None`, indicating that no congestion control is applied.
/// Options `CongestionControl::Cubic` and `CongestionControl::Reno` are also available.
///
/// `CongestionControl::Cubic` represents a modern congestion control algorithm designed to
/// be more efficient and fair compared to `CongestionControl::Reno`.
/// It is the default choice for Linux, Windows, and macOS.
///
/// `CongestionControl::Reno` is a classic congestion control algorithm valued for its simplicity and
/// widespread support. Despite having a lower algorithmic complexity than `Cubic`,
/// it is less efficient in terms of bandwidth usage.
pub fn set_congestion_control(&mut self, congestion_control: CongestionControl) {
use congestion_controller::*;

self.congestion_controller = match congestion_control {
CongestionControl::None => GenericController::None,
CongestionControl::Cubic => GenericController::Cubic(cubic::Cubic::default()),
CongestionControl::Reno => GenericController::Reno(reno::Reno::default()),
}
}

/// Return the current congestion control algorithm.
pub fn get_congestion_control(&self) -> CongestionControl {
use congestion_controller::*;

match self.congestion_controller {
GenericController::None => CongestionControl::None,
GenericController::Cubic(_) => CongestionControl::Cubic,
GenericController::Reno(_) => CongestionControl::Reno,
}
}

/// Register a waker for receive operations.
///
/// The waker is woken on state changes that might affect the return value
Expand Down
75 changes: 58 additions & 17 deletions src/socket/tcp/congestion_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,8 @@ use crate::time::Instant;

use super::RttEstimator;

#[cfg(feature = "socket-tcp-cubic")]
mod cubic;

#[cfg(feature = "socket-tcp-reno")]
mod reno;

#[cfg(not(any(feature = "socket-tcp-cubic", feature = "socket-tcp-reno")))]
mod no_control;

#[cfg(feature = "socket-tcp-cubic")]
pub(super) type ActiveCC = cubic::Cubic;

#[cfg(feature = "socket-tcp-reno")]
pub(super) type ActiveCC = reno::Reno;

#[cfg(not(any(feature = "socket-tcp-cubic", feature = "socket-tcp-reno")))]
pub(super) type ActiveCC = no_control::NoControl;
pub(super) mod cubic;
pub(super) mod reno;

pub(super) trait CongestionController: Default {
/// Returns the number of bytes that can be sent.
Expand Down Expand Up @@ -47,3 +32,59 @@ pub(super) trait CongestionController: Default {
#[allow(unused_variables)]
fn set_mss(&mut self, mss: usize) {}
}

#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(super) enum GenericController {
None,
Cubic(cubic::Cubic),
Reno(reno::Reno),
}

impl Default for GenericController {
fn default() -> Self {
GenericController::None
}
}

impl CongestionController for GenericController {
fn window(&self) -> usize {
match self {
GenericController::None => usize::MAX,
GenericController::Cubic(c) => c.window(),
GenericController::Reno(r) => r.window(),
}
}

fn set_remote_window(&mut self, remote_window: usize) {
match self {
GenericController::None => {}
GenericController::Cubic(c) => c.set_remote_window(remote_window),
GenericController::Reno(r) => r.set_remote_window(remote_window),
}
}

fn on_ack(&mut self, now: Instant, len: usize, rtt: &RttEstimator) {
match self {
GenericController::None => {}
GenericController::Cubic(c) => c.on_ack(now, len, rtt),
GenericController::Reno(r) => r.on_ack(now, len, rtt),
}
}

fn on_retransmit(&mut self, now: Instant) {
match self {
GenericController::None => {}
GenericController::Cubic(c) => c.on_retransmit(now),
GenericController::Reno(r) => r.on_retransmit(now),
}
}

fn on_duplicate_ack(&mut self, now: Instant) {
match self {
GenericController::None => {}
GenericController::Cubic(c) => c.on_duplicate_ack(now),
GenericController::Reno(r) => r.on_duplicate_ack(now),
}
}
}
17 changes: 0 additions & 17 deletions src/socket/tcp/congestion_controller/no_control.rs

This file was deleted.

0 comments on commit b3e38ee

Please sign in to comment.