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