diff --git a/README.md b/README.md index 3dbae77..63a3d72 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,10 @@ Note: This change is not persistent, the module will not be loaded at boot. sudo modprobe tcp_roccet sudo sysctl net.ipv4.tcp_congestion_control=roccet ``` +### Versions + +- release-2026-06-12-v1 + - Adds minimum RTT probing ## Build from source @@ -86,4 +90,4 @@ To see the trace output use To remove the module again, use `sudo rmmod roccet_kprobe` -_See more info at_ https://docs.kernel.org/trace/kprobes.html and https://www.kernel.org/doc/Documentation/trace/kprobetrace.rst \ No newline at end of file +_See more info at_ https://docs.kernel.org/trace/kprobes.html and https://www.kernel.org/doc/Documentation/trace/kprobetrace.rst diff --git a/tcp_roccet.c b/tcp_roccet.c index 892faf9..878a05e 100644 --- a/tcp_roccet.c +++ b/tcp_roccet.c @@ -14,7 +14,8 @@ * CUBIC's window growth function and adds, based on RTT * and ACK rate, congestion events. * - * A peer-reviewed paper on TCP ROCCET will be presented at the WONS 2026 conference. + * A peer-reviewed paper on TCP ROCCET will be presented + * at the WONS 2026 conference. * A draft of the paper is available here: * https://arxiv.org/abs/2510.25281 * @@ -45,12 +46,12 @@ */ #include "tcp_roccet.h" -#include "linux/printk.h" #include #include #include #include #include +#include #include /* Scale factor beta calculation (max_cwnd = snd_cwnd * beta) */ @@ -63,35 +64,18 @@ */ #define ROCCET_ALPHA_TIMES_100 20 -/* The amount of seconds ROCCET stores a minRTT. - * Enable "calculate_min_rtt" first. - */ -#define ROCCET_RTT_LOOKBACK_S 10 +/* min RTT probe period in seconds */ +#define ROCCET_NEXT_MIN_RTT_PROBE 5000 /* Parameters that are specific to the ROCCET-Algorithm */ static int sr_rtt_upper_bound __read_mostly = 100; static int ack_rate_diff_ss __read_mostly = 10; -static int ack_rate_diff_ca __read_mostly = 200; -static bool calculate_min_rtt __read_mostly; -static bool ignore_loss __read_mostly; -static int roccet_min_rtt_interpolation_factor __read_mostly = 70; module_param(sr_rtt_upper_bound, int, 0644); MODULE_PARM_DESC(sr_rtt_upper_bound, "ROCCET's upper bound for srRTT."); module_param(ack_rate_diff_ss, int, 0644); MODULE_PARM_DESC(ack_rate_diff_ss, "ROCCET's threshold to exit slow start if ACK-rate defer by given amount of segments."); -module_param(ack_rate_diff_ca, int, 0644); -MODULE_PARM_DESC(ack_rate_diff_ca, - "ROCCET's threshold for ack-rate and cum_cwnd, in percantage of the current cwnd."); -module_param(calculate_min_rtt, bool, 0644); -MODULE_PARM_DESC(calculate_min_rtt, - "Calculate min RTT if no lower RTT occurs after 10 sec."); -module_param(ignore_loss, bool, 0644); -MODULE_PARM_DESC(ignore_loss, "Ignore loss as a congestion event."); -module_param(roccet_min_rtt_interpolation_factor, int, 0644); -MODULE_PARM_DESC(roccet_min_rtt_interpolation_factor, - "ROCCET factor for interpolating the current RTT with the last minRTT (minRTT = (factor * currRTT + (100-factor) * minRTT) / 100)"); static int fast_convergence __read_mostly = 1; static int beta __read_mostly = 717; /* = 717/1024 (BICTCP_BETA_SCALE) */ @@ -116,92 +100,121 @@ MODULE_PARM_DESC(bic_scale, module_param(tcp_friendliness, int, 0644); MODULE_PARM_DESC(tcp_friendliness, "turn on/off tcp friendliness"); -static inline void roccettcp_reset(struct roccettcp *ca) +static __always_inline void roccettcp_reset(struct roccettcp *ca) { - memset(ca, 0, offsetof(struct roccettcp, curr_rtt)); - ca->bw_limit.sum_cwnd = 1; - ca->bw_limit.sum_acked = 1; - ca->bw_limit.next_check = 0; - ca->curr_min_rtt_timed.rtt = ~0U; - ca->curr_min_rtt_timed.time = ~0U; + memset(ca, 0, sizeof(struct roccettcp)); + ca->next_srrtt_check = 0; + ca->curr_min_rtt = ~0U; ca->last_rtt = 0; ca->ece_received = false; } -static inline void update_min_rtt(struct sock *sk) +/* Return true if ROCCET is in min RTT probing. + */ +static __always_inline bool is_in_min_rtt_probing(struct roccettcp *ca, u32 now) +{ + if (ca->probe_min_rtt_until == 0) + return false; + return before(now, ca->probe_min_rtt_until); +} + +static __always_inline void update_min_rtt(struct sock *sk) { struct roccettcp *ca = inet_csk_ca(sk); - u32 now = jiffies_to_usecs(tcp_jiffies32); - - if (now - ca->curr_min_rtt_timed.time > - ROCCET_RTT_LOOKBACK_S * USEC_PER_SEC && - calculate_min_rtt) { - u32 new_min_rtt = max(ca->curr_rtt, 1); - u32 old_min_rtt = ca->curr_min_rtt_timed.rtt; - - u32 interpolated_min_rtt = - (new_min_rtt * roccet_min_rtt_interpolation_factor + - old_min_rtt * - (100 - roccet_min_rtt_interpolation_factor)) / - 100; - - ca->curr_min_rtt_timed.rtt = interpolated_min_rtt; - ca->curr_min_rtt_timed.time = now; - } /* Check if new lower min RTT was found. If so, set it directly */ - if (ca->curr_rtt < ca->curr_min_rtt_timed.rtt) { - ca->curr_min_rtt_timed.rtt = max(ca->curr_rtt, 1); - ca->curr_min_rtt_timed.time = now; + 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 seconds + * if no other update occurs. + */ + ca->next_min_rtt_probe = jiffies_to_usecs(tcp_jiffies32) + + ROCCET_NEXT_MIN_RTT_PROBE * USEC_PER_MSEC; } } /* Return difference between last and current ack rate. */ -static inline int get_ack_rate_diff(struct roccettcp *ca) +static __always_inline int get_ack_rate_diff(struct roccettcp *ca) { - return ca->ack_rate.last_rate - ca->ack_rate.curr_rate; + if (ca->ack_rate.curr_rate < ca->ack_rate.last_rate) + return 0; + return ca->ack_rate.curr_rate - ca->ack_rate.last_rate; } /* Update ack rate sampled by 100ms. */ -static inline void update_ack_rate(struct sock *sk) +static __always_inline void update_ack_rate(struct sock *sk, u32 acked, u32 now) { struct roccettcp *ca = inet_csk_ca(sk); - u32 now = jiffies_to_usecs(tcp_jiffies32); u32 interval = USEC_PER_MSEC * 100; - if ((u32)(now - ca->ack_rate.last_rate_time) >= interval) { + if (after(now, ca->ack_rate.last_rate_time + 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 = 0; + ca->ack_rate.cnt = acked; // start counting for the new interval } else { - ca->ack_rate.cnt += 1; + ca->ack_rate.cnt += acked; } } /* Compute srRTT. */ -static inline void update_srrtt(struct sock *sk) +static __always_inline void update_srrtt(struct sock *sk) { struct roccettcp *ca = inet_csk_ca(sk); - if (ca->curr_min_rtt_timed.rtt == 0) - return; + /* Avoid integer overflow in the calculation below. + * This could occur in cases where we have not yet + * received an RTT sample. In these cases, set the + * rtt to a safe value. + */ + if (ca->curr_rtt < ca->curr_min_rtt) { + ca->curr_rtt = max(ca->curr_rtt, 1); + ca->curr_min_rtt = ca->curr_rtt; + } + + /* Avoid division by zero */ + if (ca->curr_min_rtt == 0) { + ca->curr_min_rtt = max(ca->curr_min_rtt, 1); + return; // skip srRTT update + } /* Calculate the new rRTT (Scaled by 100). - * 100 * ((sRTT - sRTT_min) / sRTT_min) + * 100 * ((sRTT - sRTT_min) / sRTT_min). + * + * curr_min_rtt_timed.rtt is always <= than curr_rtt, + * since this is the minimum of the rtt. + * + * 0 is a valid value for rrtt. */ - u32 rrtt = (100 * (ca->curr_rtt - ca->curr_min_rtt_timed.rtt)) / - ca->curr_min_rtt_timed.rtt; + u32 rrtt = (100 * (ca->curr_rtt - ca->curr_min_rtt)) / + ca->curr_min_rtt; // (1 - alpha) * srRTT + alpha * rRTT ca->curr_srrtt = ((100 - ROCCET_ALPHA_TIMES_100) * ca->curr_srrtt + ROCCET_ALPHA_TIMES_100 * rrtt) / 100; } -__bpf_kfunc static void roccettcp_init(struct sock *sk) +/* Do a ROCCET congestion event. + */ +static __always_inline void roccet_congestion_event(struct sock *sk, u32 now) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct roccettcp *ca = inet_csk_ca(sk); + + ca->epoch_start = 0; + ca->roccet_last_event_time_us = now; + ca->cnt = 100 * tcp_snd_cwnd(tp); + ca->last_max_cwnd = tcp_snd_cwnd(tp); + tcp_snd_cwnd_set(tp, min(tp->snd_cwnd_clamp, + max((tcp_snd_cwnd(tp) * beta) / + BICTCP_BETA_SCALE, 2U))); + tp->snd_ssthresh = tcp_snd_cwnd(tp); +} + +static void roccettcp_init(struct sock *sk) { struct roccettcp *ca = inet_csk_ca(sk); @@ -212,14 +225,13 @@ __bpf_kfunc static void roccettcp_init(struct sock *sk) /* Initial roccet parameters */ ca->roccet_last_event_time_us = 0; - ca->curr_min_rtt = ~0U; ca->ack_rate.last_rate = 0; ca->ack_rate.last_rate_time = 0; ca->ack_rate.curr_rate = 0; ca->ack_rate.cnt = 0; } -__bpf_kfunc static void roccettcp_cwnd_event(struct sock *sk, +static void roccettcp_cwnd_event(struct sock *sk, enum tcp_ca_event event) { if (event == CA_EVENT_TX_START) { @@ -289,7 +301,8 @@ static u32 cubic_root(u64 a) /* Compute congestion window to use. */ -static inline void bictcp_update(struct roccettcp *ca, u32 cwnd, u32 acked) +static __always_inline void bictcp_update(struct roccettcp *ca, u32 cwnd, + u32 acked) { u32 delta, bic_target, max_cnt; u64 offs, t; @@ -398,29 +411,45 @@ tcp_friendliness: ca->cnt = max(ca->cnt, 2U); } -__bpf_kfunc static void roccettcp_cong_avoid(struct sock *sk, u32 ack, +static void roccettcp_cong_avoid(struct sock *sk, u32 ack, u32 acked) { struct tcp_sock *tp = tcp_sk(sk); struct roccettcp *ca = inet_csk_ca(sk); u32 now = jiffies_to_usecs(tcp_jiffies32); - u32 bw_limit_detect = 0; + bool evaluate_srrtt = false; u32 roccet_xj; u32 jitter; - if (ca->last_rtt > ca->curr_rtt) + if (after(ca->last_rtt, ca->curr_rtt)) jitter = ca->last_rtt - ca->curr_rtt; else jitter = ca->curr_rtt - ca->last_rtt; /* Update roccet parameters */ - update_ack_rate(sk); + update_ack_rate(sk, acked, now); update_min_rtt(sk); update_srrtt(sk); + /* do not increase the cwnd if in RTT probe */ + if (is_in_min_rtt_probing(ca, now)) + return; + + /* Probe for min RTT if the last min RTT update is + * ROCCET_NEXT_MIN_RTT_PROBE seconds ago. + */ + if (after(now, ca->next_min_rtt_probe)){ + ca->probe_min_rtt_until = now + min(200, 2 * ca->curr_rtt) * USEC_PER_MSEC; + ca->cwnd_before_min_rtt_probe = tcp_snd_cwnd(tp); + tcp_snd_cwnd_set(tp, max(tcp_snd_cwnd(tp) / 2, TCP_INIT_CWND)); + ca->curr_min_rtt = ~0U; + return; + } + /* ROCCET drain. * Do not increase the cwnd for 100ms after a roccet congestion event + * because the buffer is still not drained. */ if (now - ca->roccet_last_event_time_us <= 100 * USEC_PER_MSEC) return; @@ -429,18 +458,23 @@ __bpf_kfunc static void roccettcp_cong_avoid(struct sock *sk, u32 ack, * in networks with large buffers of multiple BDP * Like in cellular networks (5G, ...). * Or exit LAUNCH if cwnd is too large for application layer - * data rate. + * data rate (tcp cwnd validation). */ if ((tcp_in_slow_start(tp) && 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_is_cwnd_limited(sk) && tcp_in_slow_start(tp))) { ca->epoch_start = 0; - /* Handle initial slow start. Here we observe the most problems */ + /* Handle initial slow start. Here occur most bufferbloat */ if (tp->snd_ssthresh == TCP_INFINITE_SSTHRESH) { tcp_sk(sk)->snd_ssthresh = tcp_snd_cwnd(tp) / 2; - tcp_snd_cwnd_set(tp, tcp_snd_cwnd(tp) / 2); + /* since this is the initial slow start, + * the min cwnd won't be 1, so the window + * can't be set to 0 by accident. + */ + tcp_snd_cwnd_set(tp, max(tcp_snd_cwnd(tp) / 2, + TCP_INIT_CWND)); } else { tcp_sk(sk)->snd_ssthresh = tcp_snd_cwnd(tp) - (tcp_snd_cwnd(tp) / 3); @@ -457,57 +491,39 @@ __bpf_kfunc static void roccettcp_cong_avoid(struct sock *sk, u32 ack, return; } - if (ca->bw_limit.next_check == 0) - ca->bw_limit.next_check = now + 5 * ca->curr_rtt; + if (ca->next_srrtt_check == 0) + ca->next_srrtt_check = now + 5 * ca->curr_rtt; - ca->bw_limit.sum_cwnd += tcp_snd_cwnd(tp); - ca->bw_limit.sum_acked += acked; - - if (ca->bw_limit.next_check < now) { - /* We send more data as we got acked in the last 5 RTTs */ - if ((ca->bw_limit.sum_cwnd * 100) / ca->bw_limit.sum_acked >= - ack_rate_diff_ca) - bw_limit_detect = 1; + /* Check if it's time to evaluate the srRTT */ + if (after(now, ca->next_srrtt_check)) { + evaluate_srrtt = true; /* reset struct and set next end of period */ - ca->bw_limit.sum_cwnd = 1; - - /* set to 1 to avoid division by zero */ - ca->bw_limit.sum_acked = 1; - ca->bw_limit.next_check = now + 5 * ca->curr_rtt; + ca->next_srrtt_check = now + 5 * ca->curr_rtt; } - /* Respects the jitter of the connection and add it on top of the upper bound - * for the srRTT + /* Respects the jitter of the connection and add it on top of + * the upper bound for the srRTT. */ - roccet_xj = ((jitter * 100) / ca->curr_min_rtt_timed.rtt) + + roccet_xj = ((jitter * 100) / ca->curr_min_rtt) + sr_rtt_upper_bound; if (roccet_xj < sr_rtt_upper_bound) roccet_xj = sr_rtt_upper_bound; - if (ca->curr_srrtt > roccet_xj && (bw_limit_detect || ca->ece_received)) { - if (ca->ece_received) - ca->ece_received = false; - ca->epoch_start = 0; - ca->roccet_last_event_time_us = now; - ca->cnt = 100 * tcp_snd_cwnd(tp); - - /* Set Wmax if cwnd is larger than the old Wmax */ - if (tcp_snd_cwnd(tp) > ca->last_max_cwnd) - ca->last_max_cwnd = tcp_snd_cwnd(tp); - - tcp_snd_cwnd_set(tp, min(tp->snd_cwnd_clamp, - max((tcp_snd_cwnd(tp) * beta) / BICTCP_BETA_SCALE, 2U))); - tp->snd_ssthresh = tcp_snd_cwnd(tp); + /* The srRTT exceeds the upper bound if bufferbloat happens. + * Here, we want to reduce the cwnd and drain the buffer. + */ + if (ca->curr_srrtt > roccet_xj && evaluate_srrtt) { + roccet_congestion_event(sk, now); return; } /* Terminates this function if cwnd is not fully utilized. - * In mobile networks like 5G, this termination causes the cwnd to be frozen at - * an excessively high value. This is because slow start or HyStart massively - * exceed the available 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. + * In mobile networks like 5G, this termination causes the cwnd to be + * frozen at an excessively high value. This is because slow start or + * HyStart massively exceed the available 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. */ if (!tcp_is_cwnd_limited(sk)) return; @@ -516,15 +532,21 @@ __bpf_kfunc static void roccettcp_cong_avoid(struct sock *sk, u32 ack, tcp_cong_avoid_ai(tp, max(1, ca->cnt), acked); } -__bpf_kfunc static u32 roccettcp_recalc_ssthresh(struct sock *sk) +static u32 roccettcp_recalc_ssthresh(struct sock *sk) { const struct tcp_sock *tp = tcp_sk(sk); struct roccettcp *ca = inet_csk_ca(sk); - if (ignore_loss) + /* Handle ECN as ROCCET congestion event. */ + if (ca->ece_received) { + ca->ece_received = false; + roccet_congestion_event(sk, jiffies_to_usecs(tcp_jiffies32)); return tcp_snd_cwnd(tp); + } - /* Don't exit slow start if loss occurs. */ + /* On loss in slow start enter congestion avoidance + * without a cwnd reduction. + */ if (tcp_in_slow_start(tp)) return tcp_snd_cwnd(tp); @@ -541,7 +563,7 @@ __bpf_kfunc static u32 roccettcp_recalc_ssthresh(struct sock *sk) return max((tcp_snd_cwnd(tp) * beta) / BICTCP_BETA_SCALE, 2U); } -__bpf_kfunc static void roccettcp_state(struct sock *sk, u8 new_state) +static void roccettcp_state(struct sock *sk, u8 new_state) { struct roccettcp *ca = inet_csk_ca(sk); @@ -549,7 +571,7 @@ __bpf_kfunc static void roccettcp_state(struct sock *sk, u8 new_state) roccettcp_reset(ca); } -__bpf_kfunc static void roccettcp_acked(struct sock *sk, +static void roccettcp_acked(struct sock *sk, const struct ack_sample *sample) { struct roccettcp *ca = inet_csk_ca(sk); @@ -568,7 +590,7 @@ __bpf_kfunc static void roccettcp_acked(struct sock *sk, delay = 1; /* first time call or link delay decreases */ - if (ca->delay_min == 0 || ca->delay_min > delay) + if (ca->delay_min == 0 || after(ca->delay_min, delay)) ca->delay_min = delay; /* Get valid sample for roccet */ @@ -578,12 +600,12 @@ __bpf_kfunc static void roccettcp_acked(struct sock *sk, } } -__bpf_kfunc static void roccet_in_ack_event(struct sock *sk, u32 flags) +static void roccet_in_ack_event(struct sock *sk, u32 flags) { struct roccettcp *ca = inet_csk_ca(sk); /* Handle ECE bit. - * Processing of ECE events is done in roccettcp_cong_avoid() + * Processing of ECE events is done in roccettcp_recalc_ssthresh() */ if (flags & CA_ACK_ECE) ca->ece_received = true; @@ -602,26 +624,24 @@ static struct tcp_congestion_ops roccet_tcp __read_mostly = { .name = "roccet", }; -BTF_KFUNCS_START(tcp_roccet_check_kfunc_ids) -BTF_ID_FLAGS(func, roccettcp_init) -BTF_ID_FLAGS(func, roccettcp_recalc_ssthresh) -BTF_ID_FLAGS(func, roccettcp_cong_avoid) -BTF_ID_FLAGS(func, roccettcp_state) -BTF_ID_FLAGS(func, roccettcp_cwnd_event) -BTF_ID_FLAGS(func, roccettcp_acked) -BTF_KFUNCS_END(tcp_roccet_check_kfunc_ids) - -static const struct btf_kfunc_id_set tcp_roccet_kfunc_set = { - .owner = THIS_MODULE, - .set = &tcp_roccet_check_kfunc_ids, -}; - static int __init roccettcp_register(void) { - int ret; - BUILD_BUG_ON(sizeof(struct roccettcp) > ICSK_CA_PRIV_SIZE); + /* + * Validate parameters to avoid division by zero errors. + */ + if (beta <= 0 || beta >= BICTCP_BETA_SCALE) { + pr_err("roccet: beta must be between 0 and %d\n", + BICTCP_BETA_SCALE); + return -EINVAL; + } + + if (bic_scale <= 0) { + pr_err("roccet: bic_scale must be positive\n"); + return -EINVAL; + } + /* Precompute a bunch of the scaling factors that are used per-packet * based on SRTT of 100ms */ @@ -650,10 +670,6 @@ static int __init roccettcp_register(void) /* divide by bic_scale and by constant Srtt (100ms) */ do_div(cube_factor, bic_scale * 10); - ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, - &tcp_roccet_kfunc_set); - if (ret < 0) - return ret; return tcp_register_congestion_control(&roccet_tcp); } @@ -668,4 +684,3 @@ module_exit(roccettcp_unregister); MODULE_AUTHOR("Lukas Prause, Tim Füchsel"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("ROCCET TCP"); -MODULE_VERSION("1.0"); diff --git a/tcp_roccet.h b/tcp_roccet.h index a720186..b9037e9 100644 --- a/tcp_roccet.h +++ b/tcp_roccet.h @@ -8,24 +8,15 @@ #include struct ack_rate { - u16 last_rate; /* Last ACK-rate */ + u32 last_rate; /* Last ACK-rate */ u32 last_rate_time; /* Timestamp of the last ACK-rate */ - u16 curr_rate; /* Current ACK-rate */ - u16 cnt; /* Used for counting acks */ + u32 curr_rate; /* Current ACK-rate */ + u32 cnt; /* Used for counting acks */ }; -struct bandwidth_limit_detect { - u32 sum_cwnd; /* sum of cwnd during time interval */ - u32 sum_acked; /* sum of received acks during time interval */ - u32 next_check; /* end/upper bound of time interval */ -}; - -struct timed_rtt { - u32 time; /* Time of recording */ - u32 rtt; /* Measured RTT */ -}; - -/* Based on the BICTCP struct with additions specific for the ROCCET-Algorithm */ +/* Based on the BICTCP struct with additions specific + * for the ROCCET-Algorithm + */ struct roccettcp { u32 cnt; /* increase cwnd by 1 after ACKs */ u32 last_max_cwnd; /* last maximum snd_cwnd */ @@ -41,15 +32,17 @@ struct roccettcp { u32 tcp_cwnd; /* estimated tcp cwnd */ u32 curr_rtt; /* the minimum rtt of current round */ - u32 roccet_last_event_time_us; /* The last time ROCCET was triggered */ + u32 roccet_last_event_time_us; /* The last time ROCCET was + * triggered + */ bool ece_received; /* Set to true if an ECE bit was received */ - u32 curr_min_rtt; /* The current observed minRTT */ - struct timed_rtt curr_min_rtt_timed; /* The current observed minRTT with - * the timestamp when it was observed - */ - u32 curr_srrtt; /* The srRTT calculated based on the latest ACK */ + u32 curr_min_rtt; /* The current observed minRTT */ + u32 next_min_rtt_probe; /* Next time to probe the minRTT */ + u32 probe_min_rtt_until; /* End of minRTT probing period */ + u32 cwnd_before_min_rtt_probe; /* cwnd before min RTT probeing */ + u32 curr_srrtt; /* srRTT calculated based on the latest ACK */ + u32 next_srrtt_check; /* Next check for srRTT */ struct ack_rate ack_rate; /* The last and the current ACK rate */ - struct bandwidth_limit_detect bw_limit; u32 last_rtt; /* Used for jitter calculation */ };