162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Binary Increase Congestion control for TCP
462306a36Sopenharmony_ci * Home page:
562306a36Sopenharmony_ci *      http://netsrv.csc.ncsu.edu/twiki/bin/view/Main/BIC
662306a36Sopenharmony_ci * This is from the implementation of BICTCP in
762306a36Sopenharmony_ci * Lison-Xu, Kahaled Harfoush, and Injong Rhee.
862306a36Sopenharmony_ci *  "Binary Increase Congestion Control for Fast, Long Distance
962306a36Sopenharmony_ci *  Networks" in InfoComm 2004
1062306a36Sopenharmony_ci * Available from:
1162306a36Sopenharmony_ci *  http://netsrv.csc.ncsu.edu/export/bitcp.pdf
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * Unless BIC is enabled and congestion window is large
1462306a36Sopenharmony_ci * this behaves the same as the original Reno.
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/mm.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <net/tcp.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define BICTCP_BETA_SCALE    1024	/* Scale factor beta calculation
2262306a36Sopenharmony_ci					 * max_cwnd = snd_cwnd * beta
2362306a36Sopenharmony_ci					 */
2462306a36Sopenharmony_ci#define BICTCP_B		4	 /*
2562306a36Sopenharmony_ci					  * In binary search,
2662306a36Sopenharmony_ci					  * go to point (max+min)/N
2762306a36Sopenharmony_ci					  */
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic int fast_convergence = 1;
3062306a36Sopenharmony_cistatic int max_increment = 16;
3162306a36Sopenharmony_cistatic int low_window = 14;
3262306a36Sopenharmony_cistatic int beta = 819;		/* = 819/1024 (BICTCP_BETA_SCALE) */
3362306a36Sopenharmony_cistatic int initial_ssthresh;
3462306a36Sopenharmony_cistatic int smooth_part = 20;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cimodule_param(fast_convergence, int, 0644);
3762306a36Sopenharmony_ciMODULE_PARM_DESC(fast_convergence, "turn on/off fast convergence");
3862306a36Sopenharmony_cimodule_param(max_increment, int, 0644);
3962306a36Sopenharmony_ciMODULE_PARM_DESC(max_increment, "Limit on increment allowed during binary search");
4062306a36Sopenharmony_cimodule_param(low_window, int, 0644);
4162306a36Sopenharmony_ciMODULE_PARM_DESC(low_window, "lower bound on congestion window (for TCP friendliness)");
4262306a36Sopenharmony_cimodule_param(beta, int, 0644);
4362306a36Sopenharmony_ciMODULE_PARM_DESC(beta, "beta for multiplicative increase");
4462306a36Sopenharmony_cimodule_param(initial_ssthresh, int, 0644);
4562306a36Sopenharmony_ciMODULE_PARM_DESC(initial_ssthresh, "initial value of slow start threshold");
4662306a36Sopenharmony_cimodule_param(smooth_part, int, 0644);
4762306a36Sopenharmony_ciMODULE_PARM_DESC(smooth_part, "log(B/(B*Smin))/log(B/(B-1))+B, # of RTT from Wmax-B to Wmax");
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* BIC TCP Parameters */
5062306a36Sopenharmony_cistruct bictcp {
5162306a36Sopenharmony_ci	u32	cnt;		/* increase cwnd by 1 after ACKs */
5262306a36Sopenharmony_ci	u32	last_max_cwnd;	/* last maximum snd_cwnd */
5362306a36Sopenharmony_ci	u32	last_cwnd;	/* the last snd_cwnd */
5462306a36Sopenharmony_ci	u32	last_time;	/* time when updated last_cwnd */
5562306a36Sopenharmony_ci	u32	epoch_start;	/* beginning of an epoch */
5662306a36Sopenharmony_ci#define ACK_RATIO_SHIFT	4
5762306a36Sopenharmony_ci	u32	delayed_ack;	/* estimate the ratio of Packets/ACKs << 4 */
5862306a36Sopenharmony_ci};
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic inline void bictcp_reset(struct bictcp *ca)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	ca->cnt = 0;
6362306a36Sopenharmony_ci	ca->last_max_cwnd = 0;
6462306a36Sopenharmony_ci	ca->last_cwnd = 0;
6562306a36Sopenharmony_ci	ca->last_time = 0;
6662306a36Sopenharmony_ci	ca->epoch_start = 0;
6762306a36Sopenharmony_ci	ca->delayed_ack = 2 << ACK_RATIO_SHIFT;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic void bictcp_init(struct sock *sk)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct bictcp *ca = inet_csk_ca(sk);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	bictcp_reset(ca);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (initial_ssthresh)
7762306a36Sopenharmony_ci		tcp_sk(sk)->snd_ssthresh = initial_ssthresh;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/*
8162306a36Sopenharmony_ci * Compute congestion window to use.
8262306a36Sopenharmony_ci */
8362306a36Sopenharmony_cistatic inline void bictcp_update(struct bictcp *ca, u32 cwnd)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	if (ca->last_cwnd == cwnd &&
8662306a36Sopenharmony_ci	    (s32)(tcp_jiffies32 - ca->last_time) <= HZ / 32)
8762306a36Sopenharmony_ci		return;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	ca->last_cwnd = cwnd;
9062306a36Sopenharmony_ci	ca->last_time = tcp_jiffies32;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (ca->epoch_start == 0) /* record the beginning of an epoch */
9362306a36Sopenharmony_ci		ca->epoch_start = tcp_jiffies32;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	/* start off normal */
9662306a36Sopenharmony_ci	if (cwnd <= low_window) {
9762306a36Sopenharmony_ci		ca->cnt = cwnd;
9862306a36Sopenharmony_ci		return;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	/* binary increase */
10262306a36Sopenharmony_ci	if (cwnd < ca->last_max_cwnd) {
10362306a36Sopenharmony_ci		__u32	dist = (ca->last_max_cwnd - cwnd)
10462306a36Sopenharmony_ci			/ BICTCP_B;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		if (dist > max_increment)
10762306a36Sopenharmony_ci			/* linear increase */
10862306a36Sopenharmony_ci			ca->cnt = cwnd / max_increment;
10962306a36Sopenharmony_ci		else if (dist <= 1U)
11062306a36Sopenharmony_ci			/* binary search increase */
11162306a36Sopenharmony_ci			ca->cnt = (cwnd * smooth_part) / BICTCP_B;
11262306a36Sopenharmony_ci		else
11362306a36Sopenharmony_ci			/* binary search increase */
11462306a36Sopenharmony_ci			ca->cnt = cwnd / dist;
11562306a36Sopenharmony_ci	} else {
11662306a36Sopenharmony_ci		/* slow start AMD linear increase */
11762306a36Sopenharmony_ci		if (cwnd < ca->last_max_cwnd + BICTCP_B)
11862306a36Sopenharmony_ci			/* slow start */
11962306a36Sopenharmony_ci			ca->cnt = (cwnd * smooth_part) / BICTCP_B;
12062306a36Sopenharmony_ci		else if (cwnd < ca->last_max_cwnd + max_increment*(BICTCP_B-1))
12162306a36Sopenharmony_ci			/* slow start */
12262306a36Sopenharmony_ci			ca->cnt = (cwnd * (BICTCP_B-1))
12362306a36Sopenharmony_ci				/ (cwnd - ca->last_max_cwnd);
12462306a36Sopenharmony_ci		else
12562306a36Sopenharmony_ci			/* linear increase */
12662306a36Sopenharmony_ci			ca->cnt = cwnd / max_increment;
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	/* if in slow start or link utilization is very low */
13062306a36Sopenharmony_ci	if (ca->last_max_cwnd == 0) {
13162306a36Sopenharmony_ci		if (ca->cnt > 20) /* increase cwnd 5% per RTT */
13262306a36Sopenharmony_ci			ca->cnt = 20;
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	ca->cnt = (ca->cnt << ACK_RATIO_SHIFT) / ca->delayed_ack;
13662306a36Sopenharmony_ci	if (ca->cnt == 0)			/* cannot be zero */
13762306a36Sopenharmony_ci		ca->cnt = 1;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	struct tcp_sock *tp = tcp_sk(sk);
14362306a36Sopenharmony_ci	struct bictcp *ca = inet_csk_ca(sk);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (!tcp_is_cwnd_limited(sk))
14662306a36Sopenharmony_ci		return;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (tcp_in_slow_start(tp)) {
14962306a36Sopenharmony_ci		acked = tcp_slow_start(tp, acked);
15062306a36Sopenharmony_ci		if (!acked)
15162306a36Sopenharmony_ci			return;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci	bictcp_update(ca, tcp_snd_cwnd(tp));
15462306a36Sopenharmony_ci	tcp_cong_avoid_ai(tp, ca->cnt, acked);
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci/*
15862306a36Sopenharmony_ci *	behave like Reno until low_window is reached,
15962306a36Sopenharmony_ci *	then increase congestion window slowly
16062306a36Sopenharmony_ci */
16162306a36Sopenharmony_cistatic u32 bictcp_recalc_ssthresh(struct sock *sk)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	const struct tcp_sock *tp = tcp_sk(sk);
16462306a36Sopenharmony_ci	struct bictcp *ca = inet_csk_ca(sk);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	ca->epoch_start = 0;	/* end of epoch */
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/* Wmax and fast convergence */
16962306a36Sopenharmony_ci	if (tcp_snd_cwnd(tp) < ca->last_max_cwnd && fast_convergence)
17062306a36Sopenharmony_ci		ca->last_max_cwnd = (tcp_snd_cwnd(tp) * (BICTCP_BETA_SCALE + beta))
17162306a36Sopenharmony_ci			/ (2 * BICTCP_BETA_SCALE);
17262306a36Sopenharmony_ci	else
17362306a36Sopenharmony_ci		ca->last_max_cwnd = tcp_snd_cwnd(tp);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (tcp_snd_cwnd(tp) <= low_window)
17662306a36Sopenharmony_ci		return max(tcp_snd_cwnd(tp) >> 1U, 2U);
17762306a36Sopenharmony_ci	else
17862306a36Sopenharmony_ci		return max((tcp_snd_cwnd(tp) * beta) / BICTCP_BETA_SCALE, 2U);
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic void bictcp_state(struct sock *sk, u8 new_state)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	if (new_state == TCP_CA_Loss)
18462306a36Sopenharmony_ci		bictcp_reset(inet_csk_ca(sk));
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci/* Track delayed acknowledgment ratio using sliding window
18862306a36Sopenharmony_ci * ratio = (15*ratio + sample) / 16
18962306a36Sopenharmony_ci */
19062306a36Sopenharmony_cistatic void bictcp_acked(struct sock *sk, const struct ack_sample *sample)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	const struct inet_connection_sock *icsk = inet_csk(sk);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (icsk->icsk_ca_state == TCP_CA_Open) {
19562306a36Sopenharmony_ci		struct bictcp *ca = inet_csk_ca(sk);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci		ca->delayed_ack += sample->pkts_acked -
19862306a36Sopenharmony_ci			(ca->delayed_ack >> ACK_RATIO_SHIFT);
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic struct tcp_congestion_ops bictcp __read_mostly = {
20362306a36Sopenharmony_ci	.init		= bictcp_init,
20462306a36Sopenharmony_ci	.ssthresh	= bictcp_recalc_ssthresh,
20562306a36Sopenharmony_ci	.cong_avoid	= bictcp_cong_avoid,
20662306a36Sopenharmony_ci	.set_state	= bictcp_state,
20762306a36Sopenharmony_ci	.undo_cwnd	= tcp_reno_undo_cwnd,
20862306a36Sopenharmony_ci	.pkts_acked     = bictcp_acked,
20962306a36Sopenharmony_ci	.owner		= THIS_MODULE,
21062306a36Sopenharmony_ci	.name		= "bic",
21162306a36Sopenharmony_ci};
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic int __init bictcp_register(void)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct bictcp) > ICSK_CA_PRIV_SIZE);
21662306a36Sopenharmony_ci	return tcp_register_congestion_control(&bictcp);
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic void __exit bictcp_unregister(void)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	tcp_unregister_congestion_control(&bictcp);
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cimodule_init(bictcp_register);
22562306a36Sopenharmony_cimodule_exit(bictcp_unregister);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ciMODULE_AUTHOR("Stephen Hemminger");
22862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
22962306a36Sopenharmony_ciMODULE_DESCRIPTION("BIC TCP");
230