Compare commits

..

6 Commits

Author SHA1 Message Date
Lukas Prause
96fed81e57 Update the README file to reflect the current state of the project. 2026-03-27 14:52:29 +01:00
Lukas Prause
ac8787ed10 Merge branch 'make-formatting-linux-conform' into 'master'
Make formatting linux conform

See merge request lukas.prause/tcp-roccet-kernel-module!1
2026-02-10 14:21:08 +01:00
Tim Füchsel
27d181962d Make formatting linux conform 2026-02-10 14:21:08 +01:00
Lukas Prause
c24b02231a Adds ECN handling managed by ROCCET. 2026-02-09 13:51:17 +01:00
Lukas Prause
5ebd5c0cb7 Adjusts the comment about the ROCCET paper. 2026-01-30 13:34:47 +01:00
Lukas Prause
a4cce7378f Update paper state in README.md 2026-01-29 11:35:20 +01:00
3 changed files with 125 additions and 115 deletions

View File

@@ -1,35 +1,40 @@
# TCP ROCCET :rocket: (RTT Oriented CUBIC Congestion control ExTension)
TCP ROCCET is a new TCP congestion control
algorithm suited for current cellular 5G NR beyond networks.
It extends the kernel default congestion control CUBIC
and improves its performance, and additionally solves
unwanted side effects of CUBICs implementation.
ROCCET uses its own Slow Start, called LAUNCH, where loss
is not considered as a congestion event.
The congestion avoidance phase, called ORBITER, uses
CUBIC's window growth function and adds, based on RTT
and ACK rate, congestion events.
TCP ROCCET is a new TCP congestion control algorithm that reduces latency by detecting queuing.
Additionally, it is specially suited for 4G/5G cellular networks.
NOTE: A paper for TCP ROCCET is currently under review.
Paper draft:
https://arxiv.org/abs/2510.25281
A peer-reviewed [paper on TCP ROCCET](https://opendl.ifip-tc6.org/db/conf/wons/wons2026/1571217211.pdf) was presented at the WONS 2026 conference.
The oldest Linux kernel version we tested with ROCCET is 6.1.
## Install with apt
Add tcp-roccet-dkms to your source lists and install:
```
echo "deb [trusted=yes] https://apt.fury.io/timfuchs/ /" | sudo tee /etc/apt/sources.list.d/tcp-roccet.list
sudo apt update
sudo apt install tcp-roccet-dkms
```
## Setup
### Loading and using the congestion control
Note: This change is not persistent, the module will not be loaded at boot.
```
sudo modprobe tcp_roccet
sudo sysctl net.ipv4.tcp_congestion_control=roccet
```
## Build from source
### Setup
Kernel headers are required:
* Debian: `sudo apt install linux-headers-generic`
* Fedora: `sudo dnf install kernel-devel`
(`sudo reboot`)
## Build
### Build
`make`
## Loading & Unloading the Module
**1.** Insert into Kernel: `sudo insmod tcp_roccet.ko`
In case you get the "Invalid module Format" error, it can help reinstalling the kernel-headers.
In case you get the "Invalid module Format" error, it can help to reinstall the kernel-headers.
**2.** Use the Algorithm:
* Either via globally loading it: `sudo sysctl net.ipv4.tcp_congestion_control=roccet`
@@ -39,12 +44,12 @@ In case you get the "Invalid module Format" error, it can help reinstalling the
## Debugging (Using kprobe)
In order to debug the `tcp_roccet` congestion control algorithm, there exists a Kprobe module (`roccet_kprobe.c`). Using this it is possible to inspect events generated by the algorithm.
In order to debug the `tcp_roccet` congestion control algorithm, there exists a Kprobe module (`roccet_kprobe.c`). Using this, it is possible to inspect events generated by the algorithm.
In order to use the Kprobe module the following steps are necessary:
In order to use the Kprobe module, the following steps are necessary:
**1. Specify the event to inspect**
For this head into the `tcp_roccet.c` source code and find the function the Kprobe should attach to.
For this, head into the `tcp_roccet.c` source code and find the function the Kprobe should attach to.
For Example:
```
@@ -69,7 +74,7 @@ Required files for this are:
* `tcp_roccet.c` (Optional; Need to remove corresponding entry in Makefile if missing)
**3. Load Kprobe module**
To then load the Module use
To then load the module use
`sudo insmod roccet_kprobe.ko`
If you are seeing the error "Unknown symbol in module" you need to first load the roccet algorithm.
@@ -78,21 +83,7 @@ To see the trace output use
`sudo cat /sys/kernel/tracing/trace_pipe`
**5. Unload Kprobe module**
To remove the module again use
To remove the module again, use
`sudo rmmod roccet_kprobe`
_See more info at_ https://docs.kernel.org/trace/kprobes.html and https://www.kernel.org/doc/Documentation/trace/kprobetrace.rst
## Further Info
* On TCP-CC Ops:
* https://www.yonch.com/tech/linux-tcp-congestion-control-internals
* https://docs.ebpf.io/linux/program-type/BPF_PROG_TYPE_STRUCT_OPS/tcp_congestion_ops/
# Setup Development Environment
For specific Kernel:
1. Download linux source (the version you want to develop for)
2. Create Config via `make defconfig`
3. Compile kernel via `make`
4. Generate clangd Config via `python scripts/clang-tools/gen_compile_commands.py`
5. Copy `compile_commands.json` to development directory

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-only
// SPDX-License-Identifier: GPL-2.0
/*
* TCP ROCCET: An RTT-Oriented CUBIC Congestion Control
* Extension for 5G and Beyond Networks
@@ -14,8 +14,9 @@
* CUBIC's window growth function and adds, based on RTT
* and ACK rate, congestion events.
*
* NOTE: A paper for TCP ROCCET is currently under review.
* A draft of this paper can be found here:
* A peer-reviewed paper on TCP ROCCET will be presented at the WONS 2026 conference.
* A draft of the paper is available here:
* https://arxiv.org/abs/2510.25281
*
*
* Further information about CUBIC:
@@ -71,30 +72,26 @@
static int sr_rtt_upper_bound __read_mostly = 100;
static int ack_rate_diff_ss __read_mostly = 10;
static int ack_rate_diff_ca __read_mostly = 200;
static int calculate_min_rtt __read_mostly = 0;
static int ignore_loss __read_mostly = 0;
static int roccet_minRTT_interpolation_factor __read_mostly = 70;
static bool calculate_min_rtt __read_mostly;
static bool ignore_loss __read_mostly;
static int roccet_min_rtt_interpolation_factor __read_mostly = 70;
module_param(sr_rtt_upper_bound, int, 0644);
MODULE_PARM_DESC(sr_rtt_upper_bound, "ROCCET's upper bound for srRTT.");
module_param(ack_rate_diff_ss, int, 0644);
MODULE_PARM_DESC(ack_rate_diff_ss,
"ROCCET's threshold to exit slow start if ACK-rate defer by "
"given amount of segments.");
"ROCCET's threshold to exit slow start if ACK-rate defer by given amount of segments.");
module_param(ack_rate_diff_ca, int, 0644);
MODULE_PARM_DESC(ack_rate_diff_ca,
"ROCCET's threshold for ack-rate and cum_cwnd, in percantage "
"of the current cwnd.");
module_param(calculate_min_rtt, int, 0644);
"ROCCET's threshold for ack-rate and cum_cwnd, in percantage of the current cwnd.");
module_param(calculate_min_rtt, bool, 0644);
MODULE_PARM_DESC(calculate_min_rtt,
"Calculate min RTT if no lower RTT occurs after 10 sec.");
module_param(ignore_loss, int, 0644);
module_param(ignore_loss, bool, 0644);
MODULE_PARM_DESC(ignore_loss, "Ignore loss as a congestion event.");
module_param(roccet_minRTT_interpolation_factor, int, 0644);
MODULE_PARM_DESC(
roccet_minRTT_interpolation_factor,
"ROCCET factor for interpolating the current RTT with the last minRTT "
"(minRTT = (factor * currRTT + (100-factor) * minRTT) / 100)");
module_param(roccet_min_rtt_interpolation_factor, int, 0644);
MODULE_PARM_DESC(roccet_min_rtt_interpolation_factor,
"ROCCET factor for interpolating the current RTT with the last minRTT (minRTT = (factor * currRTT + (100-factor) * minRTT) / 100)");
static int fast_convergence __read_mostly = 1;
static int beta __read_mostly = 717; /* = 717/1024 (BICTCP_BETA_SCALE) */
@@ -114,8 +111,7 @@ MODULE_PARM_DESC(beta, "beta for multiplicative increase");
module_param(initial_ssthresh, int, 0644);
MODULE_PARM_DESC(initial_ssthresh, "initial value of slow start threshold");
module_param(bic_scale, int, 0444);
MODULE_PARM_DESC(
bic_scale,
MODULE_PARM_DESC(bic_scale,
"scale (scaled by 1024) value for bic function (bic_scale/1024)");
module_param(tcp_friendliness, int, 0644);
MODULE_PARM_DESC(tcp_friendliness, "turn on/off tcp friendliness");
@@ -129,6 +125,7 @@ static inline void roccettcp_reset(struct roccettcp *ca)
ca->curr_min_rtt_timed.rtt = ~0U;
ca->curr_min_rtt_timed.time = ~0U;
ca->last_rtt = 0;
ca->ece_received = false;
}
static inline void update_min_rtt(struct sock *sk)
@@ -143,9 +140,9 @@ static inline void update_min_rtt(struct sock *sk)
u32 old_min_rtt = ca->curr_min_rtt_timed.rtt;
u32 interpolated_min_rtt =
(new_min_rtt * roccet_minRTT_interpolation_factor +
(new_min_rtt * roccet_min_rtt_interpolation_factor +
old_min_rtt *
(100 - roccet_minRTT_interpolation_factor)) /
(100 - roccet_min_rtt_interpolation_factor)) /
100;
ca->curr_min_rtt_timed.rtt = interpolated_min_rtt;
@@ -196,13 +193,12 @@ static inline void update_srrtt(struct sock *sk)
/* Calculate the new rRTT (Scaled by 100).
* 100 * ((sRTT - sRTT_min) / sRTT_min)
*/
u32 rRTT = (100 * (ca->curr_rtt - ca->curr_min_rtt_timed.rtt)) /
u32 rrtt = (100 * (ca->curr_rtt - ca->curr_min_rtt_timed.rtt)) /
ca->curr_min_rtt_timed.rtt;
// (1 - alpha) * srRTT + alpha * rRTT
ca->curr_srRTT = ((100 - ROCCET_ALPHA_TIMES_100) * ca->curr_srRTT +
ROCCET_ALPHA_TIMES_100 * rRTT) /
100;
ca->curr_srrtt = ((100 - ROCCET_ALPHA_TIMES_100) * ca->curr_srrtt +
ROCCET_ALPHA_TIMES_100 * rrtt) / 100;
}
__bpf_kfunc static void roccettcp_init(struct sock *sk)
@@ -214,7 +210,7 @@ __bpf_kfunc static void roccettcp_init(struct sock *sk)
if (initial_ssthresh)
tcp_sk(sk)->snd_ssthresh = initial_ssthresh;
/* Initial roccet paramters */
/* Initial roccet parameters */
ca->roccet_last_event_time_us = 0;
ca->curr_min_rtt = ~0U;
ca->ack_rate.last_rate = 0;
@@ -338,7 +334,7 @@ static inline void bictcp_update(struct roccettcp *ca, u32 cwnd, u32 acked)
* (so time^3 is done by using 64 bit)
* and without the support of division of 64bit numbers
* (so all divisions are done by using 32 bit)
* also NOTE the unit of those veriables
* also NOTE the unit of those variables
* time = (t - K) / 2^bictcp_HZ
* c = bic_scale >> 10
* rtt = (srtt >> 3) / HZ
@@ -366,11 +362,10 @@ static inline void bictcp_update(struct roccettcp *ca, u32 cwnd, u32 acked)
bic_target = ca->bic_origin_point + delta;
/* cubic function - calc bictcp_cnt*/
if (bic_target > cwnd) {
if (bic_target > cwnd)
ca->cnt = cwnd / (bic_target - cwnd);
} else {
else
ca->cnt = 100 * cwnd; /* very small increment*/
}
/* The initial growth of cubic function may be too conservative
* when the available bandwidth is still unknown.
@@ -413,13 +408,13 @@ __bpf_kfunc static void roccettcp_cong_avoid(struct sock *sk, u32 ack,
u32 bw_limit_detect = 0;
u32 roccet_xj;
u32 jitter;
if (ca->last_rtt > ca->curr_rtt) {
jitter = ca->last_rtt - ca->curr_rtt;
} else {
jitter = ca->curr_rtt - ca->last_rtt;
}
/* Update roccet paramters */
if (ca->last_rtt > ca->curr_rtt)
jitter = ca->last_rtt - ca->curr_rtt;
else
jitter = ca->curr_rtt - ca->last_rtt;
/* Update roccet parameters */
update_ack_rate(sk);
update_min_rtt(sk);
update_srrtt(sk);
@@ -430,15 +425,19 @@ __bpf_kfunc static void roccettcp_cong_avoid(struct sock *sk, u32 ack,
if (now - ca->roccet_last_event_time_us <= 100 * USEC_PER_MSEC)
return;
/* Lift off: Detect an exit point for tcp slow start
/* LAUNCH: Detect an exit point for tcp slow start
* in networks with large buffers of multiple BDP
* Like in cellular networks (5G, ...).
* Or exit LAUNCH if cwnd is too large for application layer
* data rate.
*/
if (tcp_in_slow_start(tp) && ca->curr_srRTT > sr_rtt_upper_bound &&
get_ack_rate_diff(ca) >= ack_rate_diff_ss) {
if ((tcp_in_slow_start(tp) && ca->curr_srrtt > sr_rtt_upper_bound &&
get_ack_rate_diff(ca) >= ack_rate_diff_ss) ||
(!tcp_is_cwnd_limited(sk) && tcp_in_slow_start(tp))) {
ca->epoch_start = 0;
/* Handle inital slow start. Here we observe the most problems */
/* Handle initial slow start. Here we observe the most problems */
if (tp->snd_ssthresh == TCP_INFINITE_SSTHRESH) {
tcp_sk(sk)->snd_ssthresh = tcp_snd_cwnd(tp) / 2;
tcp_snd_cwnd_set(tp, tcp_snd_cwnd(tp) / 2);
@@ -486,7 +485,9 @@ __bpf_kfunc static void roccettcp_cong_avoid(struct sock *sk, u32 ack,
if (roccet_xj < sr_rtt_upper_bound)
roccet_xj = sr_rtt_upper_bound;
if (ca->curr_srRTT > roccet_xj && bw_limit_detect) {
if (ca->curr_srrtt > roccet_xj && (bw_limit_detect || ca->ece_received)) {
if (ca->ece_received)
ca->ece_received = false;
ca->epoch_start = 0;
ca->roccet_last_event_time_us = now;
ca->cnt = 100 * tcp_snd_cwnd(tp);
@@ -543,6 +544,7 @@ __bpf_kfunc static u32 roccettcp_recalc_ssthresh(struct sock *sk)
__bpf_kfunc static void roccettcp_state(struct sock *sk, u8 new_state)
{
struct roccettcp *ca = inet_csk_ca(sk);
if (new_state == TCP_CA_Loss)
roccettcp_reset(ca);
}
@@ -561,6 +563,7 @@ __bpf_kfunc static void roccettcp_acked(struct sock *sk,
return;
u32 delay = sample->rtt_us;
if (delay == 0)
delay = 1;
@@ -575,6 +578,17 @@ __bpf_kfunc static void roccettcp_acked(struct sock *sk,
}
}
__bpf_kfunc static void roccet_in_ack_event(struct sock *sk, u32 flags)
{
struct roccettcp *ca = inet_csk_ca(sk);
/* Handle ECE bit.
* Processing of ECE events is done in roccettcp_cong_avoid()
*/
if (flags & CA_ACK_ECE)
ca->ece_received = true;
}
static struct tcp_congestion_ops roccet_tcp __read_mostly = {
.init = roccettcp_init,
.ssthresh = roccettcp_recalc_ssthresh,
@@ -583,6 +597,7 @@ static struct tcp_congestion_ops roccet_tcp __read_mostly = {
.undo_cwnd = tcp_reno_undo_cwnd,
.cwnd_event = roccettcp_cwnd_event,
.pkts_acked = roccettcp_acked,
.in_ack_event = roccet_in_ack_event,
.owner = THIS_MODULE,
.name = "roccet",
};

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* TCP ROCCET congestion control interface
*/
@@ -6,21 +7,21 @@
#include <linux/math64.h>
struct AckRate {
struct ack_rate {
u16 last_rate; /* Last ACK-rate */
u32 last_rate_time; /* Timestamp of the last ACK-rate */
u16 curr_rate; /* Current ACK-rate */
u16 cnt; /* Used for counting acks */
};
struct BandwidthLimitDetect {
struct bandwidth_limit_detect {
u32 sum_cwnd; /* sum of cwnd during time interval */
u32 sum_acked; /* sum of received acks during time interval */
u32 next_check; /* end/upper bound of time interval */
};
struct TimedRTT {
u32 time; /* Time of recoding */
struct timed_rtt {
u32 time; /* Time of recording */
u32 rtt; /* Measured RTT */
};
@@ -32,20 +33,23 @@ struct roccettcp {
u32 last_time; /* time when updated last_cwnd */
u32 bic_origin_point; /* origin point of bic function */
u32 bic_K; /* time to origin point from the
beginning of the current epoch */
* beginning of the current epoch
*/
u32 delay_min; /* min delay (usec) */
u32 epoch_start; /* beginning of an epoch */
u32 ack_cnt; /* number of acks */
u32 tcp_cwnd; /* estimated tcp cwnd */
u32 curr_rtt; /* the minimum rtt of current round */
u32 roccet_last_event_time_us; /* The last time ROCCETv2 was triggered */
u32 roccet_last_event_time_us; /* The last time ROCCET was triggered */
bool ece_received; /* Set to true if an ECE bit was received */
u32 curr_min_rtt; /* The current observed minRTT */
struct TimedRTT curr_min_rtt_timed; /* The current observed minRTT with
the timestamp when it was observed */
u32 curr_srRTT; /* The srRTT calculated based on the latest ACK */
struct AckRate ack_rate; /* The last and the current ACK rate */
struct BandwidthLimitDetect bw_limit;
struct timed_rtt curr_min_rtt_timed; /* The current observed minRTT with
* the timestamp when it was observed
*/
u32 curr_srrtt; /* The srRTT calculated based on the latest ACK */
struct ack_rate ack_rate; /* The last and the current ACK rate */
struct bandwidth_limit_detect bw_limit;
u32 last_rtt; /* Used for jitter calculation */
};