xref: /openbmc/linux/net/ipv4/tcp_illinois.c (revision c462238d6a6d8ee855bda10f9fff442971540ed2)
1*c462238dSStephen Hemminger /*
2*c462238dSStephen Hemminger  * TCP Illinois congestion control.
3*c462238dSStephen Hemminger  * Home page:
4*c462238dSStephen Hemminger  *	http://www.ews.uiuc.edu/~shaoliu/tcpillinois/index.html
5*c462238dSStephen Hemminger  *
6*c462238dSStephen Hemminger  * The algorithm is described in:
7*c462238dSStephen Hemminger  * "TCP-Illinois: A Loss and Delay-Based Congestion Control Algorithm
8*c462238dSStephen Hemminger  *  for High-Speed Networks"
9*c462238dSStephen Hemminger  * http://www.ews.uiuc.edu/~shaoliu/papersandslides/liubassri06perf.pdf
10*c462238dSStephen Hemminger  *
11*c462238dSStephen Hemminger  * Implemented from description in paper and ns-2 simulation.
12*c462238dSStephen Hemminger  * Copyright (C) 2007 Stephen Hemminger <shemminger@linux-foundation.org>
13*c462238dSStephen Hemminger  */
14*c462238dSStephen Hemminger 
15*c462238dSStephen Hemminger #include <linux/module.h>
16*c462238dSStephen Hemminger #include <linux/skbuff.h>
17*c462238dSStephen Hemminger #include <linux/inet_diag.h>
18*c462238dSStephen Hemminger #include <asm/div64.h>
19*c462238dSStephen Hemminger #include <net/tcp.h>
20*c462238dSStephen Hemminger 
21*c462238dSStephen Hemminger #define ALPHA_SHIFT	7
22*c462238dSStephen Hemminger #define ALPHA_SCALE	(1u<<ALPHA_SHIFT)
23*c462238dSStephen Hemminger #define ALPHA_MIN	((3*ALPHA_SCALE)/10)	/* ~0.3 */
24*c462238dSStephen Hemminger #define ALPHA_MAX	(10*ALPHA_SCALE)	/* 10.0 */
25*c462238dSStephen Hemminger #define ALPHA_BASE	ALPHA_SCALE		/* 1.0 */
26*c462238dSStephen Hemminger 
27*c462238dSStephen Hemminger #define BETA_SHIFT	6
28*c462238dSStephen Hemminger #define BETA_SCALE	(1u<<BETA_SHIFT)
29*c462238dSStephen Hemminger #define BETA_MIN	(BETA_SCALE/8)		/* 0.8 */
30*c462238dSStephen Hemminger #define BETA_MAX	(BETA_SCALE/2)
31*c462238dSStephen Hemminger #define BETA_BASE	BETA_MAX		/* 0.5 */
32*c462238dSStephen Hemminger 
33*c462238dSStephen Hemminger #define THETA		5
34*c462238dSStephen Hemminger 
35*c462238dSStephen Hemminger static int win_thresh __read_mostly = 15;
36*c462238dSStephen Hemminger module_param(win_thresh, int, 0644);
37*c462238dSStephen Hemminger MODULE_PARM_DESC(win_thresh, "Window threshold for starting adaptive sizing");
38*c462238dSStephen Hemminger 
39*c462238dSStephen Hemminger #define MAX_RTT		0x7fffffff
40*c462238dSStephen Hemminger 
41*c462238dSStephen Hemminger /* TCP Illinois Parameters */
42*c462238dSStephen Hemminger struct tcp_illinois {
43*c462238dSStephen Hemminger 	u32	last_alpha;
44*c462238dSStephen Hemminger 	u32	min_rtt;
45*c462238dSStephen Hemminger 	u32	max_rtt;
46*c462238dSStephen Hemminger 	u32	rtt_low;
47*c462238dSStephen Hemminger 	u32	rtt_cnt;
48*c462238dSStephen Hemminger 	u64	sum_rtt;
49*c462238dSStephen Hemminger };
50*c462238dSStephen Hemminger 
51*c462238dSStephen Hemminger static void tcp_illinois_init(struct sock *sk)
52*c462238dSStephen Hemminger {
53*c462238dSStephen Hemminger 	struct tcp_illinois *ca = inet_csk_ca(sk);
54*c462238dSStephen Hemminger 
55*c462238dSStephen Hemminger 	ca->last_alpha = ALPHA_BASE;
56*c462238dSStephen Hemminger 	ca->min_rtt = 0x7fffffff;
57*c462238dSStephen Hemminger }
58*c462238dSStephen Hemminger 
59*c462238dSStephen Hemminger /*
60*c462238dSStephen Hemminger  * Keep track of min, max and average RTT
61*c462238dSStephen Hemminger  */
62*c462238dSStephen Hemminger static void tcp_illinois_rtt_calc(struct sock *sk, u32 rtt)
63*c462238dSStephen Hemminger {
64*c462238dSStephen Hemminger 	struct tcp_illinois *ca = inet_csk_ca(sk);
65*c462238dSStephen Hemminger 
66*c462238dSStephen Hemminger 	if (rtt < ca->min_rtt)
67*c462238dSStephen Hemminger 		ca->min_rtt = rtt;
68*c462238dSStephen Hemminger 	if (rtt > ca->max_rtt)
69*c462238dSStephen Hemminger 		ca->max_rtt = rtt;
70*c462238dSStephen Hemminger 
71*c462238dSStephen Hemminger 	if (++ca->rtt_cnt == 1)
72*c462238dSStephen Hemminger 		ca->sum_rtt = rtt;
73*c462238dSStephen Hemminger 	else
74*c462238dSStephen Hemminger 		ca->sum_rtt += rtt;
75*c462238dSStephen Hemminger }
76*c462238dSStephen Hemminger 
77*c462238dSStephen Hemminger /* max queuing delay */
78*c462238dSStephen Hemminger static inline u32 max_delay(const struct tcp_illinois *ca)
79*c462238dSStephen Hemminger {
80*c462238dSStephen Hemminger 	return ca->max_rtt - ca->min_rtt;
81*c462238dSStephen Hemminger }
82*c462238dSStephen Hemminger 
83*c462238dSStephen Hemminger /* average queueing delay */
84*c462238dSStephen Hemminger static u32 avg_delay(struct tcp_illinois *ca)
85*c462238dSStephen Hemminger {
86*c462238dSStephen Hemminger 	u64 avg_rtt = ca->sum_rtt;
87*c462238dSStephen Hemminger 
88*c462238dSStephen Hemminger 	do_div(avg_rtt, ca->rtt_cnt);
89*c462238dSStephen Hemminger 
90*c462238dSStephen Hemminger 	ca->sum_rtt = 0;
91*c462238dSStephen Hemminger 	ca->rtt_cnt = 0;
92*c462238dSStephen Hemminger 
93*c462238dSStephen Hemminger 	return avg_rtt - ca->min_rtt;
94*c462238dSStephen Hemminger }
95*c462238dSStephen Hemminger 
96*c462238dSStephen Hemminger /*
97*c462238dSStephen Hemminger  * Compute value of alpha used for additive increase.
98*c462238dSStephen Hemminger  * If small window then use 1.0, equivalent to Reno.
99*c462238dSStephen Hemminger  *
100*c462238dSStephen Hemminger  * For larger windows, adjust based on average delay.
101*c462238dSStephen Hemminger  * A. If average delay is at minimum (we are uncongested),
102*c462238dSStephen Hemminger  *    then use large alpha (10.0) to increase faster.
103*c462238dSStephen Hemminger  * B. If average delay is at maximum (getting congested)
104*c462238dSStephen Hemminger  *    then use small alpha (1.0)
105*c462238dSStephen Hemminger  *
106*c462238dSStephen Hemminger  * The result is a convex window growth curve.
107*c462238dSStephen Hemminger  */
108*c462238dSStephen Hemminger static u32 alpha(const struct sock *sk)
109*c462238dSStephen Hemminger {
110*c462238dSStephen Hemminger 	struct tcp_sock *tp = tcp_sk(sk);
111*c462238dSStephen Hemminger 	struct tcp_illinois *ca = inet_csk_ca(sk);
112*c462238dSStephen Hemminger 	u32 dm = max_delay(ca);
113*c462238dSStephen Hemminger 	u32 da = avg_delay(ca);
114*c462238dSStephen Hemminger 	u32 d1, a;
115*c462238dSStephen Hemminger 
116*c462238dSStephen Hemminger 	if (tp->snd_cwnd < win_thresh)
117*c462238dSStephen Hemminger 		return ALPHA_BASE;	/* same as Reno (1.0) */
118*c462238dSStephen Hemminger 
119*c462238dSStephen Hemminger 	d1 = dm / 100;
120*c462238dSStephen Hemminger 	if (da <= d1) {
121*c462238dSStephen Hemminger 		/* Don't let noise force agressive response */
122*c462238dSStephen Hemminger 		if (ca->rtt_low < THETA) {
123*c462238dSStephen Hemminger 			++ca->rtt_low;
124*c462238dSStephen Hemminger 			return ca->last_alpha;
125*c462238dSStephen Hemminger 		} else
126*c462238dSStephen Hemminger 			return ALPHA_MAX;
127*c462238dSStephen Hemminger 	}
128*c462238dSStephen Hemminger 
129*c462238dSStephen Hemminger 	ca->rtt_low = 0;
130*c462238dSStephen Hemminger 
131*c462238dSStephen Hemminger 	/*
132*c462238dSStephen Hemminger 	 * Based on:
133*c462238dSStephen Hemminger 	 *
134*c462238dSStephen Hemminger 	 *      (dm - d1) amin amax
135*c462238dSStephen Hemminger 	 * k1 = -------------------
136*c462238dSStephen Hemminger 	 *         amax - amin
137*c462238dSStephen Hemminger 	 *
138*c462238dSStephen Hemminger 	 *       (dm - d1) amin
139*c462238dSStephen Hemminger 	 * k2 = ----------------  - d1
140*c462238dSStephen Hemminger 	 *        amax - amin
141*c462238dSStephen Hemminger 	 *
142*c462238dSStephen Hemminger 	 *             k1
143*c462238dSStephen Hemminger 	 * alpha = ----------
144*c462238dSStephen Hemminger 	 *          k2 + da
145*c462238dSStephen Hemminger 	 */
146*c462238dSStephen Hemminger 
147*c462238dSStephen Hemminger 	dm -= d1;
148*c462238dSStephen Hemminger 	da -= d1;
149*c462238dSStephen Hemminger 
150*c462238dSStephen Hemminger 	a = (dm * ALPHA_MAX) / (dm - (da  * (ALPHA_MAX - ALPHA_MIN)) / ALPHA_MIN);
151*c462238dSStephen Hemminger 	ca->last_alpha = a;
152*c462238dSStephen Hemminger 	return a;
153*c462238dSStephen Hemminger }
154*c462238dSStephen Hemminger 
155*c462238dSStephen Hemminger /*
156*c462238dSStephen Hemminger  * Increase window in response to successful acknowledgment.
157*c462238dSStephen Hemminger  */
158*c462238dSStephen Hemminger static void tcp_illinois_cong_avoid(struct sock *sk, u32 ack, u32 rtt,
159*c462238dSStephen Hemminger 				    u32 in_flight, int flag)
160*c462238dSStephen Hemminger {
161*c462238dSStephen Hemminger 	struct tcp_sock *tp = tcp_sk(sk);
162*c462238dSStephen Hemminger 
163*c462238dSStephen Hemminger 	/* RFC2861 only increase cwnd if fully utilized */
164*c462238dSStephen Hemminger 	if (!tcp_is_cwnd_limited(sk, in_flight))
165*c462238dSStephen Hemminger 		return;
166*c462238dSStephen Hemminger 
167*c462238dSStephen Hemminger 	/* In slow start */
168*c462238dSStephen Hemminger 	if (tp->snd_cwnd <= tp->snd_ssthresh)
169*c462238dSStephen Hemminger 		tcp_slow_start(tp);
170*c462238dSStephen Hemminger 
171*c462238dSStephen Hemminger 	else {
172*c462238dSStephen Hemminger 		/* additive increase  cwnd += alpha / cwnd */
173*c462238dSStephen Hemminger 		if ((tp->snd_cwnd_cnt * alpha(sk)) >> ALPHA_SHIFT >= tp->snd_cwnd) {
174*c462238dSStephen Hemminger 			if (tp->snd_cwnd < tp->snd_cwnd_clamp)
175*c462238dSStephen Hemminger 				tp->snd_cwnd++;
176*c462238dSStephen Hemminger 			tp->snd_cwnd_cnt = 0;
177*c462238dSStephen Hemminger 		} else
178*c462238dSStephen Hemminger 			tp->snd_cwnd_cnt++;
179*c462238dSStephen Hemminger 	}
180*c462238dSStephen Hemminger }
181*c462238dSStephen Hemminger 
182*c462238dSStephen Hemminger /*
183*c462238dSStephen Hemminger  * Beta used for multiplicative decrease.
184*c462238dSStephen Hemminger  * For small window sizes returns same value as Reno (0.5)
185*c462238dSStephen Hemminger  *
186*c462238dSStephen Hemminger  * If delay is small (10% of max) then beta = 1/8
187*c462238dSStephen Hemminger  * If delay is up to 80% of max then beta = 1/2
188*c462238dSStephen Hemminger  * In between is a linear function
189*c462238dSStephen Hemminger  */
190*c462238dSStephen Hemminger static inline u32 beta(struct sock *sk)
191*c462238dSStephen Hemminger {
192*c462238dSStephen Hemminger 	struct tcp_sock *tp = tcp_sk(sk);
193*c462238dSStephen Hemminger 	struct tcp_illinois *ca = inet_csk_ca(sk);
194*c462238dSStephen Hemminger 	u32 dm = max_delay(ca);
195*c462238dSStephen Hemminger 	u32 da = avg_delay(ca);
196*c462238dSStephen Hemminger 	u32 d2, d3;
197*c462238dSStephen Hemminger 
198*c462238dSStephen Hemminger 	if (tp->snd_cwnd < win_thresh)
199*c462238dSStephen Hemminger 		return BETA_BASE;
200*c462238dSStephen Hemminger 
201*c462238dSStephen Hemminger 	d2 = dm / 10;
202*c462238dSStephen Hemminger 	if (da <= d2)
203*c462238dSStephen Hemminger 		return BETA_MIN;
204*c462238dSStephen Hemminger 	d3 = (8 * dm) / 10;
205*c462238dSStephen Hemminger 	if (da >= d3 || d3 <= d2)
206*c462238dSStephen Hemminger 		return BETA_MAX;
207*c462238dSStephen Hemminger 
208*c462238dSStephen Hemminger 	/*
209*c462238dSStephen Hemminger 	 * Based on:
210*c462238dSStephen Hemminger 	 *
211*c462238dSStephen Hemminger 	 *       bmin d3 - bmax d2
212*c462238dSStephen Hemminger 	 * k3 = -------------------
213*c462238dSStephen Hemminger 	 *         d3 - d2
214*c462238dSStephen Hemminger 	 *
215*c462238dSStephen Hemminger 	 *       bmax - bmin
216*c462238dSStephen Hemminger 	 * k4 = -------------
217*c462238dSStephen Hemminger 	 *         d3 - d2
218*c462238dSStephen Hemminger 	 *
219*c462238dSStephen Hemminger 	 * b = k3 + k4 da
220*c462238dSStephen Hemminger 	 */
221*c462238dSStephen Hemminger 	return (BETA_MIN * d3 - BETA_MAX * d2 + (BETA_MAX - BETA_MIN) * da)
222*c462238dSStephen Hemminger 		/ (d3 - d2);
223*c462238dSStephen Hemminger }
224*c462238dSStephen Hemminger 
225*c462238dSStephen Hemminger static u32 tcp_illinois_ssthresh(struct sock *sk)
226*c462238dSStephen Hemminger {
227*c462238dSStephen Hemminger 	struct tcp_sock *tp = tcp_sk(sk);
228*c462238dSStephen Hemminger 
229*c462238dSStephen Hemminger 	/* Multiplicative decrease */
230*c462238dSStephen Hemminger 	return max((tp->snd_cwnd * beta(sk)) >> BETA_SHIFT, 2U);
231*c462238dSStephen Hemminger }
232*c462238dSStephen Hemminger 
233*c462238dSStephen Hemminger /* Extract info for TCP socket info provided via netlink.
234*c462238dSStephen Hemminger  * We aren't really doing Vegas, but we can provide RTT info
235*c462238dSStephen Hemminger  */
236*c462238dSStephen Hemminger static void tcp_illinois_get_info(struct sock *sk, u32 ext,
237*c462238dSStephen Hemminger 			       struct sk_buff *skb)
238*c462238dSStephen Hemminger {
239*c462238dSStephen Hemminger 	const struct tcp_illinois *ca = inet_csk_ca(sk);
240*c462238dSStephen Hemminger 
241*c462238dSStephen Hemminger 	if (ext & (1 << (INET_DIAG_VEGASINFO - 1))) {
242*c462238dSStephen Hemminger 		struct tcpvegas_info info = {
243*c462238dSStephen Hemminger 			.tcpv_enabled = 1,
244*c462238dSStephen Hemminger 			.tcpv_rttcnt = ca->rtt_cnt,
245*c462238dSStephen Hemminger 			.tcpv_minrtt = ca->min_rtt,
246*c462238dSStephen Hemminger 		};
247*c462238dSStephen Hemminger 		u64 avg_rtt = ca->sum_rtt;
248*c462238dSStephen Hemminger 		do_div(avg_rtt, ca->rtt_cnt);
249*c462238dSStephen Hemminger 		info.tcpv_rtt = avg_rtt;
250*c462238dSStephen Hemminger 
251*c462238dSStephen Hemminger 		nla_put(skb, INET_DIAG_VEGASINFO, sizeof(info), &info);
252*c462238dSStephen Hemminger 	}
253*c462238dSStephen Hemminger }
254*c462238dSStephen Hemminger 
255*c462238dSStephen Hemminger static struct tcp_congestion_ops tcp_illinois = {
256*c462238dSStephen Hemminger 	.init		= tcp_illinois_init,
257*c462238dSStephen Hemminger 	.ssthresh	= tcp_illinois_ssthresh,
258*c462238dSStephen Hemminger 	.min_cwnd	= tcp_reno_min_cwnd,
259*c462238dSStephen Hemminger 	.cong_avoid	= tcp_illinois_cong_avoid,
260*c462238dSStephen Hemminger 	.rtt_sample	= tcp_illinois_rtt_calc,
261*c462238dSStephen Hemminger 	.get_info	= tcp_illinois_get_info,
262*c462238dSStephen Hemminger 
263*c462238dSStephen Hemminger 	.owner		= THIS_MODULE,
264*c462238dSStephen Hemminger 	.name		= "illinois",
265*c462238dSStephen Hemminger };
266*c462238dSStephen Hemminger 
267*c462238dSStephen Hemminger static int __init tcp_illinois_register(void)
268*c462238dSStephen Hemminger {
269*c462238dSStephen Hemminger 	BUILD_BUG_ON(sizeof(struct tcp_illinois) > ICSK_CA_PRIV_SIZE);
270*c462238dSStephen Hemminger 	return tcp_register_congestion_control(&tcp_illinois);
271*c462238dSStephen Hemminger }
272*c462238dSStephen Hemminger 
273*c462238dSStephen Hemminger static void __exit tcp_illinois_unregister(void)
274*c462238dSStephen Hemminger {
275*c462238dSStephen Hemminger 	tcp_unregister_congestion_control(&tcp_illinois);
276*c462238dSStephen Hemminger }
277*c462238dSStephen Hemminger 
278*c462238dSStephen Hemminger module_init(tcp_illinois_register);
279*c462238dSStephen Hemminger module_exit(tcp_illinois_unregister);
280*c462238dSStephen Hemminger 
281*c462238dSStephen Hemminger MODULE_AUTHOR("Stephen Hemminger, Shao Liu");
282*c462238dSStephen Hemminger MODULE_LICENSE("GPL");
283*c462238dSStephen Hemminger MODULE_DESCRIPTION("TCP Illinois");
284*c462238dSStephen Hemminger MODULE_VERSION("0.3");
285