Update the repository with the changes made since the last LKML review.
This commit is contained in:
+265
-105
@@ -67,6 +67,13 @@
|
||||
/* min RTT probe period in ms */
|
||||
#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 */
|
||||
static uint sr_rtt_upper_bound __read_mostly = 100;
|
||||
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 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_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_PARM_DESC(initial_ssthresh, "initial value of slow start threshold");
|
||||
module_param(bic_scale, int, 0444);
|
||||
@@ -100,6 +112,26 @@ MODULE_PARM_DESC(bic_scale,
|
||||
module_param(tcp_friendliness, int, 0644);
|
||||
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
|
||||
* to avoid invalid scale-related calculations. This validates the specified
|
||||
* parameters or rejects them.
|
||||
@@ -169,14 +201,19 @@ static void roccet_reset(struct sock *sk, struct roccettcp *ca)
|
||||
ca->curr_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;
|
||||
|
||||
/* Initialize to current time to avoid an
|
||||
* overflow in the ack rate calculation
|
||||
*/
|
||||
ca->ack_rate_last_rate_time = jiffies_to_usecs(tcp_jiffies32);
|
||||
ca->ack_rate_curr_rate = 0;
|
||||
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;
|
||||
@@ -186,8 +223,8 @@ static void roccet_reset(struct sock *sk, struct roccettcp *ca)
|
||||
ca->state = LAUNCH;
|
||||
ca->probe_min_rtt_until = 0;
|
||||
ca->refill_until = 0;
|
||||
ca->rtt_probe_timers_set = false;
|
||||
|
||||
ca->initial_limit_reached = false;
|
||||
ca->is_in_initial_launch = false;
|
||||
}
|
||||
|
||||
@@ -197,7 +234,8 @@ static void roccet_init(struct sock *sk)
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
/* 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) {
|
||||
ca->curr_min_rtt = max(ca->curr_rtt, 1);
|
||||
/* Probe for the min RTT in ROCCET_NEXT_MIN_RTT_PROBE ms
|
||||
* if no other update occurs.
|
||||
*/
|
||||
ca->next_min_rtt_probe =
|
||||
jiffies_to_usecs(tcp_jiffies32) +
|
||||
ROCCET_NEXT_MIN_RTT_PROBE * USEC_PER_MSEC;
|
||||
roccet_set_next_min_rtt_probe_ts(ca);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,9 +286,9 @@ static void update_min_rtt(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 (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.
|
||||
@@ -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 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.
|
||||
* 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)
|
||||
*/
|
||||
if (time_delta < -idle_threshold) {
|
||||
/* Reset ack counting as if a new
|
||||
* connection was created
|
||||
if (interval_elapsed) {
|
||||
/* Check if the connection was idle for *idle_threshold*
|
||||
* seconds (e.g. no ACK for X seconds). If so, reset the 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_time =
|
||||
jiffies_to_usecs(tcp_jiffies32);
|
||||
ca->ack_rate_curr_rate = 0;
|
||||
ca->ack_rate_cnt = 0;
|
||||
ca->curr_ack_rate = 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 = min_t(u32, acked, U16_MAX);
|
||||
ca->ack_rate_last_rate = ca->curr_ack_rate;
|
||||
ca->curr_ack_rate = ca->ack_rate_cnt;
|
||||
}
|
||||
|
||||
ca->was_idle = false;
|
||||
/* Start ack-counting with the already present ACKs */
|
||||
ca->ack_rate_cnt = min_t(u32, acked, U16_MAX);
|
||||
ca->last_ack_rate_time = now;
|
||||
} 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,
|
||||
U16_MAX);
|
||||
}
|
||||
@@ -295,7 +358,7 @@ static void update_srrtt(struct roccettcp *ca)
|
||||
* anyways in order to avoid division by zero errors.
|
||||
*/
|
||||
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 */
|
||||
}
|
||||
|
||||
@@ -338,7 +401,7 @@ static void roccet_min_rtt_probe_ce(struct roccettcp *ca, u32 cwnd)
|
||||
*/
|
||||
|
||||
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
|
||||
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->roccet_last_event_time_us = now;
|
||||
ca->last_event_time_set = true;
|
||||
|
||||
if (ca->state == RTT_PROBE) {
|
||||
/* 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)
|
||||
/ 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) {
|
||||
/* 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,
|
||||
@@ -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.
|
||||
* Continue anyway so we can recover.
|
||||
*/
|
||||
if (ca->probe_min_rtt_until > 0)
|
||||
pr_warn_once("ROCCET: Probing time should not be set");
|
||||
if (ca->rtt_probe_timers_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
|
||||
* happen but still safeguard it.
|
||||
*/
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Start min RTT probing */
|
||||
/* Probe 1*RTT or at least 200ms */
|
||||
interval = max(200 * USEC_PER_MSEC, ca->curr_rtt);
|
||||
/* Use field to store when we entered min RTT probing. Here we use
|
||||
* jiffies to stay consistent with the epoch_start format.
|
||||
*/
|
||||
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
|
||||
* 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->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);
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
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
|
||||
* 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
|
||||
* if-conditions.
|
||||
*/
|
||||
|
||||
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
|
||||
* 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))
|
||||
if (!ca->rtt_probe_timers_set)
|
||||
pr_warn_once("TCP ROCCET: Probing time should be set\n");
|
||||
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. */
|
||||
return;
|
||||
|
||||
/* 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.
|
||||
else if (time_after32(now, ca->refill_until))
|
||||
/* If the probe_min_rtt_until interval has passed we likely
|
||||
* just entered the refill interval, so go into
|
||||
* RTT_PROBE_REFILL. If we are not in the refill_until
|
||||
* 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("ROCCET: Skipped refill interval.");
|
||||
pr_warn_once("TCP ROCCET: Skipped refill interval.\n");
|
||||
|
||||
/* Reset cwnd to refill the pipe and consequently clear the stored
|
||||
* 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)
|
||||
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
|
||||
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)
|
||||
{
|
||||
/* 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. */
|
||||
ca->state = ORBITER;
|
||||
}
|
||||
@@ -526,8 +606,6 @@ static void roccet_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
|
||||
@@ -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 */
|
||||
/* calculate c * time^3 / rtt,
|
||||
* 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 &&
|
||||
get_ack_rate_diff(ca) <= ack_rate_diff_ss) ||
|
||||
!tcp_in_slow_start(tp) ||
|
||||
(!tcp_is_cwnd_limited(sk) && ca->initial_limit_reached)) {
|
||||
ca->epoch_start = 0;
|
||||
|
||||
@@ -729,8 +800,9 @@ static void roccet_launch_update(struct sock *sk, u32 acked)
|
||||
(tcp_snd_cwnd(tp) / 3),
|
||||
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->last_event_time_set = true;
|
||||
ca->state = ORBITER;
|
||||
return;
|
||||
}
|
||||
@@ -763,9 +835,11 @@ static void roccet_orbiter_update(struct sock *sk, u32 acked)
|
||||
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)) {
|
||||
if (ca->last_event_time_set &&
|
||||
time_between32(now, ca->roccet_last_event_time_us
|
||||
- 100 * USEC_PER_MSEC,
|
||||
ca->roccet_last_event_time_us
|
||||
+ 100 * USEC_PER_MSEC)) {
|
||||
ca->state = DRAIN;
|
||||
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
|
||||
* 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->state = RTT_PROBE_ENTER;
|
||||
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
|
||||
* to delays caused by jitter or scheduling.
|
||||
*/
|
||||
sent_more_than_acked =
|
||||
sent >
|
||||
received + ((tcp_snd_cwnd(tp) * tp->mss_cache) / 100);
|
||||
sent_more_than_acked = (s32)(sent - received) >
|
||||
div_u64((u64)tcp_snd_cwnd(tp) *
|
||||
(u64)tp->mss_cache, 100);
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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 */
|
||||
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.
|
||||
* 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)
|
||||
if (!tcp_is_cwnd_limited(sk) || sent_more_than_acked ||
|
||||
tcp_in_cwnd_reduction(sk))
|
||||
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);
|
||||
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
|
||||
* (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,
|
||||
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)
|
||||
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.
|
||||
*/
|
||||
if (prev_state == LAUNCH)
|
||||
if (prev_state == LAUNCH) {
|
||||
ca->is_in_initial_launch = false;
|
||||
ca->initial_limit_reached = true;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
if (ca->state == ORBITER) {
|
||||
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
|
||||
* 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->probe_min_rtt_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) {
|
||||
roccet_reset(sk, ca);
|
||||
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) {
|
||||
/* 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);
|
||||
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;
|
||||
} else if (new_state == TCP_CA_Recovery) {
|
||||
/* 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);
|
||||
}
|
||||
|
||||
roccet_handle_state_transitions(ca, prev_state, now);
|
||||
roccet_handle_state_transitions(sk, prev_state, now);
|
||||
}
|
||||
|
||||
/* 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.
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
static void roccet_update_pacing_rate(struct sock *sk)
|
||||
{
|
||||
struct roccettcp *ca = inet_csk_ca(sk);
|
||||
struct tcp_sock *tp = tcp_sk(sk);
|
||||
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
|
||||
* end of slow start and should slow down.
|
||||
*/
|
||||
if (tcp_snd_cwnd(tp) < tp->snd_ssthresh / 2)
|
||||
rate *= READ_ONCE
|
||||
(sock_net(sk)->ipv4.sysctl_tcp_pacing_ss_ratio);
|
||||
else
|
||||
if (ca->state == RTT_PROBE || tcp_snd_cwnd(tp) >= tp->snd_ssthresh / 2)
|
||||
/* Pacing rate of 100%
|
||||
* (instead of ipv4.sysctl_tcp_pacing_ca_ratio)
|
||||
*/
|
||||
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);
|
||||
|
||||
@@ -1064,9 +1206,15 @@ 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 we are in DRAIN, ca->roccet_last_event_time_us has to be set.
|
||||
* If more than 100 ms have passed after a roccet event, exit DRAIN.
|
||||
* 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))
|
||||
ca->state = LAUNCH;
|
||||
else
|
||||
@@ -1114,14 +1262,26 @@ static void roccet_control(struct sock *sk, u32 ack, int flag,
|
||||
roccet_rtt_probe_refill(ca, now);
|
||||
break;
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
+12
-7
@@ -54,23 +54,28 @@ struct roccettcp {
|
||||
u32 interval_snd_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_curr_rate; /* Current ACK-rate */
|
||||
u16 curr_ack_rate; /* Current ACK-rate */
|
||||
u16 ack_rate_cnt; /* Used for counting acks */
|
||||
|
||||
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
|
||||
bool last_event_time_set: 1; /* true if the last event time
|
||||
* has been set/initialized.
|
||||
*/
|
||||
bool rtt_probe_timers_set: 1; /* true if refill_until and
|
||||
* probe_min_rtt_until are currently
|
||||
* set.
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user