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

Changes since last commit:
	* Don't discard all rtt samples after fast recovery in min RTT probing
	* Enter LAUNCH after sufficient idle period
	* Fix outdated comments
	* Apply snd_cwnd_clamp everywhere
	* Remove possibility for double ROCCET CE in ORBITER
	* Fix incorrect implicit s32->u64 cast for `sent_more_than_acked`
	* Fix some incorrect parameter resets on roccet_reset
This commit is contained in:
Lukas Prause
2026-10-08 15:30:30 +02:00
parent 956f195ddc
commit 4bc53f25e6
+100 -70
View File
@@ -100,7 +100,7 @@ static const struct kernel_param_ops beta_param_ops = {
.get = param_get_int, .get = param_get_int,
}; };
module_param_cb(beta, &beta_param_ops, &beta, 0644); module_param_cb(beta, &beta_param_ops, &beta, 0644);
MODULE_PARM_DESC(beta, "beta for multiplicative increase"); MODULE_PARM_DESC(beta, "beta factor for multiplicative decrease");
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");
@@ -191,8 +191,14 @@ static void param_precompute(void)
static void roccet_reset(struct sock *sk, struct roccettcp *ca) static void roccet_reset(struct sock *sk, struct roccettcp *ca)
{ {
/* Save previous state for parameters that should not be reset */
bool prev_initial_limit_reached = ca->initial_limit_reached;
bool prev_is_in_initial_launch = ca->is_in_initial_launch;
/* Reset all the roccet parameters and later restore the ones that
* shouldn't be zero.
*/
memset(ca, 0, sizeof(struct roccettcp)); memset(ca, 0, sizeof(struct roccettcp));
ca->next_srrtt_check_ts = 0;
/* Initialize all RTT values to U32_MAX, so that any lower samples /* Initialize all RTT values to U32_MAX, so that any lower samples
* will be accepted. * will be accepted.
@@ -206,26 +212,21 @@ static void roccet_reset(struct sock *sk, struct roccettcp *ca)
* after init). * after init).
*/ */
ca->roccet_last_event_time_us = jiffies_to_usecs(tcp_jiffies32); ca->roccet_last_event_time_us = jiffies_to_usecs(tcp_jiffies32);
ca->last_event_time_set = false;
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->last_ack_rate_time = jiffies_to_usecs(tcp_jiffies32); ca->last_ack_rate_time = jiffies_to_usecs(tcp_jiffies32);
ca->curr_ack_rate = 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;
ca->interval_una_seq_start = tcp_sk(sk)->snd_una; ca->interval_una_seq_start = tcp_sk(sk)->snd_una;
/* Start state is LAUNCH */ /* Start state is LAUNCH */
ca->state = LAUNCH; ca->state = LAUNCH;
ca->probe_min_rtt_until = 0;
ca->refill_until = 0;
ca->rtt_probe_timers_set = false;
ca->is_in_initial_launch = false; /* Set saved state for certain parameters */
ca->initial_limit_reached = prev_initial_limit_reached;
ca->is_in_initial_launch = prev_is_in_initial_launch;
} }
static void roccet_init(struct sock *sk) static void roccet_init(struct sock *sk)
@@ -378,9 +379,9 @@ static void update_srrtt(struct roccettcp *ca)
ca->curr_min_rtt); ca->curr_min_rtt);
/* (1 - alpha) * srRTT + alpha * rRTT */ /* (1 - alpha) * srRTT + alpha * rRTT */
ca->curr_srrtt = ((100 - ROCCET_ALPHA_TIMES_100) * (u64)ca->curr_srrtt + ca->curr_srrtt = div_u64((100 - ROCCET_ALPHA_TIMES_100)
ROCCET_ALPHA_TIMES_100 * rrtt) / * (u64)ca->curr_srrtt + ROCCET_ALPHA_TIMES_100
100; * rrtt, 100);
} }
/* Handle ROCCET loss/ECN during min RTT probing. /* Handle ROCCET loss/ECN during min RTT probing.
@@ -510,7 +511,7 @@ static void roccet_enter_min_rtt_probe(struct sock *sk, u32 now)
ca->cwnd_before_min_rtt_probe = tcp_snd_cwnd(tp); ca->cwnd_before_min_rtt_probe = tcp_snd_cwnd(tp);
/* Reduce 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, min(probe_cwnd, tp->snd_cwnd_clamp));
/* Reset current min RTT to allow probing for /* Reset current min RTT to allow probing for
* a new lower and higher minimum RTT. * a new lower and higher minimum RTT.
@@ -542,7 +543,7 @@ static void roccet_min_rtt_probe(struct sock *sk, u32 now)
/* Here we are in RTT_PROBE, so the probing time should be set. /* Here we are in RTT_PROBE, so the probing time should be set.
* If not just continue and enter refill phase. * If not just continue and enter refill phase.
* *
* probe_min_rtt_until wrap-arounds are handled by the time_before32 * probe_min_rtt_until wrap-arounds are handled by the time_between32
* check. * check.
*/ */
if (!ca->rtt_probe_timers_set) if (!ca->rtt_probe_timers_set)
@@ -571,7 +572,8 @@ static void roccet_min_rtt_probe(struct sock *sk, u32 now)
if (ca->cwnd_before_min_rtt_probe == 0) if (ca->cwnd_before_min_rtt_probe == 0)
pr_warn_once("TCP ROCCET: cwnd_before_min_rtt_probe is 0 during RTT_PROBE.\n"); 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, min(ca->cwnd_before_min_rtt_probe,
tp->snd_cwnd_clamp));
ca->cwnd_before_min_rtt_probe = 0; ca->cwnd_before_min_rtt_probe = 0;
@@ -590,24 +592,6 @@ static void roccet_rtt_probe_refill(struct roccettcp *ca, u32 now)
} }
} }
static void roccet_cwnd_event_tx_start(struct sock *sk)
{
struct roccettcp *ca = inet_csk_ca(sk);
u32 now = tcp_jiffies32;
s32 delta;
delta = now - tcp_sk(sk)->lsndtime;
/* We were application limited (idle) for a while.
* Shift epoch_start to keep cwnd growth to cubic curve.
*/
if (ca->epoch_start && delta > 0) {
ca->epoch_start += delta;
if (after(ca->epoch_start, now))
ca->epoch_start = now;
}
}
/* 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
* Newton-Raphson iteration. * Newton-Raphson iteration.
* Avg err ~= 0.195% * Avg err ~= 0.195%
@@ -786,6 +770,8 @@ static void roccet_launch_update(struct sock *sk, u32 acked)
get_ack_rate_diff(ca) <= ack_rate_diff_ss) || get_ack_rate_diff(ca) <= ack_rate_diff_ss) ||
!tcp_in_slow_start(tp) || !tcp_in_slow_start(tp) ||
(!tcp_is_cwnd_limited(sk) && ca->initial_limit_reached)) { (!tcp_is_cwnd_limited(sk) && ca->initial_limit_reached)) {
u32 new_cwnd;
ca->epoch_start = 0; ca->epoch_start = 0;
/* Handle initial LAUNCH. Most bufferbloat occurs here */ /* Handle initial LAUNCH. Most bufferbloat occurs here */
@@ -793,13 +779,12 @@ static void roccet_launch_update(struct sock *sk, u32 acked)
/* Halving the cwnd will undo the previous step of slow /* Halving the cwnd will undo the previous step of slow
* start. Which is fine since the pipe is already full. * start. Which is fine since the pipe is already full.
*/ */
tcp_snd_cwnd_set(tp, max(tcp_snd_cwnd(tp) / 2, new_cwnd = tcp_snd_cwnd(tp) / 2;
TCP_INIT_CWND));
} else { } else {
tcp_snd_cwnd_set(tp, max(tcp_snd_cwnd(tp) - new_cwnd = tcp_snd_cwnd(tp) - (tcp_snd_cwnd(tp) / 3);
(tcp_snd_cwnd(tp) / 3),
TCP_INIT_CWND));
} }
tcp_snd_cwnd_set(tp, min(max(new_cwnd, TCP_INIT_CWND),
tp->snd_cwnd_clamp));
WRITE_ONCE(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->last_event_time_set = true;
@@ -886,10 +871,13 @@ static void roccet_orbiter_update(struct sock *sk, u32 acked)
* Here we use a guard space of 1% of the current cwnd. * Here we use a guard space of 1% of the current cwnd.
* 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.
*
* (s64)(s32)(sent - received) => Preserves negative wrap-behavior
* with explicit s64 conversion afterwards.
*/ */
sent_more_than_acked = (s32)(sent - received) > sent_more_than_acked = (s64)(s32)(sent - received) >
div_u64((u64)tcp_snd_cwnd(tp) * (s64)div_u64((u64)tcp_snd_cwnd(tp) *
(u64)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_between32(now, ca->next_srrtt_check_ts if (!time_between32(now, ca->next_srrtt_check_ts
@@ -905,6 +893,10 @@ static void roccet_orbiter_update(struct sock *sk, u32 acked)
ca->interval_una_seq_start = tp->snd_una; ca->interval_una_seq_start = tp->snd_una;
} }
/* If we are in recovery, skip growth and further ROCCET-CE's. */
if (tcp_in_cwnd_reduction(sk))
return;
/* Respects the jitter of the connection and add it on top of /* Respects the jitter of the connection and add it on top of
* the upper bound for the srRTT. * the upper bound for the srRTT.
*/ */
@@ -927,11 +919,8 @@ 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, /* In case there are any ACKs left over from LAUNCH,
@@ -1014,7 +1003,7 @@ static u32 roccet_handle_recovery(struct sock *sk)
* set a state when entering certain states. * set a state when entering certain states.
* *
* 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(), roccet_control(), ...).
*/ */
static void roccet_handle_state_transitions(struct sock *sk, static void roccet_handle_state_transitions(struct sock *sk,
enum roccet_state prev_state, enum roccet_state prev_state,
@@ -1028,9 +1017,8 @@ static void roccet_handle_state_transitions(struct sock *sk,
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. Also on any state transition from LAUNCH, we can
* Also on any state transition from LAUNCH, we can say that the * say that the tcp_is_cwnd_limited() is now valid.
* 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;
@@ -1086,6 +1074,36 @@ static void roccet_handle_state_transitions(struct sock *sk,
} }
} }
static void roccet_cwnd_event_tx_start(struct sock *sk)
{
struct roccettcp *ca = inet_csk_ca(sk);
struct tcp_sock *tp = tcp_sk(sk);
u32 now = tcp_jiffies32;
enum roccet_state prev_state = ca->state;
s32 delta;
delta = now - tcp_sk(sk)->lsndtime;
/* We were application limited (idle) for a while.
* Shift epoch_start to keep cwnd growth to cubic curve.
*/
if (ca->epoch_start && delta > 0) {
ca->epoch_start += delta;
if (after(ca->epoch_start, now))
ca->epoch_start = now;
}
/* After idle periods, go into LAUNCH with slow start */
if (READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_slow_start_after_idle) &&
delta >= inet_csk(sk)->icsk_rto) {
tp->snd_ssthresh = tcp_snd_cwnd(tp);
tcp_snd_cwnd_set(tp, min(TCP_INIT_CWND, tp->snd_cwnd_clamp));
ca->state = LAUNCH;
}
roccet_handle_state_transitions(sk, prev_state, jiffies_to_usecs(now));
}
/* Handle different loss-states and perform adequate cwnd reductions. /* Handle different loss-states and perform adequate cwnd reductions.
*/ */
static void roccet_state(struct sock *sk, u8 new_state) static void roccet_state(struct sock *sk, u8 new_state)
@@ -1098,9 +1116,9 @@ 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, min(TCP_INIT_CWND, tp->snd_cwnd_clamp));
WRITE_ONCE(tp->snd_ssthresh, max(tp->snd_ssthresh, WRITE_ONCE(tp->snd_ssthresh, max(tp->snd_ssthresh,
TCP_INIT_CWND)); tcp_snd_cwnd(tp)));
} else if (tcp_in_cwnd_reduction(sk)) { } else if (tcp_in_cwnd_reduction(sk)) {
/* In this case we don't want a further window reduction */ /* 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) {
@@ -1116,7 +1134,7 @@ static void roccet_state(struct sock *sk, u8 new_state)
} 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 */
cwnd = roccet_handle_recovery(sk); cwnd = roccet_handle_recovery(sk);
tcp_snd_cwnd_set(tp, cwnd); tcp_snd_cwnd_set(tp, min(cwnd, tp->snd_cwnd_clamp));
} }
roccet_handle_state_transitions(sk, prev_state, now); roccet_handle_state_transitions(sk, prev_state, now);
@@ -1127,14 +1145,30 @@ static void roccet_state(struct sock *sk, u8 new_state)
static void roccet_acked(struct sock *sk, const struct ack_sample *sample) static void roccet_acked(struct sock *sk, const struct ack_sample *sample)
{ {
struct roccettcp *ca = inet_csk_ca(sk); struct roccettcp *ca = inet_csk_ca(sk);
bool after_fast_recovery;
u32 delay; u32 delay;
/* Some calls are for duplicates without timestamps */ /* Some calls are for duplicates without timestamps */
if (sample->rtt_us < 0) if (sample->rtt_us < 0)
return; return;
/* Discard delay samples right after fast recovery */ /* Check if we are right after fast recovery. */
if (ca->epoch_start && (s32)(tcp_jiffies32 - ca->epoch_start) < HZ) after_fast_recovery = ca->epoch_start &&
(s32)(tcp_jiffies32 - ca->epoch_start) < HZ;
/* If we are not after fast recovery or we are in RTT_PROBE however,
* we want to record every new min-sample.
*/
if (!after_fast_recovery || ca->state == RTT_PROBE) {
/* Get valid sample for roccet */
if (sample->rtt_us > 0) {
ca->last_rtt = ca->curr_rtt;
ca->curr_rtt = sample->rtt_us;
}
}
/* Otherwise discard delay samples right after fast recovery. */
if (after_fast_recovery)
return; return;
delay = sample->rtt_us; delay = sample->rtt_us;
@@ -1145,19 +1179,14 @@ static void roccet_acked(struct sock *sk, const struct ack_sample *sample)
/* first call or link delay decreases */ /* first call or link delay decreases */
if (ca->delay_min == 0 || (s32)(delay - ca->delay_min) < 0) if (ca->delay_min == 0 || (s32)(delay - ca->delay_min) < 0)
ca->delay_min = delay; ca->delay_min = delay;
/* Get valid sample for roccet */
if (sample->rtt_us > 0) {
ca->last_rtt = ca->curr_rtt;
ca->curr_rtt = sample->rtt_us;
}
} }
/* 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's
* tcp_cong_control(). In (modified) slow start we want a pacing of 200% and * tcp_update_pacing_rate(). In (modified) slow start we want a pacing following the
* otherwise (congestion avoidance) we adjust the pacing to 100% and do not * sysctl_tcp_pacing_ss_ratio (usually 200%) parameter and otherwise
* use the sysctl_tcp_pacing_ca_ratio. * (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) static void roccet_update_pacing_rate(struct sock *sk)
{ {
@@ -1166,11 +1195,12 @@ static void roccet_update_pacing_rate(struct sock *sk)
u64 rate; u64 rate;
/* set sk_pacing_rate to 200 % of current rate (mss * cwnd / srtt) */ /* set sk_pacing_rate to 200 % of current rate (mss * cwnd / srtt) */
rate = (u64)tp->mss_cache * ((USEC_PER_SEC / 100) << 3); rate = (u64)tp->mss_cache * ((div64_u64(USEC_PER_SEC, 100) << 3));
/* current rate is (cwnd * mss) / srtt /* current rate is (cwnd * mss) / srtt
* In slow-start [1], set sk_pacing_rate to 200% the current rate. * In slow-start [1], set sk_pacing_rate to sysctl_tcp_pacing_ss_ratio
* Otherwise, set it to 100% the current rate. * (usually 200%) the current rate. Otherwise, set it to 100% the
* current rate.
* *
* [1]: Normal Slow Start cond is (tp->snd_cwnd < tp->snd_ssthresh) * [1]: Normal Slow Start cond is (tp->snd_cwnd < tp->snd_ssthresh)
* If snd_cwnd >= (tp->snd_ssthresh / 2), we are approaching * If snd_cwnd >= (tp->snd_ssthresh / 2), we are approaching
@@ -1178,8 +1208,8 @@ static void roccet_update_pacing_rate(struct sock *sk)
*/ */
if (ca->state == RTT_PROBE || tcp_snd_cwnd(tp) >= tp->snd_ssthresh / 2) if (ca->state == RTT_PROBE || tcp_snd_cwnd(tp) >= tp->snd_ssthresh / 2)
/* 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 else
/* Here we are well inside of slow start /* Here we are well inside of slow start