162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Sally Floyd's High Speed TCP (RFC 3649) congestion control 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * See https://www.icir.org/floyd/hstcp.html 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * John Heffner <jheffner@psc.edu> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <net/tcp.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* From AIMD tables from RFC 3649 appendix B, 1462306a36Sopenharmony_ci * with fixed-point MD scaled <<8. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_cistatic const struct hstcp_aimd_val { 1762306a36Sopenharmony_ci unsigned int cwnd; 1862306a36Sopenharmony_ci unsigned int md; 1962306a36Sopenharmony_ci} hstcp_aimd_vals[] = { 2062306a36Sopenharmony_ci { 38, 128, /* 0.50 */ }, 2162306a36Sopenharmony_ci { 118, 112, /* 0.44 */ }, 2262306a36Sopenharmony_ci { 221, 104, /* 0.41 */ }, 2362306a36Sopenharmony_ci { 347, 98, /* 0.38 */ }, 2462306a36Sopenharmony_ci { 495, 93, /* 0.37 */ }, 2562306a36Sopenharmony_ci { 663, 89, /* 0.35 */ }, 2662306a36Sopenharmony_ci { 851, 86, /* 0.34 */ }, 2762306a36Sopenharmony_ci { 1058, 83, /* 0.33 */ }, 2862306a36Sopenharmony_ci { 1284, 81, /* 0.32 */ }, 2962306a36Sopenharmony_ci { 1529, 78, /* 0.31 */ }, 3062306a36Sopenharmony_ci { 1793, 76, /* 0.30 */ }, 3162306a36Sopenharmony_ci { 2076, 74, /* 0.29 */ }, 3262306a36Sopenharmony_ci { 2378, 72, /* 0.28 */ }, 3362306a36Sopenharmony_ci { 2699, 71, /* 0.28 */ }, 3462306a36Sopenharmony_ci { 3039, 69, /* 0.27 */ }, 3562306a36Sopenharmony_ci { 3399, 68, /* 0.27 */ }, 3662306a36Sopenharmony_ci { 3778, 66, /* 0.26 */ }, 3762306a36Sopenharmony_ci { 4177, 65, /* 0.26 */ }, 3862306a36Sopenharmony_ci { 4596, 64, /* 0.25 */ }, 3962306a36Sopenharmony_ci { 5036, 62, /* 0.25 */ }, 4062306a36Sopenharmony_ci { 5497, 61, /* 0.24 */ }, 4162306a36Sopenharmony_ci { 5979, 60, /* 0.24 */ }, 4262306a36Sopenharmony_ci { 6483, 59, /* 0.23 */ }, 4362306a36Sopenharmony_ci { 7009, 58, /* 0.23 */ }, 4462306a36Sopenharmony_ci { 7558, 57, /* 0.22 */ }, 4562306a36Sopenharmony_ci { 8130, 56, /* 0.22 */ }, 4662306a36Sopenharmony_ci { 8726, 55, /* 0.22 */ }, 4762306a36Sopenharmony_ci { 9346, 54, /* 0.21 */ }, 4862306a36Sopenharmony_ci { 9991, 53, /* 0.21 */ }, 4962306a36Sopenharmony_ci { 10661, 52, /* 0.21 */ }, 5062306a36Sopenharmony_ci { 11358, 52, /* 0.20 */ }, 5162306a36Sopenharmony_ci { 12082, 51, /* 0.20 */ }, 5262306a36Sopenharmony_ci { 12834, 50, /* 0.20 */ }, 5362306a36Sopenharmony_ci { 13614, 49, /* 0.19 */ }, 5462306a36Sopenharmony_ci { 14424, 48, /* 0.19 */ }, 5562306a36Sopenharmony_ci { 15265, 48, /* 0.19 */ }, 5662306a36Sopenharmony_ci { 16137, 47, /* 0.19 */ }, 5762306a36Sopenharmony_ci { 17042, 46, /* 0.18 */ }, 5862306a36Sopenharmony_ci { 17981, 45, /* 0.18 */ }, 5962306a36Sopenharmony_ci { 18955, 45, /* 0.18 */ }, 6062306a36Sopenharmony_ci { 19965, 44, /* 0.17 */ }, 6162306a36Sopenharmony_ci { 21013, 43, /* 0.17 */ }, 6262306a36Sopenharmony_ci { 22101, 43, /* 0.17 */ }, 6362306a36Sopenharmony_ci { 23230, 42, /* 0.17 */ }, 6462306a36Sopenharmony_ci { 24402, 41, /* 0.16 */ }, 6562306a36Sopenharmony_ci { 25618, 41, /* 0.16 */ }, 6662306a36Sopenharmony_ci { 26881, 40, /* 0.16 */ }, 6762306a36Sopenharmony_ci { 28193, 39, /* 0.16 */ }, 6862306a36Sopenharmony_ci { 29557, 39, /* 0.15 */ }, 6962306a36Sopenharmony_ci { 30975, 38, /* 0.15 */ }, 7062306a36Sopenharmony_ci { 32450, 38, /* 0.15 */ }, 7162306a36Sopenharmony_ci { 33986, 37, /* 0.15 */ }, 7262306a36Sopenharmony_ci { 35586, 36, /* 0.14 */ }, 7362306a36Sopenharmony_ci { 37253, 36, /* 0.14 */ }, 7462306a36Sopenharmony_ci { 38992, 35, /* 0.14 */ }, 7562306a36Sopenharmony_ci { 40808, 35, /* 0.14 */ }, 7662306a36Sopenharmony_ci { 42707, 34, /* 0.13 */ }, 7762306a36Sopenharmony_ci { 44694, 33, /* 0.13 */ }, 7862306a36Sopenharmony_ci { 46776, 33, /* 0.13 */ }, 7962306a36Sopenharmony_ci { 48961, 32, /* 0.13 */ }, 8062306a36Sopenharmony_ci { 51258, 32, /* 0.13 */ }, 8162306a36Sopenharmony_ci { 53677, 31, /* 0.12 */ }, 8262306a36Sopenharmony_ci { 56230, 30, /* 0.12 */ }, 8362306a36Sopenharmony_ci { 58932, 30, /* 0.12 */ }, 8462306a36Sopenharmony_ci { 61799, 29, /* 0.12 */ }, 8562306a36Sopenharmony_ci { 64851, 28, /* 0.11 */ }, 8662306a36Sopenharmony_ci { 68113, 28, /* 0.11 */ }, 8762306a36Sopenharmony_ci { 71617, 27, /* 0.11 */ }, 8862306a36Sopenharmony_ci { 75401, 26, /* 0.10 */ }, 8962306a36Sopenharmony_ci { 79517, 26, /* 0.10 */ }, 9062306a36Sopenharmony_ci { 84035, 25, /* 0.10 */ }, 9162306a36Sopenharmony_ci { 89053, 24, /* 0.10 */ }, 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci#define HSTCP_AIMD_MAX ARRAY_SIZE(hstcp_aimd_vals) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistruct hstcp { 9762306a36Sopenharmony_ci u32 ai; 9862306a36Sopenharmony_ci}; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic void hstcp_init(struct sock *sk) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct tcp_sock *tp = tcp_sk(sk); 10362306a36Sopenharmony_ci struct hstcp *ca = inet_csk_ca(sk); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci ca->ai = 0; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* Ensure the MD arithmetic works. This is somewhat pedantic, 10862306a36Sopenharmony_ci * since I don't think we will see a cwnd this large. :) */ 10962306a36Sopenharmony_ci tp->snd_cwnd_clamp = min_t(u32, tp->snd_cwnd_clamp, 0xffffffff/128); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic void hstcp_cong_avoid(struct sock *sk, u32 ack, u32 acked) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct tcp_sock *tp = tcp_sk(sk); 11562306a36Sopenharmony_ci struct hstcp *ca = inet_csk_ca(sk); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (!tcp_is_cwnd_limited(sk)) 11862306a36Sopenharmony_ci return; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (tcp_in_slow_start(tp)) 12162306a36Sopenharmony_ci tcp_slow_start(tp, acked); 12262306a36Sopenharmony_ci else { 12362306a36Sopenharmony_ci /* Update AIMD parameters. 12462306a36Sopenharmony_ci * 12562306a36Sopenharmony_ci * We want to guarantee that: 12662306a36Sopenharmony_ci * hstcp_aimd_vals[ca->ai-1].cwnd < 12762306a36Sopenharmony_ci * snd_cwnd <= 12862306a36Sopenharmony_ci * hstcp_aimd_vals[ca->ai].cwnd 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ci if (tcp_snd_cwnd(tp) > hstcp_aimd_vals[ca->ai].cwnd) { 13162306a36Sopenharmony_ci while (tcp_snd_cwnd(tp) > hstcp_aimd_vals[ca->ai].cwnd && 13262306a36Sopenharmony_ci ca->ai < HSTCP_AIMD_MAX - 1) 13362306a36Sopenharmony_ci ca->ai++; 13462306a36Sopenharmony_ci } else if (ca->ai && tcp_snd_cwnd(tp) <= hstcp_aimd_vals[ca->ai-1].cwnd) { 13562306a36Sopenharmony_ci while (ca->ai && tcp_snd_cwnd(tp) <= hstcp_aimd_vals[ca->ai-1].cwnd) 13662306a36Sopenharmony_ci ca->ai--; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Do additive increase */ 14062306a36Sopenharmony_ci if (tcp_snd_cwnd(tp) < tp->snd_cwnd_clamp) { 14162306a36Sopenharmony_ci /* cwnd = cwnd + a(w) / cwnd */ 14262306a36Sopenharmony_ci tp->snd_cwnd_cnt += ca->ai + 1; 14362306a36Sopenharmony_ci if (tp->snd_cwnd_cnt >= tcp_snd_cwnd(tp)) { 14462306a36Sopenharmony_ci tp->snd_cwnd_cnt -= tcp_snd_cwnd(tp); 14562306a36Sopenharmony_ci tcp_snd_cwnd_set(tp, tcp_snd_cwnd(tp) + 1); 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic u32 hstcp_ssthresh(struct sock *sk) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci const struct tcp_sock *tp = tcp_sk(sk); 15462306a36Sopenharmony_ci struct hstcp *ca = inet_csk_ca(sk); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* Do multiplicative decrease */ 15762306a36Sopenharmony_ci return max(tcp_snd_cwnd(tp) - ((tcp_snd_cwnd(tp) * hstcp_aimd_vals[ca->ai].md) >> 8), 2U); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic struct tcp_congestion_ops tcp_highspeed __read_mostly = { 16162306a36Sopenharmony_ci .init = hstcp_init, 16262306a36Sopenharmony_ci .ssthresh = hstcp_ssthresh, 16362306a36Sopenharmony_ci .undo_cwnd = tcp_reno_undo_cwnd, 16462306a36Sopenharmony_ci .cong_avoid = hstcp_cong_avoid, 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci .owner = THIS_MODULE, 16762306a36Sopenharmony_ci .name = "highspeed" 16862306a36Sopenharmony_ci}; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic int __init hstcp_register(void) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct hstcp) > ICSK_CA_PRIV_SIZE); 17362306a36Sopenharmony_ci return tcp_register_congestion_control(&tcp_highspeed); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic void __exit hstcp_unregister(void) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci tcp_unregister_congestion_control(&tcp_highspeed); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cimodule_init(hstcp_register); 18262306a36Sopenharmony_cimodule_exit(hstcp_unregister); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ciMODULE_AUTHOR("John Heffner"); 18562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 18662306a36Sopenharmony_ciMODULE_DESCRIPTION("High Speed TCP"); 187