diff --git a/tcp_roccet.c b/tcp_roccet.c index aafe752..072d0ed 100644 --- a/tcp_roccet.c +++ b/tcp_roccet.c @@ -88,10 +88,17 @@ 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 +107,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. @@ -170,6 +197,7 @@ static void roccet_reset(struct sock *sk, struct roccettcp *ca) ca->last_rtt = U32_MAX; ca->roccet_last_event_time_us = 0; + ca->last_event_time_set = false; ca->ack_rate_last_rate = 0; /* Initialize to current time to avoid an @@ -186,6 +214,7 @@ 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; @@ -357,6 +386,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 @@ -402,8 +432,8 @@ 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("ROCCET: Probing time should not be set when entering RTT Probe"); /* Safeguard for an "infinite" probe. This should logically never * happen but still safeguard it. @@ -427,9 +457,9 @@ static void roccet_enter_min_rtt_probe(struct sock *sk, u32 now) * we set the cwnd to its previous value after probing. */ if (!tcp_is_cwnd_limited(sk)) - probe_cwnd = max(tcp_snd_cwnd(tp) / 3, TCP_INIT_CWND); + probe_cwnd = max(tcp_snd_cwnd(tp) / 3, 1); else - probe_cwnd = max(tcp_snd_cwnd(tp) / 2, TCP_INIT_CWND); + probe_cwnd = max(tcp_snd_cwnd(tp) / 2, 1); ca->probe_min_rtt_until = now + interval; ca->cwnd_before_min_rtt_probe = tcp_snd_cwnd(tp); @@ -446,6 +476,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 +484,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,25 +500,26 @@ 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) + if (!ca->rtt_probe_timers_set) pr_warn_once("ROCCET: Probing time should be set"); else if (time_before32(now, ca->probe_min_rtt_until)) /* No state change if we are in the probing interval. */ return; - - /* If the interval has passed we likely just entered the refill - * interval, so go into RTT_PROBE_REFILL. - * If we are not in the interval, something went wrong and we should - * quickly exit the probing phase. - * - * Wraps-arounds of refill_until are caught by the time_after32 check. - */ - if (time_after32(now, ca->refill_until)) + 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. + */ pr_warn_once("ROCCET: Skipped refill interval."); /* 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."); @@ -618,14 +649,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 @@ -722,15 +745,14 @@ 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)); + tcp_snd_cwnd_set(tp, max(tcp_snd_cwnd(tp) / 2, 1)); } else { tcp_snd_cwnd_set(tp, max(tcp_snd_cwnd(tp) - - (tcp_snd_cwnd(tp) / 3), - TCP_INIT_CWND)); + (tcp_snd_cwnd(tp) / 3), 1)); } 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,7 +785,7 @@ 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 && + if (ca->last_event_time_set && time_before32(now, ca->roccet_last_event_time_us + 100 * USEC_PER_MSEC)) { ca->state = DRAIN; @@ -850,6 +872,14 @@ static void roccet_orbiter_update(struct sock *sk, u32 acked) if (!tcp_is_cwnd_limited(sk) || sent_more_than_acked) 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); } @@ -952,6 +982,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; } } @@ -967,7 +998,7 @@ 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, 1); } else if (new_state == TCP_CA_CWR) { /* Handle CWR as ROCCET congestion event, * however afterwards always set Wmax to the current cwnd. @@ -1064,9 +1095,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 diff --git a/tcp_roccet.h b/tcp_roccet.h index 85fbdda..70c2da4 100644 --- a/tcp_roccet.h +++ b/tcp_roccet.h @@ -54,6 +54,10 @@ struct roccettcp { u32 interval_snd_seq_start; u32 interval_una_seq_start; + u32 ack_carry_over; /* Used to carry over leftover acks from + * LAUNCH to ORBITER + */ + u32 ack_rate_last_rate_time; /* Timestamp of the last ACK-rate */ u16 ack_rate_last_rate; /* Last ACK-rate */ u16 ack_rate_curr_rate; /* Current ACK-rate */ @@ -69,9 +73,13 @@ struct roccettcp { 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. + */ }; #endif /* __TCP_ROCCET_H */