From 64d0e54f8777db80e3ce9edeb8e75d0850b3787e Mon Sep 17 00:00:00 2001 From: Lukas Prause Date: Wed, 16 Sep 2026 15:22:54 +0200 Subject: [PATCH] Update repository to the state of the latest kernel patch. --- tcp_roccet.c | 1069 +++++++++++++++++++++++++++++++------------------- tcp_roccet.h | 30 +- 2 files changed, 679 insertions(+), 420 deletions(-) diff --git a/tcp_roccet.c b/tcp_roccet.c index 83c4b1a..aafe752 100644 --- a/tcp_roccet.c +++ b/tcp_roccet.c @@ -45,7 +45,8 @@ * this behaves the same as the original Reno. */ -#include "linux/limits.h" +#include +#include #include #include #include @@ -58,12 +59,12 @@ #define BICTCP_HZ 10 /* BIC HZ 2^10 = 1024 */ -/* Alpha value for the sRrTT multiplied by 100. +/* Alpha value for the srRTT multiplied by 100. * Here 20 represents a value of 0.2 */ #define ROCCET_ALPHA_TIMES_100 20 -/* min RTT probe period in seconds */ +/* min RTT probe period in ms */ #define ROCCET_NEXT_MIN_RTT_PROBE 5000 /* Parameters that are specific to the ROCCET-Algorithm */ @@ -74,7 +75,7 @@ module_param(sr_rtt_upper_bound, uint, 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 differs by given amount of segments."); static int fast_convergence __read_mostly = 1; static int beta __read_mostly = 717; /* = 717/1024 (BICTCP_BETA_SCALE) */ @@ -99,16 +100,78 @@ MODULE_PARM_DESC(bic_scale, module_param(tcp_friendliness, int, 0644); MODULE_PARM_DESC(tcp_friendliness, "turn on/off tcp friendliness"); -static __always_inline void roccettcp_reset(struct roccettcp *ca) +/* Used to check certain roccet parameters used in `param_precompute` in order + * to avoid invalid scale-related calculations. This validates the specified + * parameters or rejects them. + */ +static int param_check(void) +{ + if (beta <= 0 || beta >= BICTCP_BETA_SCALE) { + pr_err_once("TCP ROCCET: beta must be between 0 and %d\n", + BICTCP_BETA_SCALE); + + return -EINVAL; + } + + if (bic_scale <= 0) { + pr_err_once("TCP ROCCET: bic_scale must be positive\n"); + + return -EINVAL; + } + + return 0; +} + +/* Precompute some values based on the provided params. + * These are only precomputed during module initialization and are not updated + * during runtime. Parameter changes at runtime will only affect the next uses + * of the parameters, but not the precomputed values. + */ +static void param_precompute(void) +{ + /* Precompute a bunch of the scaling factors that are used per-packet + * based on sRTT of 100ms. + */ + beta_scale = + 8 * (BICTCP_BETA_SCALE + beta) / 3 / (BICTCP_BETA_SCALE - beta); + + cube_rtt_scale = (bic_scale * 10); /* 1024*c/rtt */ + + /* calculate the "K" for (wmax-cwnd) = c/rtt * K^3 + * so K = cubic_root( (wmax-cwnd)*rtt/c ) + * the unit of K is bictcp_HZ=2^10, not HZ + * + * c = bic_scale >> 10 + * rtt = 100ms + * + * the following code has been designed and tested for + * cwnd < 1 million packets + * RTT < 100 seconds + * HZ < 1,000,00 (corresponding to 10 nano-second) + */ + + /* 1/c * 2^2*bictcp_HZ * srtt */ + cube_factor = 1ull << (10 + 3 * BICTCP_HZ); /* 2^40 */ + + /* divide by bic_scale and by constant srtt (100ms) */ + do_div(cube_factor, bic_scale * 10); +} + +static void roccet_reset(struct sock *sk, struct roccettcp *ca) { memset(ca, 0, sizeof(struct roccettcp)); - ca->next_srrtt_check = 0; - ca->curr_min_rtt = ~0U; - ca->last_rtt = 0; - ca->ece_received = false; + ca->next_srrtt_check_ts = 0; + + /* Initialize all RTT values to U32_MAX, so that any lower samples + * will be accepted. + */ + ca->curr_min_rtt = U32_MAX; + ca->curr_rtt = U32_MAX; + ca->last_rtt = U32_MAX; ca->roccet_last_event_time_us = 0; ca->ack_rate_last_rate = 0; + /* Initialize to current time to avoid an * overflow in the ack rate calculation */ @@ -116,27 +179,42 @@ static __always_inline void roccettcp_reset(struct roccettcp *ca) ca->ack_rate_curr_rate = 0; ca->ack_rate_cnt = 0; + ca->interval_snd_seq_start = tcp_sk(sk)->snd_nxt; + ca->interval_una_seq_start = tcp_sk(sk)->snd_una; + /* Start state is LAUNCH */ ca->state = LAUNCH; + ca->probe_min_rtt_until = 0; + ca->refill_until = 0; + + ca->initial_limit_reached = false; + ca->is_in_initial_launch = false; } -/* Return true if ROCCET is in min RTT probing. - */ -static __always_inline bool is_in_min_rtt_probing(struct roccettcp *ca, u32 now) -{ - if (ca->probe_min_rtt_until == 0) - return false; - return before(now, ca->probe_min_rtt_until); -} - -static __always_inline void update_min_rtt(struct sock *sk) +static void roccet_init(struct sock *sk) { struct roccettcp *ca = inet_csk_ca(sk); - /* Check if new lower min RTT was found. If so, set it directly */ + roccet_reset(sk, ca); + + /* Reset here, so it is only set during init */ + ca->is_in_initial_launch = true; + + if (initial_ssthresh) + WRITE_ONCE(tcp_sk(sk)->snd_ssthresh, initial_ssthresh); + + cmpxchg(&sk->sk_pacing_status, SK_PACING_NONE, SK_PACING_NEEDED); +} + +static void update_min_rtt(struct roccettcp *ca) +{ + /* Check if new lower min RTT was found. If so, set it directly. + * If no valid RTT sample has been received yet, the check will fail, + * since the rtt values are initialized to U32_MAX. + */ if (ca->curr_rtt < ca->curr_min_rtt) { ca->curr_min_rtt = max(ca->curr_rtt, 1); - /* Probe for the min RTT in ROCCET_NEXT_MIN_RTT_PROBE seconds + /* Probe for the min RTT in ROCCET_NEXT_MIN_RTT_PROBE ms * if no other update occurs. */ ca->next_min_rtt_probe = @@ -147,7 +225,7 @@ static __always_inline void update_min_rtt(struct sock *sk) /* Return difference between last and current ack rate. */ -static __always_inline s32 get_ack_rate_diff(struct roccettcp *ca) +static s32 get_ack_rate_diff(struct roccettcp *ca) { if (ca->ack_rate_curr_rate < ca->ack_rate_last_rate) return 0; @@ -156,16 +234,19 @@ static __always_inline s32 get_ack_rate_diff(struct roccettcp *ca) /* Update ack rate sampled by 100ms. */ -static __always_inline void update_ack_rate(struct sock *sk, u32 acked, u32 now) +static void update_ack_rate(struct roccettcp *ca, u32 acked, u32 now) { - struct roccettcp *ca = inet_csk_ca(sk); - s32 interval = USEC_PER_MSEC * 100; + const s32 idle_threshold = USEC_PER_SEC * 2; + const s32 interval = USEC_PER_MSEC * 100; s32 time_delta = (s32)(ca->ack_rate_last_rate_time - now); - const s32 idle_threshold = USEC_PER_SEC * 2; - // Check if the time has arrived in the new interval - if (time_delta < -interval) { + /* Check if the time has arrived in the new interval. + * Alternatively if the connection was considered to be idle, + * also treat as a new interval in order to avoid timing-overflow + * problems. + */ + if (time_delta < -interval || ca->was_idle) { /* Check if the connection was idle for X seconds * (e.g. no ACK for X seconds) */ @@ -179,14 +260,16 @@ static __always_inline void update_ack_rate(struct sock *sk, u32 acked, u32 now) ca->ack_rate_curr_rate = 0; ca->ack_rate_cnt = 0; } else { + /* start counting for the new interval */ ca->ack_rate_last_rate_time = now; ca->ack_rate_last_rate = ca->ack_rate_curr_rate; ca->ack_rate_curr_rate = ca->ack_rate_cnt; - ca->ack_rate_cnt = - acked; // start counting for the new interval + ca->ack_rate_cnt = min_t(u32, acked, U16_MAX); } + + ca->was_idle = false; } else { - // Cap the ack count to avoid overflow + /* Cap the ack count to avoid overflow */ ca->ack_rate_cnt = min_t(u32, ca->ack_rate_cnt + acked, U16_MAX); } @@ -194,140 +277,240 @@ static __always_inline void update_ack_rate(struct sock *sk, u32 acked, u32 now) /* Compute srRTT. */ -static __always_inline void update_srrtt(struct sock *sk) +static void update_srrtt(struct roccettcp *ca) { - struct roccettcp *ca = inet_csk_ca(sk); + u64 rrtt; /* Avoid integer overflow in the calculation below. * This could occur in cases where we have not yet - * received an RTT sample. In these cases, set the - * rtt to a safe value. + * received an RTT sample after a min_rtt reset. + * In these cases, set the rtt to a safe value. */ if (ca->curr_rtt < ca->curr_min_rtt) { ca->curr_rtt = max(ca->curr_rtt, 1); ca->curr_min_rtt = ca->curr_rtt; } - /* Avoid division by zero */ + /* ca->curr_min_rtt can never be 0. For completeness, we check for this + * anyways in order to avoid division by zero errors. + */ if (ca->curr_min_rtt == 0) { - ca->curr_min_rtt = max(ca->curr_min_rtt, 1); - return; // skip srRTT update + pr_err_once("TCP ROCCET: Recorded curr_min_rtt is 0"); + return; /* skip srRTT update */ } /* Calculate the new rRTT (Scaled by 100). * 100 * ((sRTT - sRTT_min) / sRTT_min). * - * curr_min_rtt_timed.rtt is always <= than curr_rtt, + * curr_min_rtt is always <= than curr_rtt, * since this is the minimum of the rtt. * * 0 is a valid value for rrtt. + * + * If we have no valid RTT sample yet, curr_rtt and curr_min_rtt will + * be U32_MAX. This ultimately results in no srrtt increase, which + * is ok. */ - u32 rrtt = div_u64(100 * (u64)(ca->curr_rtt - ca->curr_min_rtt), - ca->curr_min_rtt); + rrtt = div_u64(100 * (u64)(ca->curr_rtt - ca->curr_min_rtt), + ca->curr_min_rtt); - // (1 - alpha) * srRTT + alpha * rRTT - ca->curr_srrtt = ((100 - ROCCET_ALPHA_TIMES_100) * ca->curr_srrtt + + /* (1 - alpha) * srRTT + alpha * rRTT */ + ca->curr_srrtt = ((100 - ROCCET_ALPHA_TIMES_100) * (u64)ca->curr_srrtt + ROCCET_ALPHA_TIMES_100 * rrtt) / 100; } +/* Handle ROCCET loss/ECN during min RTT probing. + */ +static void roccet_min_rtt_probe_ce(struct roccettcp *ca, u32 cwnd) +{ + /* This should only be called in RTT_PROBE state */ + if (ca->state != RTT_PROBE) + return; + + /* If ROCCET is in min RTT probing and a loss/ECN occurs, + * we use the cwnd before the probing interval to + * calculate the cwnd reduction and continue probing. + * After min RTT probing the cwnd is set to the reduced + * value. During min RTT probing it is very likely that + * congestion was caused by the cwnd value before min + * RTT probing. + */ + + if (ca->cwnd_before_min_rtt_probe == 0) + pr_warn_once("ROCCET: cwnd_before_min_rtt_probe is 0 during RTT_PROBE. This should not happen."); + else + cwnd = ca->cwnd_before_min_rtt_probe; + + ca->cwnd_before_min_rtt_probe = + max((cwnd * beta) / BICTCP_BETA_SCALE, 2U); +} + /* Do a ROCCET congestion event. */ -static __always_inline void roccet_congestion_event(struct sock *sk, u32 now) +static void roccet_congestion_event(struct sock *sk, u32 now) { struct tcp_sock *tp = tcp_sk(sk); struct roccettcp *ca = inet_csk_ca(sk); + u32 curr_cwnd = tcp_snd_cwnd(tp); + ca->epoch_start = 0; ca->roccet_last_event_time_us = now; - ca->cnt = 100 * tcp_snd_cwnd(tp); - /*Set W_max only if the current cwnd is larger */ - if (tcp_snd_cwnd(tp) > ca->last_max_cwnd) - ca->last_max_cwnd = tcp_snd_cwnd(tp); - tcp_snd_cwnd_set(tp, - min(tp->snd_cwnd_clamp, - max((tcp_snd_cwnd(tp) * beta) - / BICTCP_BETA_SCALE, 2U))); - tp->snd_ssthresh = tcp_snd_cwnd(tp); + + if (ca->state == RTT_PROBE) { + /* In case we are in RTT_PROBE, continue with this state, as + * the CE was likely caused by the cwnd before probing. + * However do react to the CE. + */ + roccet_min_rtt_probe_ce(ca, curr_cwnd); + return; + } + + ca->cnt = 100 * curr_cwnd; + + /* Set W_max only if the current cwnd is larger */ + if (ca->last_max_cwnd < curr_cwnd) + ca->last_max_cwnd = curr_cwnd; + + /* Reduce cwnd by beta */ + tcp_snd_cwnd_set(tp, min(tp->snd_cwnd_clamp, + max((curr_cwnd * beta) + / BICTCP_BETA_SCALE, 2U))); + + if (ca->state == LAUNCH) { + /* Set ssthresh on ECN, so that roccet is not in slow-start */ + tp->snd_ssthresh = tcp_snd_cwnd(tp); + ca->state = ORBITER; + } else if (ca->state == ORBITER || ca->state == RTT_PROBE_REFILL) { + /* If we are in orbiter or currently refilling the pipe, + * abort the refill. + */ + ca->state = DRAIN; + } + /* Other states (e.g. DRAIN or RTT_PROBE_ENTER) not handled as + * we don`t want any action there. + */ } -/* Do minimum RTT probing. - */ -static __always_inline void roccet_min_rtt_probe(struct sock *sk, u32 now) +static void roccet_enter_min_rtt_probe(struct sock *sk, u32 now) { struct tcp_sock *tp = tcp_sk(sk); struct roccettcp *ca = inet_csk_ca(sk); u32 interval, probe_cwnd; - /* Do nothing if we are probing */ - if (before(now, ca->probe_min_rtt_until) && ca->probe_min_rtt_until > 0) + /* If probing is already set, there was a mix up. + * Continue anyway so we can recover. + */ + if (ca->probe_min_rtt_until > 0) + pr_warn_once("ROCCET: Probing time should not be set"); + + /* Safeguard for an "infinite" probe. This should logically never + * happen but still safeguard it. + */ + if (ca->curr_rtt == U32_MAX) { + pr_warn_once("ROCCET: curr_rtt is U32_MAX, cannot enter RTT_PROBE"); + ca->state = ORBITER; + return; + } + + /* Start min RTT probing */ + /* Probe 1*RTT or at least 200ms */ + interval = max(200 * USEC_PER_MSEC, ca->curr_rtt); + + /* This is to handle deep shared buffers with loss-based + * congestion control like CUBIC. If the cwnd is not limited + * by the application but falsely detected (see ROCCET paper), + * we have to empty the pipe more. + * If the limit detection is correct this will cause no harm + * to the tcp flow because the cwnd is not fully utilized and + * we set the cwnd to its previous value after probing. + */ + if (!tcp_is_cwnd_limited(sk)) + probe_cwnd = max(tcp_snd_cwnd(tp) / 3, TCP_INIT_CWND); + else + probe_cwnd = max(tcp_snd_cwnd(tp) / 2, TCP_INIT_CWND); + + ca->probe_min_rtt_until = now + interval; + ca->cwnd_before_min_rtt_probe = tcp_snd_cwnd(tp); + + /* Half the cwnd to drain the buffer for probing. */ + tcp_snd_cwnd_set(tp, probe_cwnd); + + /* Reset current min RTT to allow probing for + * a new lower and higher minimum RTT. + */ + ca->curr_min_rtt = U32_MAX; + + /* Refill the pipe after probing. + * For this we use the previous cwnd for another probing interval. + */ + ca->refill_until = ca->probe_min_rtt_until + interval; + + /* Now we know that ca->probe_min_rtt_until < ca->refill_until + * and we advance to the next state of the probing phase. + * + * Wrap-arounds of these values are handled by the relevant + * if-conditions. + */ + + ca->state = RTT_PROBE; +} + +/* Do minimum RTT probing. + */ +static void roccet_min_rtt_probe(struct sock *sk, u32 now) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct roccettcp *ca = inet_csk_ca(sk); + + /* Here we are in RTT_PROBE, so the probing time should be set. + * If not just continue and enter refill phase. + * + * probe_min_rtt_until wrap-arounds are handled by the time_before32 + * check. + */ + if (ca->probe_min_rtt_until == 0) + pr_warn_once("ROCCET: Probing time should be set"); + else if (time_before32(now, ca->probe_min_rtt_until)) + /* No state change if we are in the probing interval. */ return; - /* Start of min RTT probing*/ - if (ca->probe_min_rtt_until == 0) { - /* Probe 1*RTT or at least 200ms */ - interval = max(200 * USEC_PER_MSEC, ca->curr_rtt); + /* If the interval has passed we likely just entered the refill + * interval, so go into RTT_PROBE_REFILL. + * If we are not in the interval, something went wrong and we should + * quickly exit the probing phase. + * + * Wraps-arounds of refill_until are caught by the time_after32 check. + */ + if (time_after32(now, ca->refill_until)) + pr_warn_once("ROCCET: Skipped refill interval."); - /* This is to handle deep shared buffers with loss-based - * congestion control like CUBIC. If the cwnd is not limited - * by the application but falsely detected (see ROCCET paper), - * we have to empty the pipe more. - * If the limit detection is correct this will cause no harm - * to the tcp flow because the cwnd is not fully utilized and - * we set the cwnd to its previous value after probing. - */ - probe_cwnd = max(tcp_snd_cwnd(tp) / 2, TCP_INIT_CWND); - if (!tcp_is_cwnd_limited(sk)) - probe_cwnd = max(tcp_snd_cwnd(tp) / 3, TCP_INIT_CWND); + /* Reset cwnd to refill the pipe and consequently clear the stored + * window. + * Also perform sanity check. THis should never happen. + */ + if (ca->cwnd_before_min_rtt_probe == 0) + pr_warn_once("ROCCET: cwnd_before_min_rtt_probe is 0 during RTT_PROBE."); + else + tcp_snd_cwnd_set(tp, ca->cwnd_before_min_rtt_probe); - ca->probe_min_rtt_until = now + interval; - ca->cwnd_before_min_rtt_probe = tcp_snd_cwnd(tp); + ca->cwnd_before_min_rtt_probe = 0; - /* Half the cwnd to drain the buffer for probing. - * Set the ssthresh to the probing cwnd otherwise - * the TCP state machine is in slow start. - */ - tcp_snd_cwnd_set(tp, probe_cwnd); - tcp_sk(sk)->snd_ssthresh = tcp_snd_cwnd(tp); + /* Exit the RTT_PROBE state */ + ca->state = RTT_PROBE_REFILL; +} - /* Reset current min RTT to allow probing for - * a new lower and higher minimum RTT. - */ - ca->curr_min_rtt = ~0U; - - /* Refill the pipe after probing. - * To this end we need the previous cwnd over - * the probing interval. - */ - ca->refill_until = ca->probe_min_rtt_until + interval; - } else if (before(now, ca->refill_until)) { - /* Reset cwnd and refill the pipe. */ - if (ca->state != RTT_PROBE_REFILL) { - tcp_snd_cwnd_set(tp, ca->cwnd_before_min_rtt_probe); - tcp_sk(sk)->snd_ssthresh = tcp_snd_cwnd(tp); - ca->state = RTT_PROBE_REFILL; - } - } else { +static void roccet_rtt_probe_refill(struct roccettcp *ca, u32 now) +{ + /* Once the refill interval is over, we can end the probing phase. */ + if (time_after32(now, ca->refill_until)) { /* End min RTT probing phase. */ - ca->probe_min_rtt_until = 0; ca->state = ORBITER; } } -static void roccettcp_init(struct sock *sk) -{ - struct roccettcp *ca = inet_csk_ca(sk); - - roccettcp_reset(ca); - - if (initial_ssthresh) - tcp_sk(sk)->snd_ssthresh = initial_ssthresh; - - cmpxchg(&sk->sk_pacing_status, SK_PACING_NONE, SK_PACING_NEEDED); - //WRITE_ONCE(sk->sk_pacing_rate, 0); -} - -static void roccettcp_cwnd_event_tx_start(struct sock *sk) +static void roccet_cwnd_event_tx_start(struct sock *sk) { struct roccettcp *ca = inet_csk_ca(sk); u32 now = tcp_jiffies32; @@ -343,6 +526,8 @@ static void roccettcp_cwnd_event_tx_start(struct sock *sk) if (after(ca->epoch_start, now)) ca->epoch_start = now; } + + ca->was_idle = true; } /* calculate the cubic root of x using a table lookup followed by one @@ -393,8 +578,8 @@ static u32 cubic_root(u64 a) /* Compute congestion window to use. */ -static __always_inline void bictcp_update(struct roccettcp *ca, u32 cwnd, - u32 acked) +static void bictcp_update(struct roccettcp *ca, u32 cwnd, + u32 acked) { u32 delta, bic_target, max_cnt; u64 offs, t; @@ -433,6 +618,14 @@ static __always_inline void bictcp_update(struct roccettcp *ca, u32 cwnd, } } + /* In case there are any ACKs left over from LAUNCH, + * apply them after the new epoch-reset + */ + if (ca->ack_carry_over) { + ca->ack_cnt += ca->ack_carry_over; + ca->ack_carry_over = 0; + } + /* cubic function - calc */ /* calculate c * time^3 / rtt, * while considering overflow in calculation of time^3 @@ -484,9 +677,11 @@ tcp_friendliness: u32 scale = beta_scale; delta = (cwnd * scale) >> 3; - while (ca->ack_cnt > delta) { /* update tcp cwnd */ - ca->ack_cnt -= delta; - ca->tcp_cwnd++; + if (delta > 0) { + while (ca->ack_cnt > delta) { /* update tcp cwnd */ + ca->ack_cnt -= delta; + ca->tcp_cwnd++; + } } if (ca->tcp_cwnd > cwnd) { /* if bic is slower than tcp */ @@ -503,233 +698,298 @@ tcp_friendliness: ca->cnt = max(ca->cnt, 2U); } -static void roccettcp_cong_avoid(struct sock *sk, u32 ack, u32 acked) +static void roccet_launch_update(struct sock *sk, u32 acked) { struct tcp_sock *tp = tcp_sk(sk); struct roccettcp *ca = inet_csk_ca(sk); u32 now = jiffies_to_usecs(tcp_jiffies32); - bool evaluate_srrtt = false; - bool send_more_than_acked = false; - u32 roccet_xj; - u32 jitter; - u32 send, received; - if (ca->state == LAUNCH) { - /* 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 (tcp cwnd validation). - */ - if ((ca->curr_srrtt > sr_rtt_upper_bound && - get_ack_rate_diff(ca) <= ack_rate_diff_ss) || - !tcp_is_cwnd_limited(sk)) { - ca->epoch_start = 0; + /* 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 (tcp cwnd validation). + */ + if ((ca->curr_srrtt > sr_rtt_upper_bound && + get_ack_rate_diff(ca) <= ack_rate_diff_ss) || + (!tcp_is_cwnd_limited(sk) && ca->initial_limit_reached)) { + ca->epoch_start = 0; - /* Handle initial slow start. - * Most bufferbloat occurs here + /* Handle initial LAUNCH. Most bufferbloat occurs here */ + if (ca->is_in_initial_launch) { + /* Halving the cwnd will undo the previous step of slow + * start. Which is fine since the pipe is already full. */ - if (tp->snd_ssthresh == TCP_INFINITE_SSTHRESH) { - tcp_sk(sk)->snd_ssthresh = tcp_snd_cwnd(tp) - / 2; - /* since this is the initial slow start, - * the min cwnd won't be 1, so the window - * can't be set to 0 by accident. - * Halfing the cwnd will undo the previous step - * of slow start. Which is fine since the pipe - * is already full. - */ - tcp_snd_cwnd_set(tp, max(tcp_snd_cwnd(tp) / 2, - TCP_INIT_CWND)); - } else { - tcp_sk(sk)->snd_ssthresh = - tcp_snd_cwnd(tp) - - (tcp_snd_cwnd(tp) / 3); - tcp_snd_cwnd_set(tp, tcp_snd_cwnd(tp) - - (tcp_snd_cwnd(tp) / 3)); - } - ca->roccet_last_event_time_us = now; - return; - } - - acked = tcp_slow_start(tp, acked); - if (!acked) - return; - - } else if (ca->state == ORBITER) { - /* ORBITER: Increase the cwnd by using the CUBIC - * cwnd growth function, if no roccet congestion - * event is detechted. - */ - - /* Calculate jitter */ - if ((s32)(ca->curr_rtt - ca->last_rtt) < 0) - jitter = ca->last_rtt - ca->curr_rtt; - else - jitter = ca->curr_rtt - ca->last_rtt; - - if (ca->next_srrtt_check == 0) - ca->next_srrtt_check = now + 5 * ca->curr_rtt; - - /* Calculate if more bytes was send than received - * in the time interval. - */ - if (before(tp->snd_nxt, ca->interval_snd_seq_start)) { - /* We had a wrap around in seq no counter */ - send = (~0U - ca->interval_snd_seq_start + tp->snd_nxt); + tcp_snd_cwnd_set(tp, max(tcp_snd_cwnd(tp) / 2, + TCP_INIT_CWND)); } else { - send = (tp->snd_nxt - ca->interval_snd_seq_start); - } - if (before(tp->snd_una, ca->interval_una_seq_start)) { - /* We had a wrap around in seq no counter */ - received = (~0U - ca->interval_una_seq_start + - tp->snd_una); - } else { - received = (tp->snd_una - ca->interval_una_seq_start); + tcp_snd_cwnd_set(tp, max(tcp_snd_cwnd(tp) - + (tcp_snd_cwnd(tp) / 3), + TCP_INIT_CWND)); } + tp->snd_ssthresh = tcp_snd_cwnd(tp); + ca->roccet_last_event_time_us = now; + ca->state = ORBITER; + return; + } - /* Here we use a guard space of 1% of the current cwnd. - * We do this to avoid a false positive evaluation due - * to delays caused by jitter or scheduling. - */ - send_more_than_acked = - send > - received + ((tcp_snd_cwnd(tp) * tp->mss_cache) / 100); - - /* Check if it's time to evaluate the srRTT */ - if ((s32)(ca->next_srrtt_check - now) < 0) { - evaluate_srrtt = true; - - /* reset struct and set next end of period */ - ca->next_srrtt_check = now + 5 * ca->curr_rtt; - - /* Reset Rate calculation */ - ca->interval_snd_seq_start = tp->snd_nxt; - ca->interval_una_seq_start = tp->snd_una; - } - - /* Respects the jitter of the connection and add it on top of - * the upper bound for the srRTT. - */ - roccet_xj = div_u64((u64)jitter * 100, ca->curr_min_rtt) + - sr_rtt_upper_bound; - if (roccet_xj < sr_rtt_upper_bound) - roccet_xj = sr_rtt_upper_bound; - - /* The srRTT exceeds the upper bound if bufferbloat happens. - * Here, we want to reduce the cwnd and drain the buffer. - */ - if (ca->curr_srrtt > roccet_xj && evaluate_srrtt && - send_more_than_acked) { - roccet_congestion_event(sk, now); - return; - } - - /* Terminates this function if cwnd is not fully utilized. - * In mobile networks like 5G, this termination causes the - * cwnd to be frozen at an excessively high value. This is - * because slow start or HyStart massively exceed the available - * bandwidth and leave the cwnd at an excessively high value. - * The cwnd cannot therefore be fully utilized because it is - * limited by the connection capacity. - */ - if (!tcp_is_cwnd_limited(sk) || send_more_than_acked) - return; - - bictcp_update(ca, tcp_snd_cwnd(tp), acked); - tcp_cong_avoid_ai(tp, max(1, ca->cnt), acked); + /* If not already exiting LAUNCH, grow cwnd similar to slow-start */ + acked = tcp_slow_start(tp, acked); + /* If cwnd hits ssthresh, go to ORBITER and if any ACKs are + * leftover save them for ORBITER. + * Check via tcp_in_slow_start() in case no ACKs are left. + */ + if (!tcp_in_slow_start(tp)) { + ca->state = ORBITER; + ca->epoch_start = 0; + ca->ack_carry_over = acked; } } -static u32 roccettcp_recalc_ssthresh(struct sock *sk) +static void roccet_orbiter_update(struct sock *sk, u32 acked) +{ + /* ORBITER: Increase the cwnd by using the CUBIC cwnd growth function, + * if no roccet congestion event is detected. + */ + + struct tcp_sock *tp = tcp_sk(sk); + struct roccettcp *ca = inet_csk_ca(sk); + + u32 now = jiffies_to_usecs(tcp_jiffies32); + bool evaluate_srrtt = false; + bool sent_more_than_acked = false; + u32 roccet_xj, jitter, sent, received; + + /* Enter DRAIN when roccet was recently triggered */ + if (ca->roccet_last_event_time_us && + time_before32(now, ca->roccet_last_event_time_us + + 100 * USEC_PER_MSEC)) { + ca->state = DRAIN; + return; + } + + /* Enter RTT_PROBE when the "timer" has expired. + * + * Since we are in ORBITER, we should have already received at least + * one RTT sample. However safeguard against it if not. + */ + if (time_after32(now, ca->next_min_rtt_probe) && + ca->curr_rtt != U32_MAX) { + ca->state = RTT_PROBE_ENTER; + return; + } + + /* Calculate jitter. + * Since we are in ORBITER, we should have already received at least + * one RTT sample. Even if not, ca->curr_rtt and ca->curr_min_rtt + * (the divisor later on) are U32_MAX, so they cancel each other out. + * And if ca->last_rtt is U32_MAX, roccet_xj will be very large, so + * the srRTT will not exceed it. + */ + if ((s32)(ca->curr_rtt - ca->last_rtt) < 0) + jitter = ca->last_rtt - ca->curr_rtt; + else + jitter = ca->curr_rtt - ca->last_rtt; + + /* Calculate if more bytes were sent than received + * in the time interval. + * + * Handle wrap arounds by relying on unsigned subtraction. + * e.g. if snd_nxt wraps to 10 and seq_start is U32_MAX - 10, + * the subtraction will result in the value of 21. + */ + sent = tp->snd_nxt - ca->interval_snd_seq_start; + received = tp->snd_una - ca->interval_una_seq_start; + + /* Check sent and received bytes from the previous interval. + * Here we use a guard space of 1% of the current cwnd. + * We do this to avoid a false positive evaluation due + * to delays caused by jitter or scheduling. + */ + sent_more_than_acked = + sent > + received + ((tcp_snd_cwnd(tp) * tp->mss_cache) / 100); + + /* Check if it's time to evaluate the srRTT */ + if (time_after32(now, ca->next_srrtt_check_ts)) { + evaluate_srrtt = true; + + /* reset struct and set next end of period */ + ca->next_srrtt_check_ts = now + 5 * ca->curr_rtt; + + /* Reset Rate calculation */ + ca->interval_snd_seq_start = tp->snd_nxt; + ca->interval_una_seq_start = tp->snd_una; + } + + /* Respects the jitter of the connection and add it on top of + * the upper bound for the srRTT. + */ + roccet_xj = div_u64((u64)jitter * 100, ca->curr_min_rtt) + + sr_rtt_upper_bound; + + /* The srRTT exceeds the upper bound if bufferbloat happens. + * Here, we want to reduce the cwnd and drain the buffer. + */ + if (ca->curr_srrtt > roccet_xj && evaluate_srrtt && + sent_more_than_acked) { + roccet_congestion_event(sk, now); + return; + } + + /* Terminates this function if cwnd is not fully utilized. + * In mobile networks like 5G, this termination causes the + * cwnd to be frozen at an excessively high value. This is + * because slow start or HyStart massively exceed the available + * bandwidth and leave the cwnd at an excessively high value. + * The cwnd cannot therefore be fully utilized because it is + * limited by the connection capacity. + */ + if (!tcp_is_cwnd_limited(sk) || sent_more_than_acked) + return; + + bictcp_update(ca, tcp_snd_cwnd(tp), acked); + tcp_cong_avoid_ai(tp, max(1, ca->cnt), acked); +} + +/* The Cubic ssthresh calculation is also used for ROCCET. + * Called before TCP-CC state changes to TCP_CA_Recovery, TCP_CA_CWR or + * TCP_CA_Loss. cwnd reduction is then handled in the roccet_state() callback. + */ +static u32 roccet_recalc_ssthresh(struct sock *sk) { const struct tcp_sock *tp = tcp_sk(sk); struct roccettcp *ca = inet_csk_ca(sk); u32 cwnd = tcp_snd_cwnd(tp); - /* If a loss/ECN occurs in the refill phase of min RTT probing + /* In LAUNCH, we want no reduction on loss/ECN. + * On ECN this is set later on in roccet_state() + */ + if (ca->state == LAUNCH) + return cwnd; + + /* In min RTT probe, use the cwnd before the probe to not undershoot */ + if (ca->state == RTT_PROBE) + cwnd = ca->cwnd_before_min_rtt_probe; + + ca->epoch_start = 0; /* end of epoch */ + + /* Wmax and fast convergence */ + if (cwnd < ca->last_max_cwnd && fast_convergence) + ca->last_max_cwnd = (cwnd * (BICTCP_BETA_SCALE + beta)) + / (2 * BICTCP_BETA_SCALE); + else + ca->last_max_cwnd = cwnd; + + return max((cwnd * beta) / BICTCP_BETA_SCALE, 2U); +} + +/* Handle a recovery event and return the new cwnd. + */ +static u32 roccet_handle_recovery(struct sock *sk) +{ + const struct tcp_sock *tp = tcp_sk(sk); + struct roccettcp *ca = inet_csk_ca(sk); + u32 cwnd = tcp_snd_cwnd(tp); + + /* If a loss occurs in the refill phase of min RTT probing * we reduce the cwnd and abort the refill. */ if (ca->state == RTT_PROBE_REFILL) ca->state = ORBITER; - /* If ROCCET is in min RTT probing and a loss/ECN occurs, - * we use the cwnd before the probing interval to - * calculate the cwnd reduction and continue probing. - * After min RTT probing the cwnd is set to the reduced - * value. During min RTT probing it is very likely that - * congestion was caused by the cwnd value before min - * RTT probing. + /* In RTT_PROBE we don`t want to change state or immediately reduce + * the cwnd. */ if (ca->state == RTT_PROBE) { - /* Handle ECN as cubic congestion event in min - * RTT probe. - */ - ca->ece_received = false; - - ca->epoch_start = 0; /* end of epoch */ - - /* Wmax and fast convergence */ - if (cwnd < ca->last_max_cwnd && fast_convergence) - ca->last_max_cwnd = - (cwnd * (BICTCP_BETA_SCALE + beta)) / - (2 * BICTCP_BETA_SCALE); - else - ca->last_max_cwnd = cwnd; - - cwnd = ca->cwnd_before_min_rtt_probe; - ca->cwnd_before_min_rtt_probe = - max((cwnd * beta) / BICTCP_BETA_SCALE, 2U); + roccet_min_rtt_probe_ce(ca, cwnd); + return tcp_snd_cwnd(tp); + } + /* On loss in LAUNCH, enter ORBITER without a cwnd reduction. */ + if (ca->state == LAUNCH) { + ca->state = ORBITER; return cwnd; } - /* Handle ECN as ROCCET congestion event. */ - if (ca->ece_received) { - ca->ece_received = false; - roccet_congestion_event(sk, jiffies_to_usecs(tcp_jiffies32)); - return tcp_snd_cwnd(tp); - } - - /* On loss in slow start enter congestion avoidance - * without a cwnd reduction. Additional slow start - * exit conditions with a cwnd reduction are handled - * in roccettcp_cong_avoid. - */ - if (tcp_in_slow_start(tp)) - return tcp_snd_cwnd(tp); - - /*CUBIC congestion event*/ - ca->epoch_start = 0; /* end of epoch */ - - /* Wmax and fast convergence */ - if (tcp_snd_cwnd(tp) < ca->last_max_cwnd && fast_convergence) - ca->last_max_cwnd = - (tcp_snd_cwnd(tp) * (BICTCP_BETA_SCALE + beta)) / - (2 * BICTCP_BETA_SCALE); - else - ca->last_max_cwnd = tcp_snd_cwnd(tp); - - return max((tcp_snd_cwnd(tp) * beta) / BICTCP_BETA_SCALE, 2U); + return max((cwnd * beta) / BICTCP_BETA_SCALE, 2U); } -static void roccettcp_state(struct sock *sk, u8 new_state) +/* Checks for state roccet-transitions and performs necessary state (re)sets. + * This is done in order to avoid the possibility of forgetting to correctly + * set a state when entering certain states. + * + * This is used whenever a state change is possible + * (e.g. in roccet_state() or roccet_control()). + */ +static void roccet_handle_state_transitions(struct roccettcp *ca, + enum roccet_state prev_state, + u32 now) +{ + if (ca->state == prev_state) + return; + + /* When we exit LAUNCH we can be sure that we are no longer in the + * initial_launch. Even on LAUNCH to LAUNCH transitions (on RTO). + */ + if (prev_state == LAUNCH) + ca->is_in_initial_launch = false; + + /* Whenever we enter ORBITER, we need to schedule the next SRTT check. + * This will be set before evaluating the next_srrtt_check_ts condition + * as this is only done in ORBITER. + */ + if (ca->state == ORBITER) + ca->next_srrtt_check_ts = now + 5 * ca->curr_rtt; + + /* Whenever we leave the min RTT probing states (and not just + * transition between them), we want to reset the probing timers. + */ + if ((prev_state == RTT_PROBE_ENTER || prev_state == RTT_PROBE || + prev_state == RTT_PROBE_REFILL) && + (ca->state != RTT_PROBE && ca->state != RTT_PROBE_REFILL)) { + ca->probe_min_rtt_until = 0; + ca->refill_until = 0; + } +} + +/* Handle different loss-states and perform adequate cwnd reductions. + */ +static void roccet_state(struct sock *sk, u8 new_state) { struct roccettcp *ca = inet_csk_ca(sk); struct tcp_sock *tp = tcp_sk(sk); + u32 cwnd; + u32 now = jiffies_to_usecs(tcp_jiffies32); + enum roccet_state prev_state = ca->state; if (new_state == TCP_CA_Loss) { - roccettcp_reset(ca); + roccet_reset(sk, ca); + tcp_snd_cwnd_set(tp, TCP_INIT_CWND); + } else if (new_state == TCP_CA_CWR) { + /* Handle CWR as ROCCET congestion event, + * however afterwards always set Wmax to the current cwnd. + */ + cwnd = tcp_snd_cwnd(tp); + roccet_congestion_event(sk, now); + ca->last_max_cwnd = cwnd; } else if (new_state == TCP_CA_Recovery) { - tcp_sk(sk)->snd_ssthresh = roccettcp_recalc_ssthresh(sk); - tcp_snd_cwnd_set(tp, tcp_sk(sk)->snd_ssthresh); + /* Directly reduce cwnd and rely on pacing */ + cwnd = roccet_handle_recovery(sk); + tcp_snd_cwnd_set(tp, cwnd); } + + roccet_handle_state_transitions(ca, prev_state, now); } -static void roccettcp_acked(struct sock *sk, const struct ack_sample *sample) +/* Update RTT samples and min RTT. + */ +static void roccet_acked(struct sock *sk, const struct ack_sample *sample) { struct roccettcp *ca = inet_csk_ca(sk); + u32 delay; /* Some calls are for duplicates without timestamps */ if (sample->rtt_us < 0) @@ -739,12 +999,12 @@ static void roccettcp_acked(struct sock *sk, const struct ack_sample *sample) if (ca->epoch_start && (s32)(tcp_jiffies32 - ca->epoch_start) < HZ) return; - u32 delay = sample->rtt_us; + delay = sample->rtt_us; if (delay == 0) delay = 1; - /* first time call or link delay decreases */ + /* first call or link delay decreases */ if (ca->delay_min == 0 || (s32)(delay - ca->delay_min) < 0) ca->delay_min = delay; @@ -755,74 +1015,23 @@ static void roccettcp_acked(struct sock *sk, const struct ack_sample *sample) } } -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_recalc_ssthresh() - */ - if (flags & CA_ACK_ECE) - ca->ece_received = true; -} - -static void roccet_control(struct sock *sk, u32 ack, int flag, - const struct rate_sample *rs) +/* Custom Pacing Rate for ROCCET TCP. + * The code here is similar to the pacing rate adjustments in tcp_input.c + * tcp_cong_control(). In LAUNCH (slow start) we want a pacing of 200% and + * in ORBITER (congestion avoidance) we adjust the pacing to 100% and do not + * use the sysctl_tcp_pacing_ca_ratio. + */ +static void roccet_update_pacing_rate(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); - struct roccettcp *ca = inet_csk_ca(sk); - - u32 now = jiffies_to_usecs(tcp_jiffies32); u64 rate; - /* Update roccet parameters */ - update_ack_rate(sk, rs->acked_sacked, now); - update_min_rtt(sk); - update_srrtt(sk); - - /* Set values for send and receive rate */ - if (ca->interval_snd_seq_start == 0) { - ca->interval_snd_seq_start = tp->snd_nxt; - ca->interval_una_seq_start = tp->snd_una; - } - - /* Update roccet state */ - if (tcp_in_slow_start(tp)) { - ca->state = LAUNCH; - } else if ((s32)now - ca->roccet_last_event_time_us <= - 100 * USEC_PER_MSEC) { - ca->state = DRAIN; - } else if (after(now, ca->next_min_rtt_probe) || - ca->state == RTT_PROBE || ca->state == RTT_PROBE_REFILL) { - if (ca->state != RTT_PROBE_REFILL) - ca->state = RTT_PROBE; - roccet_min_rtt_probe(sk, now); - } else { - ca->state = ORBITER; - } - - /* If nothing was fully acked do not increase the cwnd */ - if (!rs->acked_sacked) - return; - - /* Increase the cwnd. - * Loss recovery is handled in roccettcp_state() - */ - roccettcp_cong_avoid(sk, ack, rs->acked_sacked); - - /* Adjust pacing rate. The code here is similar to the - * pacing rate adjustments in tcp_input.c tcp_cong_control(). - * In LAUNCH (slow start) we want a pacing of 200% and - * in ORBITER (congestion avoidance) we adjust the pacing - * to 100% and do not use the sysctl_tcp_pacing_ca_ratio. - */ - /* set sk_pacing_rate to 200 % of current rate (mss * cwnd / srtt) */ rate = (u64)tp->mss_cache * ((USEC_PER_SEC / 100) << 3); /* current rate is (cwnd * mss) / srtt - * In Slow Start [1], set sk_pacing_rate to 200 % the current rate. - * In Congestion Avoidance phase, set it to 120 % the current rate. + * In slow-start [1], set sk_pacing_rate to 200% the current rate. + * Otherwise, set it to 100% the current rate. * * [1]: Normal Slow Start cond is (tp->snd_cwnd < tp->snd_ssthresh) * If snd_cwnd >= (tp->snd_ssthresh / 2), we are approaching @@ -850,74 +1059,108 @@ static void roccet_control(struct sock *sk, u32 ack, int flag, min_t(u64, rate, READ_ONCE(sk->sk_max_pacing_rate))); } +static void roccet_drain_update(struct sock *sk, u32 now) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct roccettcp *ca = inet_csk_ca(sk); + + if (ca->roccet_last_event_time_us && + time_after32(now, ca->roccet_last_event_time_us + + 100 * USEC_PER_MSEC)) { + if (tcp_in_slow_start(tp)) + ca->state = LAUNCH; + else + ca->state = ORBITER; + } +} + +static void roccet_control(struct sock *sk, u32 ack, int flag, + const struct rate_sample *rs) +{ + struct roccettcp *ca = inet_csk_ca(sk); + + u32 now = jiffies_to_usecs(tcp_jiffies32); + enum roccet_state prev_state = ca->state; + + /* Update ack rate. Even on no new acks/sacks. */ + update_ack_rate(ca, rs->acked_sacked, now); + /* Only update RTT metrics if we have new acks/sacks in order to keep + * EWMA from running multiple times for no new acks. + */ + if (rs->acked_sacked > 0) { + update_min_rtt(ca); + update_srrtt(ca); + } + + /* Evaluate roccet state */ + switch (ca->state) { + case LAUNCH: + roccet_launch_update(sk, rs->acked_sacked); + break; + case ORBITER: + roccet_orbiter_update(sk, rs->acked_sacked); + break; + case DRAIN: + /* In DRAIN the cwnd should not be increased */ + roccet_drain_update(sk, now); + break; + case RTT_PROBE_ENTER: + roccet_enter_min_rtt_probe(sk, now); + break; + case RTT_PROBE: + roccet_min_rtt_probe(sk, now); + break; + case RTT_PROBE_REFILL: + roccet_rtt_probe_refill(ca, now); + break; + default: + pr_err_once("TCP ROCCET: Invalid state %d", ca->state); + } + + roccet_handle_state_transitions(ca, prev_state, now); + + roccet_update_pacing_rate(sk); + + if (tcp_is_cwnd_limited(sk)) + ca->initial_limit_reached = true; +} + static struct tcp_congestion_ops roccet_tcp __read_mostly = { - .init = roccettcp_init, - .ssthresh = roccettcp_recalc_ssthresh, - .set_state = roccettcp_state, + .init = roccet_init, + .ssthresh = roccet_recalc_ssthresh, + .set_state = roccet_state, .undo_cwnd = tcp_reno_undo_cwnd, - .cwnd_event_tx_start = roccettcp_cwnd_event_tx_start, - .pkts_acked = roccettcp_acked, - .in_ack_event = roccet_in_ack_event, + .cwnd_event_tx_start = roccet_cwnd_event_tx_start, + .pkts_acked = roccet_acked, .cong_control = roccet_control, .owner = THIS_MODULE, .name = "roccet", }; -static int __init roccettcp_register(void) +static int __init roccet_register(void) { + int param_err; + BUILD_BUG_ON(sizeof(struct roccettcp) > ICSK_CA_PRIV_SIZE); - /* - * Validate parameters to avoid division by zero errors. - */ - if (beta <= 0 || beta >= BICTCP_BETA_SCALE) { - pr_err("roccet: beta must be between 0 and %d\n", - BICTCP_BETA_SCALE); - return -EINVAL; - } + /* Check for valid parameter ranges and then precompute factors */ + param_err = param_check(); - if (bic_scale <= 0) { - pr_err("roccet: bic_scale must be positive\n"); - return -EINVAL; - } + if (param_err) + return param_err; - /* Precompute a bunch of the scaling factors that are used per-packet - * based on SRTT of 100ms - */ - beta_scale = - 8 * (BICTCP_BETA_SCALE + beta) / 3 / (BICTCP_BETA_SCALE - beta); - - cube_rtt_scale = (bic_scale * 10); /* 1024*c/rtt */ - - /* calculate the "K" for (wmax-cwnd) = c/rtt * K^3 - * so K = cubic_root( (wmax-cwnd)*rtt/c ) - * the unit of K is bictcp_HZ=2^10, not HZ - * - * c = bic_scale >> 10 - * rtt = 100ms - * - * the following code has been designed and tested for - * cwnd < 1 million packets - * RTT < 100 seconds - * HZ < 1,000,00 (corresponding to 10 nano-second) - */ - - /* 1/c * 2^2*bictcp_HZ * srtt */ - cube_factor = 1ull << (10 + 3 * BICTCP_HZ); /* 2^40 */ - - /* divide by bic_scale and by constant Srtt (100ms) */ - do_div(cube_factor, bic_scale * 10); + param_precompute(); return tcp_register_congestion_control(&roccet_tcp); } -static void __exit roccettcp_unregister(void) +static void __exit roccet_unregister(void) { tcp_unregister_congestion_control(&roccet_tcp); } -module_init(roccettcp_register); -module_exit(roccettcp_unregister); +module_init(roccet_register); +module_exit(roccet_unregister); MODULE_AUTHOR("Lukas Prause, Tim Füchsel"); MODULE_LICENSE("GPL"); diff --git a/tcp_roccet.h b/tcp_roccet.h index b50e8ea..85fbdda 100644 --- a/tcp_roccet.h +++ b/tcp_roccet.h @@ -11,6 +11,7 @@ enum roccet_state { LAUNCH, ORBITER, + RTT_PROBE_ENTER, RTT_PROBE, RTT_PROBE_REFILL, DRAIN @@ -34,14 +35,18 @@ struct roccettcp { u32 tcp_cwnd; /* estimated tcp cwnd */ u32 curr_rtt; /* last sample rtt of current round */ - u32 roccet_last_event_time_us; /* The last time ROCCET was triggered */ + u32 roccet_last_event_time_us; /* The last time ROCCET was triggered */ u32 curr_min_rtt; /* The current observed minRTT */ u32 next_min_rtt_probe; /* Next time to probe the minRTT */ - u32 probe_min_rtt_until; /* End of minRTT probing period */ - u32 refill_until; /* End of pipe refill after minRTT probe */ - u32 cwnd_before_min_rtt_probe; /* cwnd before min RTT probeing */ + u32 probe_min_rtt_until; /* End of minRTT probing period. + * Set while in RTT_PROBE states + */ + u32 refill_until; /* End of pipe refill after minRTT probe. + * Set while in RTT_PROBE states + */ + u32 cwnd_before_min_rtt_probe; /* cwnd before min RTT probing. */ u32 curr_srrtt; /* srRTT calculated based on the latest ACK */ - u32 next_srrtt_check; /* Next check for srRTT */ + u32 next_srrtt_check_ts; /* Next check for srRTT */ u32 last_rtt; /* sample rtt of previous round. * Used for jitter calculation */ @@ -54,8 +59,19 @@ struct roccettcp { u16 ack_rate_curr_rate; /* Current ACK-rate */ u16 ack_rate_cnt; /* Used for counting acks */ - bool ece_received; /* Set to true if an ECE bit was received */ - enum roccet_state state; /* State in which roccet currently operates */ + enum roccet_state state : 3; /* Current operating state of roccet */ + bool was_idle : 1; /* Tracks whether the connection was idle + * (had no in-flight packets) + */ + bool initial_limit_reached: 1; /* Set to true after the connection + * initially gets cwnd-limited + */ + bool is_in_initial_launch: 1; /* true if the connection is in + * the initial launch phase. + */ + u32 ack_carry_over; /* Used to carry over leftover acks from + * LAUNCH to ORBITER + */ }; #endif /* __TCP_ROCCET_H */