From b3e38eef35b68798d0ed43acaef9ee2200be99a3 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Wed, 21 Feb 2024 16:02:20 +0900 Subject: [PATCH] fix(tcp): introduce `GenericController` for congestion control 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 --- Cargo.toml | 3 - README.md | 6 +- src/socket/tcp.rs | 47 +++++++++++- src/socket/tcp/congestion_controller.rs | 75 ++++++++++++++----- .../tcp/congestion_controller/no_control.rs | 17 ----- 5 files changed, 105 insertions(+), 43 deletions(-) delete mode 100644 src/socket/tcp/congestion_controller/no_control.rs diff --git a/Cargo.toml b/Cargo.toml index 9eb380c0e..818b60bc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 = [ diff --git a/README.md b/README.md index 74fa161e9..9642fdc09 100644 --- a/README.md +++ b/README.md @@ -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. @@ -229,8 +229,8 @@ They can be set in two ways: - Via Cargo features: enable a feature like `-`. `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_`. 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_`. 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 diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index c683960b2..f0eed51ce 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -17,7 +17,7 @@ use crate::wire::{ TCP_HEADER_LEN, }; -use self::congestion_controller::{ActiveCC, CongestionController}; +use self::congestion_controller::CongestionController; mod congestion_controller; @@ -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. @@ -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, @@ -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(), @@ -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 diff --git a/src/socket/tcp/congestion_controller.rs b/src/socket/tcp/congestion_controller.rs index a2e61c29f..930bf4c6a 100644 --- a/src/socket/tcp/congestion_controller.rs +++ b/src/socket/tcp/congestion_controller.rs @@ -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. @@ -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), + } + } +} diff --git a/src/socket/tcp/congestion_controller/no_control.rs b/src/socket/tcp/congestion_controller/no_control.rs deleted file mode 100644 index a3d2905af..000000000 --- a/src/socket/tcp/congestion_controller/no_control.rs +++ /dev/null @@ -1,17 +0,0 @@ -use super::CongestionController; - -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct NoControl; - -impl CongestionController for NoControl { - fn window(&self) -> usize { - usize::MAX - } -} - -impl Default for NoControl { - fn default() -> Self { - NoControl - } -}