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:
+97
-67
@@ -100,7 +100,7 @@ static const struct kernel_param_ops beta_param_ops = {
|
||||
.get = param_get_int,
|
||||
};
|
||||
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_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)
|
||||
{
|
||||
/* 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));
|
||||
ca->next_srrtt_check_ts = 0;
|
||||
|
||||
/* Initialize all RTT values to U32_MAX, so that any lower samples
|
||||
* will be accepted.
|
||||
@@ -206,26 +212,21 @@ static void roccet_reset(struct sock *sk, struct roccettcp *ca)
|
||||
* 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;
|
||||
|
||||
/* Initialize to current time to avoid an
|
||||
* overflow in the ack rate calculation
|
||||
*/
|
||||
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_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->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)
|
||||
@@ -378,9 +379,9 @@ static void update_srrtt(struct roccettcp *ca)
|
||||
ca->curr_min_rtt);
|
||||
|
||||
/* (1 - alpha) * srRTT + alpha * rRTT */
|
||||
ca->curr_srrtt = ((100 - ROCCET_ALPHA_TIMES_100) * (u64)ca->curr_srrtt +
|
||||
ROCCET_ALPHA_TIMES_100 * rrtt) /
|
||||
100;
|
||||
ca->curr_srrtt = div_u64((100 - ROCCET_ALPHA_TIMES_100)
|
||||
* (u64)ca->curr_srrtt + ROCCET_ALPHA_TIMES_100
|
||||
* rrtt, 100);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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
|
||||
* 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.
|
||||
* 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.
|
||||
*/
|
||||
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)
|
||||
pr_warn_once("TCP ROCCET: cwnd_before_min_rtt_probe is 0 during RTT_PROBE.\n");
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
* Newton-Raphson iteration.
|
||||
* 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) ||
|
||||
!tcp_in_slow_start(tp) ||
|
||||
(!tcp_is_cwnd_limited(sk) && ca->initial_limit_reached)) {
|
||||
u32 new_cwnd;
|
||||
|
||||
ca->epoch_start = 0;
|
||||
|
||||
/* 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
|
||||
* start. Which is fine since the pipe is already full.
|
||||
*/
|
||||
tcp_snd_cwnd_set(tp, max(tcp_snd_cwnd(tp) / 2,
|
||||
TCP_INIT_CWND));
|
||||
new_cwnd = tcp_snd_cwnd(tp) / 2;
|
||||
} else {
|
||||
tcp_snd_cwnd_set(tp, max(tcp_snd_cwnd(tp) -
|
||||
(tcp_snd_cwnd(tp) / 3),
|
||||
TCP_INIT_CWND));
|
||||
new_cwnd = tcp_snd_cwnd(tp) - (tcp_snd_cwnd(tp) / 3);
|
||||
}
|
||||
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));
|
||||
ca->roccet_last_event_time_us = now;
|
||||
ca->last_event_time_set = true;
|
||||
@@ -886,9 +871,12 @@ static void roccet_orbiter_update(struct sock *sk, u32 acked)
|
||||
* 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.
|
||||
*
|
||||
* (s64)(s32)(sent - received) => Preserves negative wrap-behavior
|
||||
* with explicit s64 conversion afterwards.
|
||||
*/
|
||||
sent_more_than_acked = (s32)(sent - received) >
|
||||
div_u64((u64)tcp_snd_cwnd(tp) *
|
||||
sent_more_than_acked = (s64)(s32)(sent - received) >
|
||||
(s64)div_u64((u64)tcp_snd_cwnd(tp) *
|
||||
(u64)tp->mss_cache, 100);
|
||||
|
||||
/* Check if it's time to evaluate the srRTT */
|
||||
@@ -905,6 +893,10 @@ static void roccet_orbiter_update(struct sock *sk, u32 acked)
|
||||
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
|
||||
* 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.
|
||||
* The cwnd cannot therefore be fully utilized because it is
|
||||
* 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 ||
|
||||
tcp_in_cwnd_reduction(sk))
|
||||
if (!tcp_is_cwnd_limited(sk) || sent_more_than_acked)
|
||||
return;
|
||||
|
||||
/* 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.
|
||||
*
|
||||
* 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,
|
||||
enum roccet_state prev_state,
|
||||
@@ -1028,9 +1017,8 @@ static void roccet_handle_state_transitions(struct sock *sk,
|
||||
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).
|
||||
* Also on any state transition from LAUNCH, we can say that the
|
||||
* tcp_is_cwnd_limited() is now valid.
|
||||
* initial_launch. Also on any state transition from LAUNCH, we can
|
||||
* say that the tcp_is_cwnd_limited() is now valid.
|
||||
*/
|
||||
if (prev_state == LAUNCH) {
|
||||
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.
|
||||
*/
|
||||
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) {
|
||||
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,
|
||||
TCP_INIT_CWND));
|
||||
tcp_snd_cwnd(tp)));
|
||||
} 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) {
|
||||
@@ -1116,7 +1134,7 @@ static void roccet_state(struct sock *sk, u8 new_state)
|
||||
} else if (new_state == TCP_CA_Recovery) {
|
||||
/* Directly reduce cwnd and rely on pacing */
|
||||
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);
|
||||
@@ -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)
|
||||
{
|
||||
struct roccettcp *ca = inet_csk_ca(sk);
|
||||
bool after_fast_recovery;
|
||||
u32 delay;
|
||||
|
||||
/* Some calls are for duplicates without timestamps */
|
||||
if (sample->rtt_us < 0)
|
||||
return;
|
||||
|
||||
/* Discard delay samples right after fast recovery */
|
||||
if (ca->epoch_start && (s32)(tcp_jiffies32 - ca->epoch_start) < HZ)
|
||||
/* Check if we are right after fast recovery. */
|
||||
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;
|
||||
|
||||
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 */
|
||||
if (ca->delay_min == 0 || (s32)(delay - ca->delay_min) < 0)
|
||||
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.
|
||||
* The code here is similar to the pacing rate adjustments in tcp_input.c
|
||||
* tcp_cong_control(). In (modified) slow start we want a pacing of 200% and
|
||||
* otherwise (congestion avoidance) we adjust the pacing to 100% and do not
|
||||
* use the sysctl_tcp_pacing_ca_ratio.
|
||||
* The code here is similar to the pacing rate adjustments in tcp_input.c's
|
||||
* tcp_update_pacing_rate(). In (modified) slow start we want a pacing following the
|
||||
* sysctl_tcp_pacing_ss_ratio (usually 200%) parameter and otherwise
|
||||
* (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)
|
||||
{
|
||||
@@ -1166,11 +1195,12 @@ static void roccet_update_pacing_rate(struct sock *sk)
|
||||
u64 rate;
|
||||
|
||||
/* 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
|
||||
* In slow-start [1], set sk_pacing_rate to 200% the current rate.
|
||||
* Otherwise, set it to 100% the current rate.
|
||||
* In slow-start [1], set sk_pacing_rate to sysctl_tcp_pacing_ss_ratio
|
||||
* (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)
|
||||
* If snd_cwnd >= (tp->snd_ssthresh / 2), we are approaching
|
||||
|
||||
Reference in New Issue
Block a user