162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * CAIA Delay-Gradient (CDG) congestion control 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This implementation is based on the paper: 662306a36Sopenharmony_ci * D.A. Hayes and G. Armitage. "Revisiting TCP congestion control using 762306a36Sopenharmony_ci * delay gradients." In IFIP Networking, pages 328-341. Springer, 2011. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Scavenger traffic (Less-than-Best-Effort) should disable coexistence 1062306a36Sopenharmony_ci * heuristics using parameters use_shadow=0 and use_ineff=0. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Parameters window, backoff_beta, and backoff_factor are crucial for 1362306a36Sopenharmony_ci * throughput and delay. Future work is needed to determine better defaults, 1462306a36Sopenharmony_ci * and to provide guidelines for use in different environments/contexts. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Except for window, knobs are configured via /sys/module/tcp_cdg/parameters/. 1762306a36Sopenharmony_ci * Parameter window is only configurable when loading tcp_cdg as a module. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Notable differences from paper/FreeBSD: 2062306a36Sopenharmony_ci * o Using Hybrid Slow start and Proportional Rate Reduction. 2162306a36Sopenharmony_ci * o Add toggle for shadow window mechanism. Suggested by David Hayes. 2262306a36Sopenharmony_ci * o Add toggle for non-congestion loss tolerance. 2362306a36Sopenharmony_ci * o Scaling parameter G is changed to a backoff factor; 2462306a36Sopenharmony_ci * conversion is given by: backoff_factor = 1000/(G * window). 2562306a36Sopenharmony_ci * o Limit shadow window to 2 * cwnd, or to cwnd when application limited. 2662306a36Sopenharmony_ci * o More accurate e^-x. 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci#include <linux/kernel.h> 2962306a36Sopenharmony_ci#include <linux/random.h> 3062306a36Sopenharmony_ci#include <linux/module.h> 3162306a36Sopenharmony_ci#include <linux/sched/clock.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <net/tcp.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define HYSTART_ACK_TRAIN 1 3662306a36Sopenharmony_ci#define HYSTART_DELAY 2 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int window __read_mostly = 8; 3962306a36Sopenharmony_cistatic unsigned int backoff_beta __read_mostly = 0.7071 * 1024; /* sqrt 0.5 */ 4062306a36Sopenharmony_cistatic unsigned int backoff_factor __read_mostly = 42; 4162306a36Sopenharmony_cistatic unsigned int hystart_detect __read_mostly = 3; 4262306a36Sopenharmony_cistatic unsigned int use_ineff __read_mostly = 5; 4362306a36Sopenharmony_cistatic bool use_shadow __read_mostly = true; 4462306a36Sopenharmony_cistatic bool use_tolerance __read_mostly; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cimodule_param(window, int, 0444); 4762306a36Sopenharmony_ciMODULE_PARM_DESC(window, "gradient window size (power of two <= 256)"); 4862306a36Sopenharmony_cimodule_param(backoff_beta, uint, 0644); 4962306a36Sopenharmony_ciMODULE_PARM_DESC(backoff_beta, "backoff beta (0-1024)"); 5062306a36Sopenharmony_cimodule_param(backoff_factor, uint, 0644); 5162306a36Sopenharmony_ciMODULE_PARM_DESC(backoff_factor, "backoff probability scale factor"); 5262306a36Sopenharmony_cimodule_param(hystart_detect, uint, 0644); 5362306a36Sopenharmony_ciMODULE_PARM_DESC(hystart_detect, "use Hybrid Slow start " 5462306a36Sopenharmony_ci "(0: disabled, 1: ACK train, 2: delay threshold, 3: both)"); 5562306a36Sopenharmony_cimodule_param(use_ineff, uint, 0644); 5662306a36Sopenharmony_ciMODULE_PARM_DESC(use_ineff, "use ineffectual backoff detection (threshold)"); 5762306a36Sopenharmony_cimodule_param(use_shadow, bool, 0644); 5862306a36Sopenharmony_ciMODULE_PARM_DESC(use_shadow, "use shadow window heuristic"); 5962306a36Sopenharmony_cimodule_param(use_tolerance, bool, 0644); 6062306a36Sopenharmony_ciMODULE_PARM_DESC(use_tolerance, "use loss tolerance heuristic"); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistruct cdg_minmax { 6362306a36Sopenharmony_ci union { 6462306a36Sopenharmony_ci struct { 6562306a36Sopenharmony_ci s32 min; 6662306a36Sopenharmony_ci s32 max; 6762306a36Sopenharmony_ci }; 6862306a36Sopenharmony_ci u64 v64; 6962306a36Sopenharmony_ci }; 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cienum cdg_state { 7362306a36Sopenharmony_ci CDG_UNKNOWN = 0, 7462306a36Sopenharmony_ci CDG_NONFULL = 1, 7562306a36Sopenharmony_ci CDG_FULL = 2, 7662306a36Sopenharmony_ci CDG_BACKOFF = 3, 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistruct cdg { 8062306a36Sopenharmony_ci struct cdg_minmax rtt; 8162306a36Sopenharmony_ci struct cdg_minmax rtt_prev; 8262306a36Sopenharmony_ci struct cdg_minmax *gradients; 8362306a36Sopenharmony_ci struct cdg_minmax gsum; 8462306a36Sopenharmony_ci bool gfilled; 8562306a36Sopenharmony_ci u8 tail; 8662306a36Sopenharmony_ci u8 state; 8762306a36Sopenharmony_ci u8 delack; 8862306a36Sopenharmony_ci u32 rtt_seq; 8962306a36Sopenharmony_ci u32 shadow_wnd; 9062306a36Sopenharmony_ci u16 backoff_cnt; 9162306a36Sopenharmony_ci u16 sample_cnt; 9262306a36Sopenharmony_ci s32 delay_min; 9362306a36Sopenharmony_ci u32 last_ack; 9462306a36Sopenharmony_ci u32 round_start; 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/** 9862306a36Sopenharmony_ci * nexp_u32 - negative base-e exponential 9962306a36Sopenharmony_ci * @ux: x in units of micro 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * Returns exp(ux * -1e-6) * U32_MAX. 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_cistatic u32 __pure nexp_u32(u32 ux) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci static const u16 v[] = { 10662306a36Sopenharmony_ci /* exp(-x)*65536-1 for x = 0, 0.000256, 0.000512, ... */ 10762306a36Sopenharmony_ci 65535, 10862306a36Sopenharmony_ci 65518, 65501, 65468, 65401, 65267, 65001, 64470, 63422, 10962306a36Sopenharmony_ci 61378, 57484, 50423, 38795, 22965, 8047, 987, 14, 11062306a36Sopenharmony_ci }; 11162306a36Sopenharmony_ci u32 msb = ux >> 8; 11262306a36Sopenharmony_ci u32 res; 11362306a36Sopenharmony_ci int i; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* Cut off when ux >= 2^24 (actual result is <= 222/U32_MAX). */ 11662306a36Sopenharmony_ci if (msb > U16_MAX) 11762306a36Sopenharmony_ci return 0; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* Scale first eight bits linearly: */ 12062306a36Sopenharmony_ci res = U32_MAX - (ux & 0xff) * (U32_MAX / 1000000); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* Obtain e^(x + y + ...) by computing e^x * e^y * ...: */ 12362306a36Sopenharmony_ci for (i = 1; msb; i++, msb >>= 1) { 12462306a36Sopenharmony_ci u32 y = v[i & -(msb & 1)] + U32_C(1); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci res = ((u64)res * y) >> 16; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return res; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* Based on the HyStart algorithm (by Ha et al.) that is implemented in 13362306a36Sopenharmony_ci * tcp_cubic. Differences/experimental changes: 13462306a36Sopenharmony_ci * o Using Hayes' delayed ACK filter. 13562306a36Sopenharmony_ci * o Using a usec clock for the ACK train. 13662306a36Sopenharmony_ci * o Reset ACK train when application limited. 13762306a36Sopenharmony_ci * o Invoked at any cwnd (i.e. also when cwnd < 16). 13862306a36Sopenharmony_ci * o Invoked only when cwnd < ssthresh (i.e. not when cwnd == ssthresh). 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_cistatic void tcp_cdg_hystart_update(struct sock *sk) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct cdg *ca = inet_csk_ca(sk); 14362306a36Sopenharmony_ci struct tcp_sock *tp = tcp_sk(sk); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci ca->delay_min = min_not_zero(ca->delay_min, ca->rtt.min); 14662306a36Sopenharmony_ci if (ca->delay_min == 0) 14762306a36Sopenharmony_ci return; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (hystart_detect & HYSTART_ACK_TRAIN) { 15062306a36Sopenharmony_ci u32 now_us = tp->tcp_mstamp; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (ca->last_ack == 0 || !tcp_is_cwnd_limited(sk)) { 15362306a36Sopenharmony_ci ca->last_ack = now_us; 15462306a36Sopenharmony_ci ca->round_start = now_us; 15562306a36Sopenharmony_ci } else if (before(now_us, ca->last_ack + 3000)) { 15662306a36Sopenharmony_ci u32 base_owd = max(ca->delay_min / 2U, 125U); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci ca->last_ack = now_us; 15962306a36Sopenharmony_ci if (after(now_us, ca->round_start + base_owd)) { 16062306a36Sopenharmony_ci NET_INC_STATS(sock_net(sk), 16162306a36Sopenharmony_ci LINUX_MIB_TCPHYSTARTTRAINDETECT); 16262306a36Sopenharmony_ci NET_ADD_STATS(sock_net(sk), 16362306a36Sopenharmony_ci LINUX_MIB_TCPHYSTARTTRAINCWND, 16462306a36Sopenharmony_ci tcp_snd_cwnd(tp)); 16562306a36Sopenharmony_ci tp->snd_ssthresh = tcp_snd_cwnd(tp); 16662306a36Sopenharmony_ci return; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (hystart_detect & HYSTART_DELAY) { 17262306a36Sopenharmony_ci if (ca->sample_cnt < 8) { 17362306a36Sopenharmony_ci ca->sample_cnt++; 17462306a36Sopenharmony_ci } else { 17562306a36Sopenharmony_ci s32 thresh = max(ca->delay_min + ca->delay_min / 8U, 17662306a36Sopenharmony_ci 125U); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (ca->rtt.min > thresh) { 17962306a36Sopenharmony_ci NET_INC_STATS(sock_net(sk), 18062306a36Sopenharmony_ci LINUX_MIB_TCPHYSTARTDELAYDETECT); 18162306a36Sopenharmony_ci NET_ADD_STATS(sock_net(sk), 18262306a36Sopenharmony_ci LINUX_MIB_TCPHYSTARTDELAYCWND, 18362306a36Sopenharmony_ci tcp_snd_cwnd(tp)); 18462306a36Sopenharmony_ci tp->snd_ssthresh = tcp_snd_cwnd(tp); 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic s32 tcp_cdg_grad(struct cdg *ca) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci s32 gmin = ca->rtt.min - ca->rtt_prev.min; 19362306a36Sopenharmony_ci s32 gmax = ca->rtt.max - ca->rtt_prev.max; 19462306a36Sopenharmony_ci s32 grad; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (ca->gradients) { 19762306a36Sopenharmony_ci ca->gsum.min += gmin - ca->gradients[ca->tail].min; 19862306a36Sopenharmony_ci ca->gsum.max += gmax - ca->gradients[ca->tail].max; 19962306a36Sopenharmony_ci ca->gradients[ca->tail].min = gmin; 20062306a36Sopenharmony_ci ca->gradients[ca->tail].max = gmax; 20162306a36Sopenharmony_ci ca->tail = (ca->tail + 1) & (window - 1); 20262306a36Sopenharmony_ci gmin = ca->gsum.min; 20362306a36Sopenharmony_ci gmax = ca->gsum.max; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* We keep sums to ignore gradients during cwnd reductions; 20762306a36Sopenharmony_ci * the paper's smoothed gradients otherwise simplify to: 20862306a36Sopenharmony_ci * (rtt_latest - rtt_oldest) / window. 20962306a36Sopenharmony_ci * 21062306a36Sopenharmony_ci * We also drop division by window here. 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci grad = gmin > 0 ? gmin : gmax; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* Extrapolate missing values in gradient window: */ 21562306a36Sopenharmony_ci if (!ca->gfilled) { 21662306a36Sopenharmony_ci if (!ca->gradients && window > 1) 21762306a36Sopenharmony_ci grad *= window; /* Memory allocation failed. */ 21862306a36Sopenharmony_ci else if (ca->tail == 0) 21962306a36Sopenharmony_ci ca->gfilled = true; 22062306a36Sopenharmony_ci else 22162306a36Sopenharmony_ci grad = (grad * window) / (int)ca->tail; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* Backoff was effectual: */ 22562306a36Sopenharmony_ci if (gmin <= -32 || gmax <= -32) 22662306a36Sopenharmony_ci ca->backoff_cnt = 0; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (use_tolerance) { 22962306a36Sopenharmony_ci /* Reduce small variations to zero: */ 23062306a36Sopenharmony_ci gmin = DIV_ROUND_CLOSEST(gmin, 64); 23162306a36Sopenharmony_ci gmax = DIV_ROUND_CLOSEST(gmax, 64); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (gmin > 0 && gmax <= 0) 23462306a36Sopenharmony_ci ca->state = CDG_FULL; 23562306a36Sopenharmony_ci else if ((gmin > 0 && gmax > 0) || gmax < 0) 23662306a36Sopenharmony_ci ca->state = CDG_NONFULL; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci return grad; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic bool tcp_cdg_backoff(struct sock *sk, u32 grad) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct cdg *ca = inet_csk_ca(sk); 24462306a36Sopenharmony_ci struct tcp_sock *tp = tcp_sk(sk); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (get_random_u32() <= nexp_u32(grad * backoff_factor)) 24762306a36Sopenharmony_ci return false; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (use_ineff) { 25062306a36Sopenharmony_ci ca->backoff_cnt++; 25162306a36Sopenharmony_ci if (ca->backoff_cnt > use_ineff) 25262306a36Sopenharmony_ci return false; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci ca->shadow_wnd = max(ca->shadow_wnd, tcp_snd_cwnd(tp)); 25662306a36Sopenharmony_ci ca->state = CDG_BACKOFF; 25762306a36Sopenharmony_ci tcp_enter_cwr(sk); 25862306a36Sopenharmony_ci return true; 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci/* Not called in CWR or Recovery state. */ 26262306a36Sopenharmony_cistatic void tcp_cdg_cong_avoid(struct sock *sk, u32 ack, u32 acked) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci struct cdg *ca = inet_csk_ca(sk); 26562306a36Sopenharmony_ci struct tcp_sock *tp = tcp_sk(sk); 26662306a36Sopenharmony_ci u32 prior_snd_cwnd; 26762306a36Sopenharmony_ci u32 incr; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (tcp_in_slow_start(tp) && hystart_detect) 27062306a36Sopenharmony_ci tcp_cdg_hystart_update(sk); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (after(ack, ca->rtt_seq) && ca->rtt.v64) { 27362306a36Sopenharmony_ci s32 grad = 0; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (ca->rtt_prev.v64) 27662306a36Sopenharmony_ci grad = tcp_cdg_grad(ca); 27762306a36Sopenharmony_ci ca->rtt_seq = tp->snd_nxt; 27862306a36Sopenharmony_ci ca->rtt_prev = ca->rtt; 27962306a36Sopenharmony_ci ca->rtt.v64 = 0; 28062306a36Sopenharmony_ci ca->last_ack = 0; 28162306a36Sopenharmony_ci ca->sample_cnt = 0; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (grad > 0 && tcp_cdg_backoff(sk, grad)) 28462306a36Sopenharmony_ci return; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (!tcp_is_cwnd_limited(sk)) { 28862306a36Sopenharmony_ci ca->shadow_wnd = min(ca->shadow_wnd, tcp_snd_cwnd(tp)); 28962306a36Sopenharmony_ci return; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci prior_snd_cwnd = tcp_snd_cwnd(tp); 29362306a36Sopenharmony_ci tcp_reno_cong_avoid(sk, ack, acked); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci incr = tcp_snd_cwnd(tp) - prior_snd_cwnd; 29662306a36Sopenharmony_ci ca->shadow_wnd = max(ca->shadow_wnd, ca->shadow_wnd + incr); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic void tcp_cdg_acked(struct sock *sk, const struct ack_sample *sample) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct cdg *ca = inet_csk_ca(sk); 30262306a36Sopenharmony_ci struct tcp_sock *tp = tcp_sk(sk); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (sample->rtt_us <= 0) 30562306a36Sopenharmony_ci return; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* A heuristic for filtering delayed ACKs, adapted from: 30862306a36Sopenharmony_ci * D.A. Hayes. "Timing enhancements to the FreeBSD kernel to support 30962306a36Sopenharmony_ci * delay and rate based TCP mechanisms." TR 100219A. CAIA, 2010. 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_ci if (tp->sacked_out == 0) { 31262306a36Sopenharmony_ci if (sample->pkts_acked == 1 && ca->delack) { 31362306a36Sopenharmony_ci /* A delayed ACK is only used for the minimum if it is 31462306a36Sopenharmony_ci * provenly lower than an existing non-zero minimum. 31562306a36Sopenharmony_ci */ 31662306a36Sopenharmony_ci ca->rtt.min = min(ca->rtt.min, sample->rtt_us); 31762306a36Sopenharmony_ci ca->delack--; 31862306a36Sopenharmony_ci return; 31962306a36Sopenharmony_ci } else if (sample->pkts_acked > 1 && ca->delack < 5) { 32062306a36Sopenharmony_ci ca->delack++; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci ca->rtt.min = min_not_zero(ca->rtt.min, sample->rtt_us); 32562306a36Sopenharmony_ci ca->rtt.max = max(ca->rtt.max, sample->rtt_us); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic u32 tcp_cdg_ssthresh(struct sock *sk) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci struct cdg *ca = inet_csk_ca(sk); 33162306a36Sopenharmony_ci struct tcp_sock *tp = tcp_sk(sk); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (ca->state == CDG_BACKOFF) 33462306a36Sopenharmony_ci return max(2U, (tcp_snd_cwnd(tp) * min(1024U, backoff_beta)) >> 10); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (ca->state == CDG_NONFULL && use_tolerance) 33762306a36Sopenharmony_ci return tcp_snd_cwnd(tp); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci ca->shadow_wnd = min(ca->shadow_wnd >> 1, tcp_snd_cwnd(tp)); 34062306a36Sopenharmony_ci if (use_shadow) 34162306a36Sopenharmony_ci return max3(2U, ca->shadow_wnd, tcp_snd_cwnd(tp) >> 1); 34262306a36Sopenharmony_ci return max(2U, tcp_snd_cwnd(tp) >> 1); 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic void tcp_cdg_cwnd_event(struct sock *sk, const enum tcp_ca_event ev) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci struct cdg *ca = inet_csk_ca(sk); 34862306a36Sopenharmony_ci struct tcp_sock *tp = tcp_sk(sk); 34962306a36Sopenharmony_ci struct cdg_minmax *gradients; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci switch (ev) { 35262306a36Sopenharmony_ci case CA_EVENT_CWND_RESTART: 35362306a36Sopenharmony_ci gradients = ca->gradients; 35462306a36Sopenharmony_ci if (gradients) 35562306a36Sopenharmony_ci memset(gradients, 0, window * sizeof(gradients[0])); 35662306a36Sopenharmony_ci memset(ca, 0, sizeof(*ca)); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci ca->gradients = gradients; 35962306a36Sopenharmony_ci ca->rtt_seq = tp->snd_nxt; 36062306a36Sopenharmony_ci ca->shadow_wnd = tcp_snd_cwnd(tp); 36162306a36Sopenharmony_ci break; 36262306a36Sopenharmony_ci case CA_EVENT_COMPLETE_CWR: 36362306a36Sopenharmony_ci ca->state = CDG_UNKNOWN; 36462306a36Sopenharmony_ci ca->rtt_seq = tp->snd_nxt; 36562306a36Sopenharmony_ci ca->rtt_prev = ca->rtt; 36662306a36Sopenharmony_ci ca->rtt.v64 = 0; 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci default: 36962306a36Sopenharmony_ci break; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic void tcp_cdg_init(struct sock *sk) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct cdg *ca = inet_csk_ca(sk); 37662306a36Sopenharmony_ci struct tcp_sock *tp = tcp_sk(sk); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci ca->gradients = NULL; 37962306a36Sopenharmony_ci /* We silently fall back to window = 1 if allocation fails. */ 38062306a36Sopenharmony_ci if (window > 1) 38162306a36Sopenharmony_ci ca->gradients = kcalloc(window, sizeof(ca->gradients[0]), 38262306a36Sopenharmony_ci GFP_NOWAIT | __GFP_NOWARN); 38362306a36Sopenharmony_ci ca->rtt_seq = tp->snd_nxt; 38462306a36Sopenharmony_ci ca->shadow_wnd = tcp_snd_cwnd(tp); 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic void tcp_cdg_release(struct sock *sk) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci struct cdg *ca = inet_csk_ca(sk); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci kfree(ca->gradients); 39262306a36Sopenharmony_ci ca->gradients = NULL; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic struct tcp_congestion_ops tcp_cdg __read_mostly = { 39662306a36Sopenharmony_ci .cong_avoid = tcp_cdg_cong_avoid, 39762306a36Sopenharmony_ci .cwnd_event = tcp_cdg_cwnd_event, 39862306a36Sopenharmony_ci .pkts_acked = tcp_cdg_acked, 39962306a36Sopenharmony_ci .undo_cwnd = tcp_reno_undo_cwnd, 40062306a36Sopenharmony_ci .ssthresh = tcp_cdg_ssthresh, 40162306a36Sopenharmony_ci .release = tcp_cdg_release, 40262306a36Sopenharmony_ci .init = tcp_cdg_init, 40362306a36Sopenharmony_ci .owner = THIS_MODULE, 40462306a36Sopenharmony_ci .name = "cdg", 40562306a36Sopenharmony_ci}; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic int __init tcp_cdg_register(void) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci if (backoff_beta > 1024 || window < 1 || window > 256) 41062306a36Sopenharmony_ci return -ERANGE; 41162306a36Sopenharmony_ci if (!is_power_of_2(window)) 41262306a36Sopenharmony_ci return -EINVAL; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct cdg) > ICSK_CA_PRIV_SIZE); 41562306a36Sopenharmony_ci tcp_register_congestion_control(&tcp_cdg); 41662306a36Sopenharmony_ci return 0; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic void __exit tcp_cdg_unregister(void) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci tcp_unregister_congestion_control(&tcp_cdg); 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cimodule_init(tcp_cdg_register); 42562306a36Sopenharmony_cimodule_exit(tcp_cdg_unregister); 42662306a36Sopenharmony_ciMODULE_AUTHOR("Kenneth Klette Jonassen"); 42762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 42862306a36Sopenharmony_ciMODULE_DESCRIPTION("TCP CDG"); 429