Update the repository with the changes made since the last LKML review.

This commit is contained in:
Lukas Prause
2026-09-30 12:47:22 +02:00
parent 64d0e54f87
commit 956f195ddc
2 changed files with 283 additions and 118 deletions
+265 -105
View File
@@ -67,6 +67,13 @@
/* min RTT probe period in ms */ /* min RTT probe period in ms */
#define ROCCET_NEXT_MIN_RTT_PROBE 5000 #define ROCCET_NEXT_MIN_RTT_PROBE 5000
/* The maximum interval length of a min RTT probe
* (and consequently also the max interval for the refill).
*/
#define ROCCET_MAX_PROBE_INTERVAL_MS 10000
#define ROCCET_MAX_SRRTT_CHECK_INTERVAL_MS (5 * 200)
/* Parameters that are specific to the ROCCET-Algorithm */ /* Parameters that are specific to the ROCCET-Algorithm */
static uint sr_rtt_upper_bound __read_mostly = 100; static uint sr_rtt_upper_bound __read_mostly = 100;
static int ack_rate_diff_ss __read_mostly = 10; static int ack_rate_diff_ss __read_mostly = 10;
@@ -87,11 +94,16 @@ static u32 cube_rtt_scale __read_mostly;
static u32 beta_scale __read_mostly; static u32 beta_scale __read_mostly;
static u64 cube_factor __read_mostly; static u64 cube_factor __read_mostly;
/* Note parameters that are used for precomputing scale factors are read-only */ static int beta_param_set(const char *val, const struct kernel_param *kp);
static const struct kernel_param_ops beta_param_ops = {
.set = beta_param_set,
.get = param_get_int,
};
module_param_cb(beta, &beta_param_ops, &beta, 0644);
MODULE_PARM_DESC(beta, "beta for multiplicative increase");
module_param(fast_convergence, int, 0644); module_param(fast_convergence, int, 0644);
MODULE_PARM_DESC(fast_convergence, "turn on/off fast convergence"); MODULE_PARM_DESC(fast_convergence, "turn on/off fast convergence");
module_param(beta, int, 0644);
MODULE_PARM_DESC(beta, "beta for multiplicative increase");
module_param(initial_ssthresh, int, 0644); module_param(initial_ssthresh, int, 0644);
MODULE_PARM_DESC(initial_ssthresh, "initial value of slow start threshold"); MODULE_PARM_DESC(initial_ssthresh, "initial value of slow start threshold");
module_param(bic_scale, int, 0444); module_param(bic_scale, int, 0444);
@@ -100,6 +112,26 @@ MODULE_PARM_DESC(bic_scale,
module_param(tcp_friendliness, int, 0644); module_param(tcp_friendliness, int, 0644);
MODULE_PARM_DESC(tcp_friendliness, "turn on/off tcp friendliness"); MODULE_PARM_DESC(tcp_friendliness, "turn on/off tcp friendliness");
/* Check & set the new beta parameter candidate value.
*/
static int beta_param_set(const char *val, const struct kernel_param *kp)
{
int beta_candidate;
int ret = kstrtoint(val, 10, &beta_candidate);
if (ret)
return ret;
if (beta_candidate <= 0 || beta_candidate >= BICTCP_BETA_SCALE) {
pr_err_once("TCP ROCCET: beta must be between 0 and %d\n",
BICTCP_BETA_SCALE);
return -EINVAL;
}
return param_set_int(val, kp);
}
/* Used to check certain roccet parameters used in `param_precompute` in order /* Used to check certain roccet parameters used in `param_precompute` in order
* to avoid invalid scale-related calculations. This validates the specified * to avoid invalid scale-related calculations. This validates the specified
* parameters or rejects them. * parameters or rejects them.
@@ -169,14 +201,19 @@ static void roccet_reset(struct sock *sk, struct roccettcp *ca)
ca->curr_rtt = U32_MAX; ca->curr_rtt = U32_MAX;
ca->last_rtt = U32_MAX; ca->last_rtt = U32_MAX;
ca->roccet_last_event_time_us = 0; /* Initialize the last event time to the current time, to use it for
* temporarily knowing when the connection started (only used for this
* after init).
*/
ca->roccet_last_event_time_us = jiffies_to_usecs(tcp_jiffies32);
ca->last_event_time_set = false;
ca->ack_rate_last_rate = 0; ca->ack_rate_last_rate = 0;
/* Initialize to current time to avoid an /* Initialize to current time to avoid an
* overflow in the ack rate calculation * overflow in the ack rate calculation
*/ */
ca->ack_rate_last_rate_time = jiffies_to_usecs(tcp_jiffies32); ca->last_ack_rate_time = jiffies_to_usecs(tcp_jiffies32);
ca->ack_rate_curr_rate = 0; ca->curr_ack_rate = 0;
ca->ack_rate_cnt = 0; ca->ack_rate_cnt = 0;
ca->interval_snd_seq_start = tcp_sk(sk)->snd_nxt; ca->interval_snd_seq_start = tcp_sk(sk)->snd_nxt;
@@ -186,8 +223,8 @@ static void roccet_reset(struct sock *sk, struct roccettcp *ca)
ca->state = LAUNCH; ca->state = LAUNCH;
ca->probe_min_rtt_until = 0; ca->probe_min_rtt_until = 0;
ca->refill_until = 0; ca->refill_until = 0;
ca->rtt_probe_timers_set = false;
ca->initial_limit_reached = false;
ca->is_in_initial_launch = false; ca->is_in_initial_launch = false;
} }
@@ -197,7 +234,8 @@ static void roccet_init(struct sock *sk)
roccet_reset(sk, ca); roccet_reset(sk, ca);
/* Reset here, so it is only set during init */ /* Reset some parameters here, so they only get set during init */
ca->initial_limit_reached = false;
ca->is_in_initial_launch = true; ca->is_in_initial_launch = true;
if (initial_ssthresh) if (initial_ssthresh)
@@ -206,6 +244,32 @@ static void roccet_init(struct sock *sk)
cmpxchg(&sk->sk_pacing_status, SK_PACING_NONE, SK_PACING_NEEDED); cmpxchg(&sk->sk_pacing_status, SK_PACING_NONE, SK_PACING_NEEDED);
} }
static void roccet_set_next_srrtt_check_ts(struct roccettcp *ca, u32 now)
{
/* Check the srrtt after 5 RTT's. Cap the assumed RTT's
* at 200 ms each => 1 sec max.
*/
ca->next_srrtt_check_ts = now + min(5 * ca->curr_rtt,
ROCCET_MAX_SRRTT_CHECK_INTERVAL_MS
* USEC_PER_MSEC);
}
static void roccet_set_next_min_rtt_probe_ts(struct roccettcp *ca)
{
/* Probe for the min RTT in ROCCET_NEXT_MIN_RTT_PROBE_MS
* if no other update occurs.
* If we are in either RTT_PROBE or RTT_PROBE_REFILL, don't set
* the next probe time so we know when we approximately entered
* the probing phase.
*/
if (ca->state == RTT_PROBE || ca->state == RTT_PROBE_REFILL)
return;
ca->next_min_rtt_probe = jiffies_to_usecs(tcp_jiffies32) +
ROCCET_NEXT_MIN_RTT_PROBE_MS * USEC_PER_MSEC;
}
static void update_min_rtt(struct roccettcp *ca) static void update_min_rtt(struct roccettcp *ca)
{ {
/* Check if new lower min RTT was found. If so, set it directly. /* Check if new lower min RTT was found. If so, set it directly.
@@ -214,12 +278,7 @@ static void update_min_rtt(struct roccettcp *ca)
*/ */
if (ca->curr_rtt < ca->curr_min_rtt) { if (ca->curr_rtt < ca->curr_min_rtt) {
ca->curr_min_rtt = max(ca->curr_rtt, 1); ca->curr_min_rtt = max(ca->curr_rtt, 1);
/* Probe for the min RTT in ROCCET_NEXT_MIN_RTT_PROBE ms roccet_set_next_min_rtt_probe_ts(ca);
* if no other update occurs.
*/
ca->next_min_rtt_probe =
jiffies_to_usecs(tcp_jiffies32) +
ROCCET_NEXT_MIN_RTT_PROBE * USEC_PER_MSEC;
} }
} }
@@ -227,9 +286,9 @@ static void update_min_rtt(struct roccettcp *ca)
*/ */
static 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) if (ca->curr_ack_rate < ca->ack_rate_last_rate)
return 0; return 0;
return (s32)(ca->ack_rate_curr_rate - ca->ack_rate_last_rate); return (s32)(ca->curr_ack_rate - ca->ack_rate_last_rate);
} }
/* Update ack rate sampled by 100ms. /* Update ack rate sampled by 100ms.
@@ -239,37 +298,41 @@ static void update_ack_rate(struct roccettcp *ca, u32 acked, u32 now)
const s32 idle_threshold = USEC_PER_SEC * 2; const s32 idle_threshold = USEC_PER_SEC * 2;
const s32 interval = USEC_PER_MSEC * 100; const s32 interval = USEC_PER_MSEC * 100;
s32 time_delta = (s32)(ca->ack_rate_last_rate_time - now); /* Check if the last interval has elapsed.
*
* Check via time_between32, so that idle connections
* (for more than U32_MAX/2 usecs), also trigger a new update
* and don't stall the ack-counting.
*/
bool interval_elapsed = !time_between32(now, ca->last_ack_rate_time
- interval,
ca->last_ack_rate_time
+ interval);
/* Check if the time has arrived in the new interval. if (interval_elapsed) {
* Alternatively if the connection was considered to be idle, /* Check if the connection was idle for *idle_threshold*
* also treat as a new interval in order to avoid timing-overflow * seconds (e.g. no ACK for X seconds). If so, reset the ack
* problems. * counting as if a new connection was created.
*/
if (time_delta < -interval || ca->was_idle) {
/* Check if the connection was idle for X seconds
* (e.g. no ACK for X seconds)
*/
if (time_delta < -idle_threshold) {
/* Reset ack counting as if a new
* connection was created
*/ */
if (!time_between32(now, ca->last_ack_rate_time
- idle_threshold,
ca->last_ack_rate_time
+ idle_threshold)) {
ca->ack_rate_last_rate = 0; ca->ack_rate_last_rate = 0;
ca->ack_rate_last_rate_time = ca->curr_ack_rate = 0;
jiffies_to_usecs(tcp_jiffies32);
ca->ack_rate_curr_rate = 0;
ca->ack_rate_cnt = 0;
} else { } else {
/* start counting for the new interval */ /* start counting for the new interval */
ca->ack_rate_last_rate_time = now; ca->ack_rate_last_rate = ca->curr_ack_rate;
ca->ack_rate_last_rate = ca->ack_rate_curr_rate; ca->curr_ack_rate = ca->ack_rate_cnt;
ca->ack_rate_curr_rate = ca->ack_rate_cnt;
ca->ack_rate_cnt = min_t(u32, acked, U16_MAX);
} }
/* Start ack-counting with the already present ACKs */
ca->was_idle = false; ca->ack_rate_cnt = min_t(u32, acked, U16_MAX);
ca->last_ack_rate_time = now;
} else { } else {
/* Cap the ack count to avoid overflow */ /* Cap the ack count to avoid overflow.
* If we there are more than U16_MAX ACKs in 100ms,
* the resulting LAUNCH-exit won't cause any harm.
*/
ca->ack_rate_cnt = min_t(u32, ca->ack_rate_cnt + acked, ca->ack_rate_cnt = min_t(u32, ca->ack_rate_cnt + acked,
U16_MAX); U16_MAX);
} }
@@ -295,7 +358,7 @@ static void update_srrtt(struct roccettcp *ca)
* anyways in order to avoid division by zero errors. * anyways in order to avoid division by zero errors.
*/ */
if (ca->curr_min_rtt == 0) { if (ca->curr_min_rtt == 0) {
pr_err_once("TCP ROCCET: Recorded curr_min_rtt is 0"); pr_err_once("TCP ROCCET: Recorded curr_min_rtt is 0\n");
return; /* skip srRTT update */ return; /* skip srRTT update */
} }
@@ -338,7 +401,7 @@ static void roccet_min_rtt_probe_ce(struct roccettcp *ca, u32 cwnd)
*/ */
if (ca->cwnd_before_min_rtt_probe == 0) 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."); pr_warn_once("TCP ROCCET: cwnd_before_min_rtt_probe is 0 during RTT_PROBE. This should not happen.\n");
else else
cwnd = ca->cwnd_before_min_rtt_probe; cwnd = ca->cwnd_before_min_rtt_probe;
@@ -357,6 +420,7 @@ static void roccet_congestion_event(struct sock *sk, u32 now)
ca->epoch_start = 0; ca->epoch_start = 0;
ca->roccet_last_event_time_us = now; ca->roccet_last_event_time_us = now;
ca->last_event_time_set = true;
if (ca->state == RTT_PROBE) { if (ca->state == RTT_PROBE) {
/* In case we are in RTT_PROBE, continue with this state, as /* In case we are in RTT_PROBE, continue with this state, as
@@ -378,9 +442,10 @@ static void roccet_congestion_event(struct sock *sk, u32 now)
max((curr_cwnd * beta) max((curr_cwnd * beta)
/ BICTCP_BETA_SCALE, 2U))); / BICTCP_BETA_SCALE, 2U)));
/* Re-adjust ssthresh in case of ORBITER Congestion Event */
WRITE_ONCE(tp->snd_ssthresh, tcp_snd_cwnd(tp));
if (ca->state == LAUNCH) { 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; ca->state = ORBITER;
} else if (ca->state == ORBITER || ca->state == RTT_PROBE_REFILL) { } else if (ca->state == ORBITER || ca->state == RTT_PROBE_REFILL) {
/* If we are in orbiter or currently refilling the pipe, /* If we are in orbiter or currently refilling the pipe,
@@ -402,21 +467,31 @@ static void roccet_enter_min_rtt_probe(struct sock *sk, u32 now)
/* If probing is already set, there was a mix up. /* If probing is already set, there was a mix up.
* Continue anyway so we can recover. * Continue anyway so we can recover.
*/ */
if (ca->probe_min_rtt_until > 0) if (ca->rtt_probe_timers_set)
pr_warn_once("ROCCET: Probing time should not be set"); pr_warn_once("TCP ROCCET: Probing time should not be set when entering RTT Probe\n");
/* Safeguard for an "infinite" probe. This should logically never /* Safeguard for an "infinite" probe. This should logically never
* happen but still safeguard it. * happen but still safeguard it.
*/ */
if (ca->curr_rtt == U32_MAX) { if (ca->curr_rtt == U32_MAX) {
pr_warn_once("ROCCET: curr_rtt is U32_MAX, cannot enter RTT_PROBE"); pr_warn_once("TCP ROCCET: curr_rtt is U32_MAX. Won't enter RTT_PROBE\n");
ca->state = ORBITER; ca->state = ORBITER;
return; return;
} }
/* Start min RTT probing */ /* Use field to store when we entered min RTT probing. Here we use
/* Probe 1*RTT or at least 200ms */ * jiffies to stay consistent with the epoch_start format.
interval = max(200 * USEC_PER_MSEC, ca->curr_rtt); */
ca->next_min_rtt_probe = tcp_jiffies32;
/* Start min RTT probing. Probe for 1 RTT or at least 200ms.
* Also bound the probe to a maximum of ROCCET_MAX_PROBE_INTERVAL_MS
* to avoid any "invalid"/very large RTT samples from breaking
* the probing and allowing ROCCET to recover afterwards.
*/
interval = max(200 * USEC_PER_MSEC, min(ca->curr_rtt,
ROCCET_MAX_PROBE_INTERVAL_MS
* USEC_PER_MSEC));
/* This is to handle deep shared buffers with loss-based /* This is to handle deep shared buffers with loss-based
* congestion control like CUBIC. If the cwnd is not limited * congestion control like CUBIC. If the cwnd is not limited
@@ -434,7 +509,7 @@ static void roccet_enter_min_rtt_probe(struct sock *sk, u32 now)
ca->probe_min_rtt_until = now + interval; ca->probe_min_rtt_until = now + interval;
ca->cwnd_before_min_rtt_probe = tcp_snd_cwnd(tp); ca->cwnd_before_min_rtt_probe = tcp_snd_cwnd(tp);
/* Half the cwnd to drain the buffer for probing. */ /* Reduce the cwnd to drain the buffer for probing. */
tcp_snd_cwnd_set(tp, probe_cwnd); tcp_snd_cwnd_set(tp, probe_cwnd);
/* Reset current min RTT to allow probing for /* Reset current min RTT to allow probing for
@@ -446,6 +521,7 @@ static void roccet_enter_min_rtt_probe(struct sock *sk, u32 now)
* For this we use the previous cwnd for another probing interval. * For this we use the previous cwnd for another probing interval.
*/ */
ca->refill_until = ca->probe_min_rtt_until + interval; ca->refill_until = ca->probe_min_rtt_until + interval;
ca->rtt_probe_timers_set = true; /* Now both timers are set. */
/* Now we know that ca->probe_min_rtt_until < ca->refill_until /* Now we know that ca->probe_min_rtt_until < ca->refill_until
* and we advance to the next state of the probing phase. * and we advance to the next state of the probing phase.
@@ -453,7 +529,6 @@ static void roccet_enter_min_rtt_probe(struct sock *sk, u32 now)
* Wrap-arounds of these values are handled by the relevant * Wrap-arounds of these values are handled by the relevant
* if-conditions. * if-conditions.
*/ */
ca->state = RTT_PROBE; ca->state = RTT_PROBE;
} }
@@ -470,28 +545,31 @@ static void roccet_min_rtt_probe(struct sock *sk, u32 now)
* probe_min_rtt_until wrap-arounds are handled by the time_before32 * probe_min_rtt_until wrap-arounds are handled by the time_before32
* check. * check.
*/ */
if (ca->probe_min_rtt_until == 0) if (!ca->rtt_probe_timers_set)
pr_warn_once("ROCCET: Probing time should be set"); pr_warn_once("TCP ROCCET: Probing time should be set\n");
else if (time_before32(now, ca->probe_min_rtt_until)) else if (time_between32(now, ca->probe_min_rtt_until
- ROCCET_MAX_PROBE_INTERVAL_MS
* USEC_PER_MSEC, ca->probe_min_rtt_until))
/* No state change if we are in the probing interval. */ /* No state change if we are in the probing interval. */
return; return;
else if (time_after32(now, ca->refill_until))
/* If the interval has passed we likely just entered the refill /* If the probe_min_rtt_until interval has passed we likely
* interval, so go into RTT_PROBE_REFILL. * just entered the refill interval, so go into
* If we are not in the interval, something went wrong and we should * RTT_PROBE_REFILL. If we are not in the refill_until
* quickly exit the probing phase. * interval, something went wrong and we should quickly exit
* the probing phase.
* *
* Wraps-arounds of refill_until are caught by the time_after32 check. * Wraps-arounds of refill_until are caught by the time_after32
* check.
*/ */
if (time_after32(now, ca->refill_until)) pr_warn_once("TCP ROCCET: Skipped refill interval.\n");
pr_warn_once("ROCCET: Skipped refill interval.");
/* Reset cwnd to refill the pipe and consequently clear the stored /* Reset cwnd to refill the pipe and consequently clear the stored
* window. * window.
* Also perform sanity check. THis should never happen. * Also perform sanity check. This should never happen.
*/ */
if (ca->cwnd_before_min_rtt_probe == 0) if (ca->cwnd_before_min_rtt_probe == 0)
pr_warn_once("ROCCET: cwnd_before_min_rtt_probe is 0 during RTT_PROBE."); pr_warn_once("TCP ROCCET: cwnd_before_min_rtt_probe is 0 during RTT_PROBE.\n");
else else
tcp_snd_cwnd_set(tp, ca->cwnd_before_min_rtt_probe); tcp_snd_cwnd_set(tp, ca->cwnd_before_min_rtt_probe);
@@ -504,7 +582,9 @@ static void roccet_min_rtt_probe(struct sock *sk, u32 now)
static void roccet_rtt_probe_refill(struct roccettcp *ca, u32 now) static void roccet_rtt_probe_refill(struct roccettcp *ca, u32 now)
{ {
/* Once the refill interval is over, we can end the probing phase. */ /* Once the refill interval is over, we can end the probing phase. */
if (time_after32(now, ca->refill_until)) { if (!time_between32(now, ca->refill_until
- ROCCET_MAX_PROBE_INTERVAL_MS
* USEC_PER_MSEC, ca->refill_until)) {
/* End min RTT probing phase. */ /* End min RTT probing phase. */
ca->state = ORBITER; ca->state = ORBITER;
} }
@@ -526,8 +606,6 @@ static void roccet_cwnd_event_tx_start(struct sock *sk)
if (after(ca->epoch_start, now)) if (after(ca->epoch_start, now))
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 /* calculate the cubic root of x using a table lookup followed by one
@@ -618,14 +696,6 @@ static 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 */ /* cubic function - calc */
/* calculate c * time^3 / rtt, /* calculate c * time^3 / rtt,
* while considering overflow in calculation of time^3 * while considering overflow in calculation of time^3
@@ -714,6 +784,7 @@ static void roccet_launch_update(struct sock *sk, u32 acked)
*/ */
if ((ca->curr_srrtt > sr_rtt_upper_bound && if ((ca->curr_srrtt > sr_rtt_upper_bound &&
get_ack_rate_diff(ca) <= ack_rate_diff_ss) || get_ack_rate_diff(ca) <= ack_rate_diff_ss) ||
!tcp_in_slow_start(tp) ||
(!tcp_is_cwnd_limited(sk) && ca->initial_limit_reached)) { (!tcp_is_cwnd_limited(sk) && ca->initial_limit_reached)) {
ca->epoch_start = 0; ca->epoch_start = 0;
@@ -729,8 +800,9 @@ static void roccet_launch_update(struct sock *sk, u32 acked)
(tcp_snd_cwnd(tp) / 3), (tcp_snd_cwnd(tp) / 3),
TCP_INIT_CWND)); TCP_INIT_CWND));
} }
tp->snd_ssthresh = tcp_snd_cwnd(tp); WRITE_ONCE(tp->snd_ssthresh, tcp_snd_cwnd(tp));
ca->roccet_last_event_time_us = now; ca->roccet_last_event_time_us = now;
ca->last_event_time_set = true;
ca->state = ORBITER; ca->state = ORBITER;
return; return;
} }
@@ -763,9 +835,11 @@ static void roccet_orbiter_update(struct sock *sk, u32 acked)
u32 roccet_xj, jitter, sent, received; u32 roccet_xj, jitter, sent, received;
/* Enter DRAIN when roccet was recently triggered */ /* Enter DRAIN when roccet was recently triggered */
if (ca->roccet_last_event_time_us && if (ca->last_event_time_set &&
time_before32(now, ca->roccet_last_event_time_us + time_between32(now, ca->roccet_last_event_time_us
100 * USEC_PER_MSEC)) { - 100 * USEC_PER_MSEC,
ca->roccet_last_event_time_us
+ 100 * USEC_PER_MSEC)) {
ca->state = DRAIN; ca->state = DRAIN;
return; return;
} }
@@ -774,8 +848,13 @@ static void roccet_orbiter_update(struct sock *sk, u32 acked)
* *
* Since we are in ORBITER, we should have already received at least * Since we are in ORBITER, we should have already received at least
* one RTT sample. However safeguard against it if not. * one RTT sample. However safeguard against it if not.
*
* Use double the probing period as a guard space to avoid
* idle-time locking us out of probing.
*/ */
if (time_after32(now, ca->next_min_rtt_probe) && if (!time_between32(now, ca->next_min_rtt_probe
- 2 * ROCCET_NEXT_MIN_RTT_PROBE_MS
* USEC_PER_MSEC, ca->next_min_rtt_probe) &&
ca->curr_rtt != U32_MAX) { ca->curr_rtt != U32_MAX) {
ca->state = RTT_PROBE_ENTER; ca->state = RTT_PROBE_ENTER;
return; return;
@@ -808,16 +887,18 @@ static void roccet_orbiter_update(struct sock *sk, u32 acked)
* We do this to avoid a false positive evaluation due * We do this to avoid a false positive evaluation due
* to delays caused by jitter or scheduling. * to delays caused by jitter or scheduling.
*/ */
sent_more_than_acked = sent_more_than_acked = (s32)(sent - received) >
sent > div_u64((u64)tcp_snd_cwnd(tp) *
received + ((tcp_snd_cwnd(tp) * tp->mss_cache) / 100); (u64)tp->mss_cache, 100);
/* Check if it's time to evaluate the srRTT */ /* Check if it's time to evaluate the srRTT */
if (time_after32(now, ca->next_srrtt_check_ts)) { if (!time_between32(now, ca->next_srrtt_check_ts
- 2 * ROCCET_MAX_SRRTT_CHECK_INTERVAL_MS
* USEC_PER_MSEC, ca->next_srrtt_check_ts)) {
evaluate_srrtt = true; evaluate_srrtt = true;
/* reset struct and set next end of period */ /* reset struct and set next end of period */
ca->next_srrtt_check_ts = now + 5 * ca->curr_rtt; roccet_set_next_srrtt_check_ts(ca, now);
/* Reset Rate calculation */ /* Reset Rate calculation */
ca->interval_snd_seq_start = tp->snd_nxt; ca->interval_snd_seq_start = tp->snd_nxt;
@@ -846,10 +927,21 @@ static void roccet_orbiter_update(struct sock *sk, u32 acked)
* bandwidth and leave the cwnd at an excessively high value. * bandwidth and leave the cwnd at an excessively high value.
* The cwnd cannot therefore be fully utilized because it is * The cwnd cannot therefore be fully utilized because it is
* limited by the connection capacity. * limited by the connection capacity.
*
* Also avoid cwnd-growth if we are in cwnd reduction phase.
*/ */
if (!tcp_is_cwnd_limited(sk) || sent_more_than_acked) if (!tcp_is_cwnd_limited(sk) || sent_more_than_acked ||
tcp_in_cwnd_reduction(sk))
return; return;
/* In case there are any ACKs left over from LAUNCH,
* apply them now.
*/
if (ca->ack_carry_over) {
acked += ca->ack_carry_over;
ca->ack_carry_over = 0;
}
bictcp_update(ca, tcp_snd_cwnd(tp), acked); bictcp_update(ca, tcp_snd_cwnd(tp), acked);
tcp_cong_avoid_ai(tp, max(1, ca->cnt), acked); tcp_cong_avoid_ai(tp, max(1, ca->cnt), acked);
} }
@@ -924,25 +1016,63 @@ static u32 roccet_handle_recovery(struct sock *sk)
* This is used whenever a state change is possible * This is used whenever a state change is possible
* (e.g. in roccet_state() or roccet_control()). * (e.g. in roccet_state() or roccet_control()).
*/ */
static void roccet_handle_state_transitions(struct roccettcp *ca, static void roccet_handle_state_transitions(struct sock *sk,
enum roccet_state prev_state, enum roccet_state prev_state,
u32 now) u32 now)
{ {
struct roccettcp *ca = inet_csk_ca(sk);
struct tcp_sock *tp = tcp_sk(sk);
u32 now_jiffies = tcp_jiffies32;
if (ca->state == prev_state) if (ca->state == prev_state)
return; return;
/* When we exit LAUNCH we can be sure that we are no longer in the /* 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). * initial_launch. Even on LAUNCH to LAUNCH transitions (on RTO).
* Also on any state transition from LAUNCH, we can say that the
* tcp_is_cwnd_limited() is now valid.
*/ */
if (prev_state == LAUNCH) if (prev_state == LAUNCH) {
ca->is_in_initial_launch = false; ca->is_in_initial_launch = false;
ca->initial_limit_reached = true;
}
/* Whenever we enter ORBITER, we need to schedule the next SRTT check. /* Whenever we enter ORBITER, we need to schedule the next SRTT check.
* This will be set before evaluating the next_srrtt_check_ts condition * This will be set before evaluating the next_srrtt_check_ts condition
* as this is only done in ORBITER. * as this is only done in ORBITER.
*/ */
if (ca->state == ORBITER) if (ca->state == ORBITER) {
ca->next_srrtt_check_ts = now + 5 * ca->curr_rtt; roccet_set_next_srrtt_check_ts(ca, now);
ca->interval_snd_seq_start = tp->snd_nxt;
ca->interval_una_seq_start = tp->snd_una;
/* Add the time we spent in the min RTT probing phase to the
* epoch_start, so the cubic growth won't suddenly jump
* after probing. Here we use the next_min_rtt_probe field
* which we (ab)used to store the time when we entered min RTT
* probing (in jiffies).
*/
if ((prev_state == RTT_PROBE ||
prev_state == RTT_PROBE_REFILL)) {
if (ca->epoch_start != 0) {
ca->epoch_start += now_jiffies -
ca->next_min_rtt_probe;
/* Clamp so that epoch_start doesn't go
* in the future.
*/
if (after(ca->epoch_start, now_jiffies))
ca->epoch_start = now_jiffies;
}
roccet_set_next_min_rtt_probe_ts(ca);
}
}
/* In refill we can transition to DRAIN. In this case, overwrite the
* jiffies ts
*/
if (ca->state == DRAIN && prev_state == RTT_PROBE_REFILL)
roccet_set_next_min_rtt_probe_ts(ca);
/* Whenever we leave the min RTT probing states (and not just /* Whenever we leave the min RTT probing states (and not just
* transition between them), we want to reset the probing timers. * transition between them), we want to reset the probing timers.
@@ -952,6 +1082,7 @@ static void roccet_handle_state_transitions(struct roccettcp *ca,
(ca->state != RTT_PROBE && ca->state != RTT_PROBE_REFILL)) { (ca->state != RTT_PROBE && ca->state != RTT_PROBE_REFILL)) {
ca->probe_min_rtt_until = 0; ca->probe_min_rtt_until = 0;
ca->refill_until = 0; ca->refill_until = 0;
ca->rtt_probe_timers_set = false;
} }
} }
@@ -968,12 +1099,19 @@ static void roccet_state(struct sock *sk, u8 new_state)
if (new_state == TCP_CA_Loss) { if (new_state == TCP_CA_Loss) {
roccet_reset(sk, ca); roccet_reset(sk, ca);
tcp_snd_cwnd_set(tp, TCP_INIT_CWND); tcp_snd_cwnd_set(tp, TCP_INIT_CWND);
WRITE_ONCE(tp->snd_ssthresh, max(tp->snd_ssthresh,
TCP_INIT_CWND));
} else if (tcp_in_cwnd_reduction(sk)) {
/* In this case we don't want a further window reduction */
} else if (new_state == TCP_CA_CWR) { } else if (new_state == TCP_CA_CWR) {
/* Handle CWR as ROCCET congestion event, /* Handle CWR as ROCCET congestion event,
* however afterwards always set Wmax to the current cwnd. * however afterwards always set Wmax to the current cwnd
* except during min RTT probing.
*/ */
cwnd = tcp_snd_cwnd(tp); cwnd = tcp_snd_cwnd(tp);
roccet_congestion_event(sk, now); roccet_congestion_event(sk, now);
if (ca->state != RTT_PROBE_ENTER && ca->state != RTT_PROBE &&
ca->state != RTT_PROBE_REFILL)
ca->last_max_cwnd = cwnd; ca->last_max_cwnd = cwnd;
} else if (new_state == TCP_CA_Recovery) { } else if (new_state == TCP_CA_Recovery) {
/* Directly reduce cwnd and rely on pacing */ /* Directly reduce cwnd and rely on pacing */
@@ -981,7 +1119,7 @@ static void roccet_state(struct sock *sk, u8 new_state)
tcp_snd_cwnd_set(tp, cwnd); tcp_snd_cwnd_set(tp, cwnd);
} }
roccet_handle_state_transitions(ca, prev_state, now); roccet_handle_state_transitions(sk, prev_state, now);
} }
/* Update RTT samples and min RTT. /* Update RTT samples and min RTT.
@@ -1017,12 +1155,13 @@ static void roccet_acked(struct sock *sk, const struct ack_sample *sample)
/* Custom Pacing Rate for ROCCET TCP. /* Custom Pacing Rate for ROCCET TCP.
* The code here is similar to the pacing rate adjustments in tcp_input.c * 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 * tcp_cong_control(). In (modified) slow start we want a pacing of 200% and
* in ORBITER (congestion avoidance) we adjust the pacing to 100% and do not * otherwise (congestion avoidance) we adjust the pacing to 100% and do not
* use the sysctl_tcp_pacing_ca_ratio. * use the sysctl_tcp_pacing_ca_ratio.
*/ */
static void roccet_update_pacing_rate(struct sock *sk) static void roccet_update_pacing_rate(struct sock *sk)
{ {
struct roccettcp *ca = inet_csk_ca(sk);
struct tcp_sock *tp = tcp_sk(sk); struct tcp_sock *tp = tcp_sk(sk);
u64 rate; u64 rate;
@@ -1037,14 +1176,17 @@ static void roccet_update_pacing_rate(struct sock *sk)
* If snd_cwnd >= (tp->snd_ssthresh / 2), we are approaching * If snd_cwnd >= (tp->snd_ssthresh / 2), we are approaching
* end of slow start and should slow down. * end of slow start and should slow down.
*/ */
if (tcp_snd_cwnd(tp) < tp->snd_ssthresh / 2) if (ca->state == RTT_PROBE || tcp_snd_cwnd(tp) >= tp->snd_ssthresh / 2)
rate *= READ_ONCE
(sock_net(sk)->ipv4.sysctl_tcp_pacing_ss_ratio);
else
/* Pacing rate of 100% /* Pacing rate of 100%
* (instead of ipv4.sysctl_tcp_pacing_ca_ratio) * (instead of ipv4.sysctl_tcp_pacing_ca_ratio)
*/ */
rate *= 100; rate *= 100;
else
/* Here we are well inside of slow start
* (tcp_snd_cwnd(tp) < tp->snd_ssthresh / 2)
*/
rate *= READ_ONCE
(sock_net(sk)->ipv4.sysctl_tcp_pacing_ss_ratio);
rate *= max(tcp_snd_cwnd(tp), tp->packets_out); rate *= max(tcp_snd_cwnd(tp), tp->packets_out);
@@ -1064,9 +1206,15 @@ static void roccet_drain_update(struct sock *sk, u32 now)
struct tcp_sock *tp = tcp_sk(sk); struct tcp_sock *tp = tcp_sk(sk);
struct roccettcp *ca = inet_csk_ca(sk); struct roccettcp *ca = inet_csk_ca(sk);
if (ca->roccet_last_event_time_us && /* If we are in DRAIN, ca->roccet_last_event_time_us has to be set.
time_after32(now, ca->roccet_last_event_time_us + * If more than 100 ms have passed after a roccet event, exit DRAIN.
100 * USEC_PER_MSEC)) { * The between check is here to handle idle-times and timer-wraps.
* It is ok if we "accidentally" exit DRAIN in such an occasion.
*/
if (!time_between32(now, ca->roccet_last_event_time_us
- 100 * USEC_PER_MSEC,
ca->roccet_last_event_time_us
+ 100 * USEC_PER_MSEC)) {
if (tcp_in_slow_start(tp)) if (tcp_in_slow_start(tp))
ca->state = LAUNCH; ca->state = LAUNCH;
else else
@@ -1114,14 +1262,26 @@ static void roccet_control(struct sock *sk, u32 ack, int flag,
roccet_rtt_probe_refill(ca, now); roccet_rtt_probe_refill(ca, now);
break; break;
default: default:
pr_err_once("TCP ROCCET: Invalid state %d", ca->state); pr_err_once("TCP ROCCET: Invalid state %d\n", ca->state);
} }
roccet_handle_state_transitions(ca, prev_state, now); roccet_handle_state_transitions(sk, prev_state, now);
roccet_update_pacing_rate(sk); roccet_update_pacing_rate(sk);
if (tcp_is_cwnd_limited(sk)) /* Abuse ca->roccet_last_event_time_us as the start time for this
* connection (only after init, while initial_limit_reached is not
* set). When the connection starts sending enough data or after 500 ms
* we know that we can use tcp_is_cwnd_limited() to check if the
* connection is actually limited by the cwnd and is not just
* starting up.
*/
if (!ca->initial_limit_reached &&
(tcp_is_cwnd_limited(sk) ||
!time_between32(now, ca->roccet_last_event_time_us
- 500 * USEC_PER_MSEC,
ca->roccet_last_event_time_us
+ 500 * USEC_PER_MSEC)))
ca->initial_limit_reached = true; ca->initial_limit_reached = true;
} }
+12 -7
View File
@@ -54,23 +54,28 @@ struct roccettcp {
u32 interval_snd_seq_start; u32 interval_snd_seq_start;
u32 interval_una_seq_start; u32 interval_una_seq_start;
u32 ack_rate_last_rate_time; /* Timestamp of the last ACK-rate */ u32 ack_carry_over; /* Used to carry over leftover acks from
* LAUNCH to ORBITER
*/
u32 last_ack_rate_time; /* Timestamp of the last ACK-rate */
u16 ack_rate_last_rate; /* Last ACK-rate */ u16 ack_rate_last_rate; /* Last ACK-rate */
u16 ack_rate_curr_rate; /* Current ACK-rate */ u16 curr_ack_rate; /* Current ACK-rate */
u16 ack_rate_cnt; /* Used for counting acks */ u16 ack_rate_cnt; /* Used for counting acks */
enum roccet_state state : 3; /* Current operating state of roccet */ 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 bool initial_limit_reached: 1; /* Set to true after the connection
* initially gets cwnd-limited * initially gets cwnd-limited
*/ */
bool is_in_initial_launch: 1; /* true if the connection is in bool is_in_initial_launch: 1; /* true if the connection is in
* the initial launch phase. * the initial launch phase.
*/ */
u32 ack_carry_over; /* Used to carry over leftover acks from bool last_event_time_set: 1; /* true if the last event time
* LAUNCH to ORBITER * has been set/initialized.
*/
bool rtt_probe_timers_set: 1; /* true if refill_until and
* probe_min_rtt_until are currently
* set.
*/ */
}; };