├── .gitmodules
├── .gitignore
├── dcc.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── project.pbxproj
├── README.md
├── t
└── test.c
├── cc_int.h
├── cc.c
├── cc_cubic.h
├── cc_newreno.c
├── cc.h
└── cc_cubic.c
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "deps/picotest"]
2 | path = deps/picotest
3 | url = https://github.com/h2o/picotest.git
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.old
3 | *.csr
4 | */*.dSYM/
5 | .DS_Store
6 | CMakeCache.txt
7 | CMakeFiles/
8 | Makefile
9 | build/
10 | cmake_install.cmake
11 | xcuserdata
12 | *.xccheckout
13 | tmp/
14 |
--------------------------------------------------------------------------------
/dcc.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | daemons-cc
2 | ===
3 |
4 | daemons-cc is the FreeBSD's congestion control implementation extracted as a userspace library.
5 |
6 | At the moment, NewReno and Cubic have been ported.
7 | Since the changes to the modular API is kept minimum, it is anticipated that other algorithms can be ported fairly easily.
8 |
9 | Goals
10 | ---
11 | * create a congestion control implementation that can be used for QUIC
12 | * retain the modular API provided by mod_cc
13 |
14 | Notable Changes
15 | ---
16 | Stated below are the differences from the original version found in the FreeBSD kernel.
17 | * adjustments to minimize exposure (e.g., introduction of cc_int.h)
18 | * eliminate dependency on `tcpcb`; values are supplied as arguments to public function (e.g., `cc_ack_received`, `cc_cong_signal`)
19 | * `cc_get_cwnd` to obtain the congestion window size
20 | * new type `CC_FIRST_RTO` to signal first RTO. Exit of recovery mode is signalled using the last argument of `cc_ack_received`.
21 |
--------------------------------------------------------------------------------
/t/test.c:
--------------------------------------------------------------------------------
1 | /*-
2 | * Copyright (c) 2017,2018 Fastly
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions
7 | * are met:
8 | * 1. Redistributions of source code must retain the above copyright
9 | * notice, this list of conditions and the following disclaimer.
10 | * 2. Redistributions in binary form must reproduce the above copyright
11 | * notice, this list of conditions and the following disclaimer in the
12 | * documentation and/or other materials provided with the distribution.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 | * SUCH DAMAGE.
25 | */
26 |
27 | #include
28 | #include
29 | #include "picotest.h"
30 | #include "cc.h"
31 |
32 | struct cc_algo *cur_algo = NULL;
33 | static int cur_step;
34 |
35 | static void test_ss_core(void)
36 | {
37 | struct cc_var ccv;
38 | int i;
39 |
40 | cc_ticks = 0;
41 | cc_init(&ccv, cur_algo, 1280 * 8, 1280);
42 | cc_ticks += 10;
43 |
44 | for (i = 1; i <= 8; i += cur_step) {
45 | cc_ack_received(&ccv, CC_ACK, cc_get_cwnd(&ccv), cur_step, cur_step * cc_get_maxseg(&ccv), 10, 0);
46 | }
47 | ok(cc_get_cwnd(&ccv) == 16 * cc_get_maxseg(&ccv));
48 |
49 | cc_destroy(&ccv);
50 | }
51 |
52 | static void test_rto(void)
53 | {
54 | struct cc_var ccv;
55 | uint32_t bytes_in_pipe;
56 |
57 | cc_ticks = 0;
58 |
59 | cc_init(&ccv, cur_algo, 1280 * 4, 1280);
60 |
61 | /* send four, get three acked (but not the second one) */
62 | bytes_in_pipe = cc_get_cwnd(&ccv);
63 | cc_ticks += 10;
64 | cc_ack_received(&ccv, CC_ACK, bytes_in_pipe, 3, 3 * cc_get_maxseg(&ccv), 10, 0);
65 | ok(cc_get_cwnd(&ccv) == 7 * cc_get_maxseg(&ccv));
66 |
67 | /* retransmit */
68 | cc_ticks += 10;
69 | cc_cong_signal(&ccv, CC_RTO, cc_get_cwnd(&ccv));
70 | ok(ccv.ccvc.ccv.snd_ssthresh == 3 * cc_get_maxseg(&ccv));
71 | ok(cc_get_cwnd(&ccv) == cc_get_maxseg(&ccv));
72 |
73 | /* get acks for all */
74 | cc_ticks += 10;
75 | cc_ack_received(&ccv, CC_ACK, bytes_in_pipe, 1, cc_get_maxseg(&ccv), 10, 1);
76 | ok(cc_get_cwnd(&ccv) <= 2 * cc_get_maxseg(&ccv));
77 |
78 | cc_destroy(&ccv);
79 | }
80 |
81 | static void test_dupack(void)
82 | {
83 | struct cc_var ccv;
84 |
85 | cc_ticks = 0;
86 |
87 | cc_init(&ccv, cur_algo, 1280 * 4, 1280);
88 |
89 | /* send 4 packets, got 3 dupacks */
90 | cc_ticks += 10;
91 | cc_ack_received(&ccv, CC_DUPACK, 1280 * 4, 0, 0, 10, 0);
92 | cc_ticks += 10;
93 | cc_ack_received(&ccv, CC_DUPACK, 1280 * 4, 0, 0, 10, 0);
94 | cc_ticks += 10;
95 | cc_cong_signal(&ccv, CC_NDUPACK, 1280 * 4);
96 | cc_ack_received(&ccv, CC_DUPACK, 1280 * 4, 0, 0, 10, 0);
97 |
98 | ok(CC_IN_RECOVERY(ccv.ccvc.ccv.t_flags));
99 | ok(cc_get_cwnd(&ccv) <= 1280 * 4);
100 | ok(ccv.ccvc.ccv.snd_ssthresh < 1280 * 4);
101 |
102 | /* got ack for 4 packets */
103 | cc_ticks += 10;
104 | cc_ack_received(&ccv, CC_ACK, 1280 * 4, 4, 1280 * 4, 10, 1);
105 | ok(cc_get_cwnd(&ccv) <= 1280 * 4);
106 |
107 | cc_destroy(&ccv);
108 | }
109 |
110 | static void test_algo(void)
111 | {
112 | cur_step = 1;
113 | subtest("ss;step=1", test_ss_core);
114 | cur_step = 2;
115 | subtest("ss;step=2", test_ss_core);
116 | cur_step = 8;
117 | subtest("ss;step=8", test_ss_core);
118 |
119 | subtest("rto", test_rto);
120 |
121 | subtest("dupack", test_dupack);
122 | }
123 |
124 | int main(int argc, char **argv)
125 | {
126 | extern struct cc_algo cubic_cc_algo;
127 |
128 | cur_algo = &newreno_cc_algo;
129 | subtest("newreno", test_algo);
130 | cur_algo = &cubic_cc_algo;
131 | subtest("cubic", test_algo);
132 |
133 | return done_testing();
134 | }
135 |
--------------------------------------------------------------------------------
/cc_int.h:
--------------------------------------------------------------------------------
1 | /*-
2 | * Copyright (c) 2007-2008
3 | * Swinburne University of Technology, Melbourne, Australia.
4 | * Copyright (c) 2009-2010 Lawrence Stewart
5 | * Copyright (c) 2010 The FreeBSD Foundation
6 | * All rights reserved.
7 | *
8 | * This software was developed at the Centre for Advanced Internet
9 | * Architectures, Swinburne University of Technology, by Lawrence Stewart and
10 | * James Healy, made possible in part by a grant from the Cisco University
11 | * Research Program Fund at Community Foundation Silicon Valley.
12 | *
13 | * Portions of this software were developed at the Centre for Advanced
14 | * Internet Architectures, Swinburne University of Technology, Melbourne,
15 | * Australia by David Hayes under sponsorship from the FreeBSD Foundation.
16 | *
17 | * Redistribution and use in source and binary forms, with or without
18 | * modification, are permitted provided that the following conditions
19 | * are met:
20 | * 1. Redistributions of source code must retain the above copyright
21 | * notice, this list of conditions and the following disclaimer.
22 | * 2. Redistributions in binary form must reproduce the above copyright
23 | * notice, this list of conditions and the following disclaimer in the
24 | * documentation and/or other materials provided with the distribution.
25 | *
26 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
27 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
30 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 | * SUCH DAMAGE.
37 | *
38 | * $FreeBSD$
39 | */
40 |
41 | /*
42 | * This software was first released in 2007 by James Healy and Lawrence Stewart
43 | * whilst working on the NewTCP research project at Swinburne University of
44 | * Technology's Centre for Advanced Internet Architectures, Melbourne,
45 | * Australia, which was made possible in part by a grant from the Cisco
46 | * University Research Program Fund at Community Foundation Silicon Valley.
47 | * More details are available at:
48 | * http://caia.swin.edu.au/urp/newtcp/
49 | */
50 |
51 | #ifndef cc_int_h
52 | #define cc_int_h
53 |
54 | #include
55 | #include
56 | #include
57 | #include
58 | #include
59 | #include
60 | #include
61 | #include
62 | #include "cc.h"
63 |
64 | #define _KERNEL
65 |
66 | #ifndef min
67 | #define min(x, y) ((x) <= (y) ? (x) : (y))
68 | #define max(x, y) ((x) >= (y) ? (x) : (y))
69 | #endif
70 |
71 | #define VNET_DECLARE(...)
72 | #define VNET(v) cc_##v
73 | #define SYSCTL_DECL(...)
74 | #define SYSCTL_PROC(...)
75 | #define TCP_CA_NAME_MAX 16
76 |
77 | #define IN_RECOVERY CC_IN_RECOVERY
78 | #define ENTER_RECOVERY CC_ENTER_RECOVERY
79 | #define EXIT_RECOVERY CC_EXIT_RECOVERY
80 |
81 | #define IN_FASTRECOVERY CC_IN_FASTRECOVERY
82 | #define ENTER_FASTRECOVERY CC_ENTER_FASTRECOVERY
83 | #define EXIT_FASTRECOVERY CC_EXIT_FASTRECOVERY
84 |
85 | #define IN_CONGRECOVERY CC_IN_CONGRECOVERY
86 | #define ENTER_CONGRECOVERY CC_ENTER_CONGRECOVERY
87 | #define EXIT_CONGRECOVERY CC_EXIT_CONGRECOVERY
88 |
89 | #define MALLOC_DEFINE(sym, lbl, desc) const char *sym = lbl
90 | #define malloc(sz, lbl, flags) cc_malloc((sz), (lbl))
91 | #define free(p, lbl) cc_free((p), (lbl))
92 |
93 | #define V_tcp_do_rfc3390 VNET(tcp_do_rfc3390)
94 | #define V_tcp_do_rfc3465 VNET(tcp_do_rfc3465)
95 | #define V_tcp_do_rfc6675_pipe 0
96 | #define V_tcp_abc_l_var VNET(tcp_abc_l_var)
97 |
98 | #define TCP_MAXWIN 65535
99 | #define TCPTV_SRTTBASE 0 /* base roundtrip time;
100 | if 0, no idea yet */
101 |
102 | /*
103 | * The smoothed round-trip time and estimated variance
104 | * are stored as fixed point numbers scaled by the values below.
105 | * For convenience, these scales are also used in smoothing the average
106 | * (smoothed = (1/scale)sample + ((scale-1)/scale)smoothed).
107 | * With these scales, srtt has 3 bits to the right of the binary point,
108 | * and thus an "ALPHA" of 0.875. rttvar has 2 bits to the right of the
109 | * binary point, and is smoothed with an ALPHA of 0.75.
110 | */
111 | #define TCP_RTT_SCALE 32 /* multiplier for srtt; 3 bits frac. */
112 | #define TCP_RTT_SHIFT 5 /* shift for srtt; 3 bits frac. */
113 | #define TCP_RTTVAR_SCALE 16 /* multiplier for rttvar; 2 bits */
114 | #define TCP_RTTVAR_SHIFT 4 /* shift for rttvar; 2 bits */
115 |
116 | #define hz cc_hz
117 | #define ticks cc_ticks
118 |
119 | #define CCV(ccv, what) (ccv)->ccvc.ccv.what
120 | #define DECLARE_CC_MODULE(...)
121 | #define KASSERT(cond, ...) assert(cond) /* FIXME */
122 |
123 | #include "cc.h"
124 |
125 | #endif
126 |
--------------------------------------------------------------------------------
/cc.c:
--------------------------------------------------------------------------------
1 | /*-
2 | * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994, 1995
3 | * The Regents of the University of California. All rights reserved.
4 | * Copyright (c) 2007-2008,2010
5 | * Swinburne University of Technology, Melbourne, Australia.
6 | * Copyright (c) 2009-2010 Lawrence Stewart
7 | * Copyright (c) 2010 The FreeBSD Foundation
8 | * Copyright (c) 2010-2011 Juniper Networks, Inc.
9 | * Copyright (c) 2017,2018 Fastly
10 | * All rights reserved.
11 | *
12 | * Portions of this software were developed at the Centre for Advanced Internet
13 | * Architectures, Swinburne University of Technology, by Lawrence Stewart,
14 | * James Healy and David Hayes, made possible in part by a grant from the Cisco
15 | * University Research Program Fund at Community Foundation Silicon Valley.
16 | *
17 | * Portions of this software were developed at the Centre for Advanced
18 | * Internet Architectures, Swinburne University of Technology, Melbourne,
19 | * Australia by David Hayes under sponsorship from the FreeBSD Foundation.
20 | *
21 | * Portions of this software were developed by Robert N. M. Watson under
22 | * contract to Juniper Networks, Inc.
23 | *
24 | * Redistribution and use in source and binary forms, with or without
25 | * modification, are permitted provided that the following conditions
26 | * are met:
27 | * 1. Redistributions of source code must retain the above copyright
28 | * notice, this list of conditions and the following disclaimer.
29 | * 2. Redistributions in binary form must reproduce the above copyright
30 | * notice, this list of conditions and the following disclaimer in the
31 | * documentation and/or other materials provided with the distribution.
32 | * 3. Neither the name of the University nor the names of its contributors
33 | * may be used to endorse or promote products derived from this software
34 | * without specific prior written permission.
35 | *
36 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
37 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
39 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
40 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
41 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
42 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
44 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
45 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46 | * SUCH DAMAGE.
47 | *
48 | * @(#)tcp_input.c 8.12 (Berkeley) 5/24/95
49 | */
50 |
51 | #include
52 | #include
53 | #include
54 | #include "cc.h"
55 |
56 | /* Enable RFC 3390 (Increasing TCP's Initial Congestion Window) */
57 | int cc_tcp_do_rfc3390 = 1;
58 |
59 | /* Enable RFC 3465 (Appropriate Byte Counting) */
60 | int cc_tcp_do_rfc3465 = 1;
61 |
62 | /* Cap the max cwnd increment during slow-start to this number of segments */
63 | int cc_tcp_abc_l_var = 2;
64 |
65 | int cc_hz = 100;
66 | volatile int cc_ticks;
67 |
68 | void *cc_malloc(size_t sz, const char *lbl)
69 | {
70 | return malloc(sz);
71 | }
72 |
73 | void cc_free(void *p, const char *lbl)
74 | {
75 | free(p);
76 | }
77 |
78 | #include "cc_int.h"
79 |
80 | int cc_init(struct cc_var *ccv, struct cc_algo *algo, uint32_t cwnd, unsigned maxseg)
81 | {
82 | int ret = 0;
83 |
84 | memset(ccv, 0, sizeof(*ccv));
85 | CCV(ccv, cc_algo) = algo;
86 |
87 | if (CCV(ccv, cc_algo)->cb_init != NULL && (ret = CCV(ccv, cc_algo)->cb_init(ccv)) != 0)
88 | return ret;
89 |
90 | CCV(ccv, snd_cwnd) = cwnd;
91 | CCV(ccv, t_maxseg) = maxseg;
92 | CCV(ccv, snd_ssthresh) = 65535 << 14;
93 |
94 | if (CCV(ccv, cc_algo)->conn_init != NULL)
95 | CCV(ccv, cc_algo)->conn_init(ccv);
96 |
97 | return ret;
98 | }
99 |
100 | void cc_destroy(struct cc_var *ccv)
101 | {
102 | if (CCV(ccv, cc_algo)->cb_destroy != NULL)
103 | CCV(ccv, cc_algo)->cb_destroy(ccv);
104 | }
105 |
106 | void cc_ack_received(struct cc_var *ccv, uint16_t type, uint32_t bytes_in_pipe, uint16_t segs_acked, uint32_t bytes_acked, int srtt,
107 | int exit_recovery)
108 | {
109 | CCV(ccv, snd_pipe) = bytes_in_pipe;
110 |
111 | if (exit_recovery) {
112 | CCV(ccv, t_flags) &= ~CC_TF_RETRANSMIT;
113 | if (CC_ALGO(ccv)->post_recovery != NULL)
114 | CC_ALGO(ccv)->post_recovery(ccv);
115 | CCV(ccv, t_bytes_acked) = 0;
116 | }
117 |
118 | ccv->nsegs = segs_acked;
119 | ccv->bytes_this_ack = bytes_acked;
120 | if (CCV(ccv, snd_cwnd) <= CCV(ccv, snd_pipe))
121 | ccv->flags |= CCF_CWND_LIMITED;
122 | else
123 | ccv->flags &= ~CCF_CWND_LIMITED;
124 |
125 | if (type == CC_ACK) {
126 | if (CCV(ccv, snd_cwnd) > CCV(ccv, snd_ssthresh)) {
127 | CCV(ccv, t_bytes_acked) += min(bytes_acked, segs_acked * V_tcp_abc_l_var * CCV(ccv, t_maxseg));
128 | if (CCV(ccv, t_bytes_acked) >= CCV(ccv, snd_cwnd)) {
129 | CCV(ccv, t_bytes_acked) -= CCV(ccv, snd_cwnd);
130 | ccv->flags |= CCF_ABC_SENTAWND;
131 | }
132 | } else {
133 | ccv->flags &= ~CCF_ABC_SENTAWND;
134 | CCV(ccv, t_bytes_acked) = 0;
135 | }
136 | CCV(ccv, t_srtt) = srtt;
137 | CCV(ccv, t_rttupdated) = CCV(ccv, t_rttupdated) + 1;
138 | }
139 |
140 | if (CC_ALGO(ccv)->ack_received != NULL) {
141 | /* XXXLAS: Find a way to live without this */
142 | CC_ALGO(ccv)->ack_received(ccv, type);
143 | }
144 |
145 | if (exit_recovery)
146 | EXIT_RECOVERY(CCV(ccv, t_flags));
147 | }
148 |
149 | void cc_cong_signal(struct cc_var *ccv, uint32_t type, uint32_t bytes_in_pipe)
150 | {
151 | unsigned maxseg;
152 |
153 | CCV(ccv, snd_pipe) = bytes_in_pipe;
154 |
155 | switch(type) {
156 | case CC_NDUPACK:
157 | case CC_ECN:
158 | break;
159 | case CC_FIRST_RTO:
160 | CCV(ccv, snd_cwnd_prev) = CCV(ccv, snd_cwnd);
161 | CCV(ccv, snd_ssthresh_prev) = CCV(ccv, snd_ssthresh);
162 | if (IN_FASTRECOVERY(CCV(ccv, t_flags))) {
163 | CCV(ccv, t_flags) |= CC_TF_WASFRECOVERY;
164 | } else {
165 | CCV(ccv, t_flags) &= ~CC_TF_WASFRECOVERY;
166 | }
167 | if (IN_CONGRECOVERY(CCV(ccv, t_flags))) {
168 | CCV(ccv, t_flags) |= CC_TF_WASCRECOVERY;
169 | } else {
170 | CCV(ccv, t_flags) &= ~CC_TF_WASCRECOVERY;
171 | }
172 | CCV(ccv, t_flags) |= CC_TF_PREVVALID;
173 | CCV(ccv, t_badrxtwin) = ticks + (CCV(ccv, t_srtt) >> (TCP_RTT_SHIFT + 1));
174 | /* fallthru */
175 | case CC_RTO:
176 | maxseg = CCV(ccv, t_maxseg);
177 | CCV(ccv, t_bytes_acked) = 0;
178 | EXIT_RECOVERY(CCV(ccv, t_flags));
179 | CCV(ccv, snd_ssthresh) = max(2, min(bytes_in_pipe, CCV(ccv, snd_cwnd)) / 2 / maxseg) * maxseg;
180 | CCV(ccv, snd_cwnd) = maxseg; /* KAZUHO FreeBSD does this; but is it correct? */
181 | CCV(ccv, t_flags) |= CC_TF_RETRANSMIT;
182 | break;
183 | case CC_RTO_ERR:
184 | CCV(ccv, snd_cwnd) = CCV(ccv, snd_cwnd_prev);
185 | CCV(ccv, snd_ssthresh) = CCV(ccv, snd_ssthresh_prev);
186 | if (CCV(ccv, t_flags) & CC_TF_WASFRECOVERY)
187 | ENTER_FASTRECOVERY(CCV(ccv, t_flags));
188 | if (CCV(ccv, t_flags) & CC_TF_WASCRECOVERY)
189 | ENTER_CONGRECOVERY(CCV(ccv, t_flags));
190 | CCV(ccv, t_flags) &= ~CC_TF_PREVVALID;
191 | CCV(ccv, t_badrxtwin) = 0;
192 | break;
193 | default:
194 | assert(!"FIXME");
195 | break;
196 | }
197 |
198 | if (CC_ALGO(ccv)->cong_signal != NULL) {
199 | CC_ALGO(ccv)->cong_signal(ccv, type);
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/cc_cubic.h:
--------------------------------------------------------------------------------
1 | /*-
2 | * Copyright (c) 2008-2010 Lawrence Stewart
3 | * Copyright (c) 2010 The FreeBSD Foundation
4 | * All rights reserved.
5 | *
6 | * This software was developed by Lawrence Stewart while studying at the Centre
7 | * for Advanced Internet Architectures, Swinburne University of Technology, made
8 | * possible in part by a grant from the Cisco University Research Program Fund
9 | * at Community Foundation Silicon Valley.
10 | *
11 | * Portions of this software were developed at the Centre for Advanced
12 | * Internet Architectures, Swinburne University of Technology, Melbourne,
13 | * Australia by David Hayes under sponsorship from the FreeBSD Foundation.
14 | *
15 | * Redistribution and use in source and binary forms, with or without
16 | * modification, are permitted provided that the following conditions
17 | * are met:
18 | * 1. Redistributions of source code must retain the above copyright
19 | * notice, this list of conditions and the following disclaimer.
20 | * 2. Redistributions in binary form must reproduce the above copyright
21 | * notice, this list of conditions and the following disclaimer in the
22 | * documentation and/or other materials provided with the distribution.
23 | *
24 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 | * SUCH DAMAGE.
35 | *
36 | * $FreeBSD$
37 | */
38 |
39 | #ifndef _NETINET_CC_CUBIC_H_
40 | #define _NETINET_CC_CUBIC_H_
41 |
42 | /* Number of bits of precision for fixed point math calcs. */
43 | #define CUBIC_SHIFT 8
44 |
45 | #define CUBIC_SHIFT_4 32
46 |
47 | /* 0.5 << CUBIC_SHIFT. */
48 | #define RENO_BETA 128
49 |
50 | /* ~0.8 << CUBIC_SHIFT. */
51 | #define CUBIC_BETA 204
52 |
53 | /* ~0.2 << CUBIC_SHIFT. */
54 | #define ONE_SUB_CUBIC_BETA 51
55 |
56 | /* 3 * ONE_SUB_CUBIC_BETA. */
57 | #define THREE_X_PT2 153
58 |
59 | /* (2 << CUBIC_SHIFT) - ONE_SUB_CUBIC_BETA. */
60 | #define TWO_SUB_PT2 461
61 |
62 | /* ~0.4 << CUBIC_SHIFT. */
63 | #define CUBIC_C_FACTOR 102
64 |
65 | /* CUBIC fast convergence factor: ~0.9 << CUBIC_SHIFT. */
66 | #define CUBIC_FC_FACTOR 230
67 |
68 | /* Don't trust s_rtt until this many rtt samples have been taken. */
69 | #define CUBIC_MIN_RTT_SAMPLES 8
70 |
71 | /* Userland only bits. */
72 | #ifndef _KERNEL
73 |
74 | extern int hz;
75 |
76 | /*
77 | * Implementation based on the formulae found in the CUBIC Internet Draft
78 | * "draft-rhee-tcpm-cubic-02".
79 | *
80 | * Note BETA used in cc_cubic is equal to (1-beta) in the I-D
81 | */
82 |
83 | static __inline float
84 | theoretical_cubic_k(double wmax_pkts)
85 | {
86 | double C;
87 |
88 | C = 0.4;
89 |
90 | return (pow((wmax_pkts * 0.2) / C, (1.0 / 3.0)) * pow(2, CUBIC_SHIFT));
91 | }
92 |
93 | static __inline unsigned long
94 | theoretical_cubic_cwnd(int ticks_since_cong, unsigned long wmax, uint32_t smss)
95 | {
96 | double C, wmax_pkts;
97 |
98 | C = 0.4;
99 | wmax_pkts = wmax / (double)smss;
100 |
101 | return (smss * (wmax_pkts +
102 | (C * pow(ticks_since_cong / (double)hz -
103 | theoretical_cubic_k(wmax_pkts) / pow(2, CUBIC_SHIFT), 3.0))));
104 | }
105 |
106 | static __inline unsigned long
107 | theoretical_reno_cwnd(int ticks_since_cong, int rtt_ticks, unsigned long wmax,
108 | uint32_t smss)
109 | {
110 |
111 | return ((wmax * 0.5) + ((ticks_since_cong / (float)rtt_ticks) * smss));
112 | }
113 |
114 | static __inline unsigned long
115 | theoretical_tf_cwnd(int ticks_since_cong, int rtt_ticks, unsigned long wmax,
116 | uint32_t smss)
117 | {
118 |
119 | return ((wmax * 0.8) + ((3 * 0.2) / (2 - 0.2) *
120 | (ticks_since_cong / (float)rtt_ticks) * smss));
121 | }
122 |
123 | #endif /* !_KERNEL */
124 |
125 | /*
126 | * Compute the CUBIC K value used in the cwnd calculation, using an
127 | * implementation of eqn 2 in the I-D. The method used
128 | * here is adapted from Apple Computer Technical Report #KT-32.
129 | */
130 | static __inline int64_t
131 | cubic_k(unsigned long wmax_pkts)
132 | {
133 | int64_t s, K;
134 | uint16_t p;
135 |
136 | K = s = 0;
137 | p = 0;
138 |
139 | /* (wmax * beta)/C with CUBIC_SHIFT worth of precision. */
140 | s = ((wmax_pkts * ONE_SUB_CUBIC_BETA) << CUBIC_SHIFT) / CUBIC_C_FACTOR;
141 |
142 | /* Rebase s to be between 1 and 1/8 with a shift of CUBIC_SHIFT. */
143 | while (s >= 256) {
144 | s >>= 3;
145 | p++;
146 | }
147 |
148 | /*
149 | * Some magic constants taken from the Apple TR with appropriate
150 | * shifts: 275 == 1.072302 << CUBIC_SHIFT, 98 == 0.3812513 <<
151 | * CUBIC_SHIFT, 120 == 0.46946116 << CUBIC_SHIFT.
152 | */
153 | K = (((s * 275) >> CUBIC_SHIFT) + 98) -
154 | (((s * s * 120) >> CUBIC_SHIFT) >> CUBIC_SHIFT);
155 |
156 | /* Multiply by 2^p to undo the rebasing of s from above. */
157 | return (K <<= p);
158 | }
159 |
160 | /*
161 | * Compute the new cwnd value using an implementation of eqn 1 from the I-D.
162 | * Thanks to Kip Macy for help debugging this function.
163 | *
164 | * XXXLAS: Characterise bounds for overflow.
165 | */
166 | static __inline unsigned long
167 | cubic_cwnd(int ticks_since_cong, unsigned long wmax, uint32_t smss, int64_t K)
168 | {
169 | int64_t cwnd;
170 |
171 | /* K is in fixed point form with CUBIC_SHIFT worth of precision. */
172 |
173 | /* t - K, with CUBIC_SHIFT worth of precision. */
174 | cwnd = ((int64_t)(ticks_since_cong << CUBIC_SHIFT) - (K * hz)) / hz;
175 |
176 | /* (t - K)^3, with CUBIC_SHIFT^3 worth of precision. */
177 | cwnd *= (cwnd * cwnd);
178 |
179 | /*
180 | * C(t - K)^3 + wmax
181 | * The down shift by CUBIC_SHIFT_4 is because cwnd has 4 lots of
182 | * CUBIC_SHIFT included in the value. 3 from the cubing of cwnd above,
183 | * and an extra from multiplying through by CUBIC_C_FACTOR.
184 | */
185 | cwnd = ((cwnd * CUBIC_C_FACTOR * smss) >> CUBIC_SHIFT_4) + wmax;
186 |
187 | return ((unsigned long)cwnd);
188 | }
189 |
190 | /*
191 | * Compute an approximation of the NewReno cwnd some number of ticks after a
192 | * congestion event. RTT should be the average RTT estimate for the path
193 | * measured over the previous congestion epoch and wmax is the value of cwnd at
194 | * the last congestion event. The "TCP friendly" concept in the CUBIC I-D is
195 | * rather tricky to understand and it turns out this function is not required.
196 | * It is left here for reference.
197 | */
198 | static __inline unsigned long
199 | reno_cwnd(int ticks_since_cong, int rtt_ticks, unsigned long wmax,
200 | uint32_t smss)
201 | {
202 |
203 | /*
204 | * For NewReno, beta = 0.5, therefore: W_tcp(t) = wmax*0.5 + t/RTT
205 | * W_tcp(t) deals with cwnd/wmax in pkts, so because our cwnd is in
206 | * bytes, we have to multiply by smss.
207 | */
208 | return (((wmax * RENO_BETA) + (((ticks_since_cong * smss)
209 | << CUBIC_SHIFT) / rtt_ticks)) >> CUBIC_SHIFT);
210 | }
211 |
212 | /*
213 | * Compute an approximation of the "TCP friendly" cwnd some number of ticks
214 | * after a congestion event that is designed to yield the same average cwnd as
215 | * NewReno while using CUBIC's beta of 0.8. RTT should be the average RTT
216 | * estimate for the path measured over the previous congestion epoch and wmax is
217 | * the value of cwnd at the last congestion event.
218 | */
219 | static __inline unsigned long
220 | tf_cwnd(int ticks_since_cong, int rtt_ticks, unsigned long wmax,
221 | uint32_t smss)
222 | {
223 |
224 | /* Equation 4 of I-D. */
225 | return (((wmax * CUBIC_BETA) + (((THREE_X_PT2 * ticks_since_cong *
226 | smss) << CUBIC_SHIFT) / TWO_SUB_PT2 / rtt_ticks)) >> CUBIC_SHIFT);
227 | }
228 |
229 | #endif /* _NETINET_CC_CUBIC_H_ */
230 |
--------------------------------------------------------------------------------
/cc_newreno.c:
--------------------------------------------------------------------------------
1 | /*-
2 | * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994, 1995
3 | * The Regents of the University of California.
4 | * Copyright (c) 2007-2008,2010
5 | * Swinburne University of Technology, Melbourne, Australia.
6 | * Copyright (c) 2009-2010 Lawrence Stewart
7 | * Copyright (c) 2010 The FreeBSD Foundation
8 | * All rights reserved.
9 | *
10 | * This software was developed at the Centre for Advanced Internet
11 | * Architectures, Swinburne University of Technology, by Lawrence Stewart, James
12 | * Healy and David Hayes, made possible in part by a grant from the Cisco
13 | * University Research Program Fund at Community Foundation Silicon Valley.
14 | *
15 | * Portions of this software were developed at the Centre for Advanced
16 | * Internet Architectures, Swinburne University of Technology, Melbourne,
17 | * Australia by David Hayes under sponsorship from the FreeBSD Foundation.
18 | *
19 | * Redistribution and use in source and binary forms, with or without
20 | * modification, are permitted provided that the following conditions
21 | * are met:
22 | * 1. Redistributions of source code must retain the above copyright
23 | * notice, this list of conditions and the following disclaimer.
24 | * 2. Redistributions in binary form must reproduce the above copyright
25 | * notice, this list of conditions and the following disclaimer in the
26 | * documentation and/or other materials provided with the distribution.
27 | *
28 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
29 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
32 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 | * SUCH DAMAGE.
39 | */
40 |
41 | /*
42 | * This software was first released in 2007 by James Healy and Lawrence Stewart
43 | * whilst working on the NewTCP research project at Swinburne University of
44 | * Technology's Centre for Advanced Internet Architectures, Melbourne,
45 | * Australia, which was made possible in part by a grant from the Cisco
46 | * University Research Program Fund at Community Foundation Silicon Valley.
47 | * More details are available at:
48 | * http://caia.swin.edu.au/urp/newtcp/
49 | */
50 |
51 | #include "cc_int.h"
52 |
53 | static void newreno_ack_received(struct cc_var *ccv, uint16_t type);
54 | static void newreno_after_idle(struct cc_var *ccv);
55 | static void newreno_cong_signal(struct cc_var *ccv, uint32_t type);
56 | static void newreno_post_recovery(struct cc_var *ccv);
57 |
58 | struct cc_algo newreno_cc_algo = {
59 | .name = "newreno",
60 | .ack_received = newreno_ack_received,
61 | .after_idle = newreno_after_idle,
62 | .cong_signal = newreno_cong_signal,
63 | .post_recovery = newreno_post_recovery,
64 | };
65 |
66 | static void
67 | newreno_ack_received(struct cc_var *ccv, uint16_t type)
68 | {
69 | if (type == CC_ACK && !IN_RECOVERY(CCV(ccv, t_flags)) &&
70 | (ccv->flags & CCF_CWND_LIMITED)) {
71 | unsigned cw = CCV(ccv, snd_cwnd);
72 | unsigned incr = CCV(ccv, t_maxseg);
73 |
74 | /*
75 | * Regular in-order ACK, open the congestion window.
76 | * Method depends on which congestion control state we're
77 | * in (slow start or cong avoid) and if ABC (RFC 3465) is
78 | * enabled.
79 | *
80 | * slow start: cwnd <= ssthresh
81 | * cong avoid: cwnd > ssthresh
82 | *
83 | * slow start and ABC (RFC 3465):
84 | * Grow cwnd exponentially by the amount of data
85 | * ACKed capping the max increment per ACK to
86 | * (abc_l_var * maxseg) bytes.
87 | *
88 | * slow start without ABC (RFC 5681):
89 | * Grow cwnd exponentially by maxseg per ACK.
90 | *
91 | * cong avoid and ABC (RFC 3465):
92 | * Grow cwnd linearly by maxseg per RTT for each
93 | * cwnd worth of ACKed data.
94 | *
95 | * cong avoid without ABC (RFC 5681):
96 | * Grow cwnd linearly by approximately maxseg per RTT using
97 | * maxseg^2 / cwnd per ACK as the increment.
98 | * If cwnd > maxseg^2, fix the cwnd increment at 1 byte to
99 | * avoid capping cwnd.
100 | */
101 | if (cw > CCV(ccv, snd_ssthresh)) {
102 | if (V_tcp_do_rfc3465) {
103 | if (ccv->flags & CCF_ABC_SENTAWND)
104 | ccv->flags &= ~CCF_ABC_SENTAWND;
105 | else
106 | incr = 0;
107 | } else
108 | incr = max((incr * incr / cw), 1);
109 | } else if (V_tcp_do_rfc3465) {
110 | /*
111 | * In slow-start with ABC enabled and no RTO in sight?
112 | * (Must not use abc_l_var > 1 if slow starting after
113 | * an RTO. On RTO, snd_nxt = snd_una, so the
114 | * snd_nxt == snd_max check is sufficient to
115 | * handle this).
116 | *
117 | * XXXLAS: Find a way to signal SS after RTO that
118 | * doesn't rely on tcpcb vars.
119 | */
120 | if (!(CCV(ccv, t_flags) & CC_TF_RETRANSMIT))
121 | incr = min(ccv->bytes_this_ack,
122 | ccv->nsegs * V_tcp_abc_l_var *
123 | CCV(ccv, t_maxseg));
124 | else
125 | incr = min(ccv->bytes_this_ack, CCV(ccv, t_maxseg));
126 | }
127 | /* ABC is on by default, so incr equals 0 frequently. */
128 | if (incr > 0)
129 | CCV(ccv, snd_cwnd) = min(cw + incr,
130 | TCP_MAXWIN << CCV(ccv, snd_scale));
131 | }
132 | }
133 |
134 | static void
135 | newreno_after_idle(struct cc_var *ccv)
136 | {
137 | int rw;
138 |
139 | /*
140 | * If we've been idle for more than one retransmit timeout the old
141 | * congestion window is no longer current and we have to reduce it to
142 | * the restart window before we can transmit again.
143 | *
144 | * The restart window is the initial window or the last CWND, whichever
145 | * is smaller.
146 | *
147 | * This is done to prevent us from flooding the path with a full CWND at
148 | * wirespeed, overloading router and switch buffers along the way.
149 | *
150 | * See RFC5681 Section 4.1. "Restarting Idle Connections".
151 | */
152 | if (V_tcp_do_rfc3390)
153 | rw = min(4 * CCV(ccv, t_maxseg),
154 | max(2 * CCV(ccv, t_maxseg), 4380));
155 | else
156 | rw = CCV(ccv, t_maxseg) * 2;
157 |
158 | CCV(ccv, snd_cwnd) = min(rw, CCV(ccv, snd_cwnd));
159 | }
160 |
161 | /*
162 | * Perform any necessary tasks before we enter congestion recovery.
163 | */
164 | static void
165 | newreno_cong_signal(struct cc_var *ccv, uint32_t type)
166 | {
167 | unsigned win;
168 |
169 | /* Catch algos which mistakenly leak private signal types. */
170 | KASSERT((type & CC_SIGPRIVMASK) == 0,
171 | ("%s: congestion signal type 0x%08x is private\n", __func__, type));
172 |
173 | win = max(CCV(ccv, snd_cwnd) / 2 / CCV(ccv, t_maxseg), 2) *
174 | CCV(ccv, t_maxseg);
175 |
176 | switch (type) {
177 | case CC_NDUPACK:
178 | if (!IN_FASTRECOVERY(CCV(ccv, t_flags))) {
179 | if (!IN_CONGRECOVERY(CCV(ccv, t_flags)))
180 | CCV(ccv, snd_ssthresh) = win;
181 | ENTER_RECOVERY(CCV(ccv, t_flags));
182 | }
183 | break;
184 | case CC_ECN:
185 | if (!IN_CONGRECOVERY(CCV(ccv, t_flags))) {
186 | CCV(ccv, snd_ssthresh) = win;
187 | CCV(ccv, snd_cwnd) = win;
188 | ENTER_CONGRECOVERY(CCV(ccv, t_flags));
189 | }
190 | break;
191 | }
192 | }
193 |
194 | /*
195 | * Perform any necessary tasks before we exit congestion recovery.
196 | */
197 | static void
198 | newreno_post_recovery(struct cc_var *ccv)
199 | {
200 | int pipe;
201 | pipe = 0;
202 |
203 | if (IN_FASTRECOVERY(CCV(ccv, t_flags))) {
204 | /*
205 | * Fast recovery will conclude after returning from this
206 | * function. Window inflation should have left us with
207 | * approximately snd_ssthresh outstanding data. But in case we
208 | * would be inclined to send a burst, better to do it via the
209 | * slow start mechanism.
210 | *
211 | * XXXLAS: Find a way to do this without needing curack
212 | */
213 | pipe = CCV(ccv, snd_pipe);
214 |
215 | if (pipe < CCV(ccv, snd_ssthresh))
216 | CCV(ccv, snd_cwnd) = pipe + CCV(ccv, t_maxseg);
217 | else
218 | CCV(ccv, snd_cwnd) = CCV(ccv, snd_ssthresh);
219 | }
220 | }
221 |
222 |
223 | DECLARE_CC_MODULE(newreno, &newreno_cc_algo);
224 |
--------------------------------------------------------------------------------
/cc.h:
--------------------------------------------------------------------------------
1 | /*-
2 | * Copyright (c) 2007-2008
3 | * Swinburne University of Technology, Melbourne, Australia.
4 | * Copyright (c) 2009-2010 Lawrence Stewart
5 | * Copyright (c) 2010 The FreeBSD Foundation
6 | * Copyright (c) 2017,2018 Fastly
7 | * All rights reserved.
8 | *
9 | * This software was developed at the Centre for Advanced Internet
10 | * Architectures, Swinburne University of Technology, by Lawrence Stewart and
11 | * James Healy, made possible in part by a grant from the Cisco University
12 | * Research Program Fund at Community Foundation Silicon Valley.
13 | *
14 | * Portions of this software were developed at the Centre for Advanced
15 | * Internet Architectures, Swinburne University of Technology, Melbourne,
16 | * Australia by David Hayes under sponsorship from the FreeBSD Foundation.
17 | *
18 | * Redistribution and use in source and binary forms, with or without
19 | * modification, are permitted provided that the following conditions
20 | * are met:
21 | * 1. Redistributions of source code must retain the above copyright
22 | * notice, this list of conditions and the following disclaimer.
23 | * 2. Redistributions in binary form must reproduce the above copyright
24 | * notice, this list of conditions and the following disclaimer in the
25 | * documentation and/or other materials provided with the distribution.
26 | *
27 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
28 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
31 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 | * SUCH DAMAGE.
38 | *
39 | * $FreeBSD$
40 | */
41 |
42 | /*
43 | * This software was first released in 2007 by James Healy and Lawrence Stewart
44 | * whilst working on the NewTCP research project at Swinburne University of
45 | * Technology's Centre for Advanced Internet Architectures, Melbourne,
46 | * Australia, which was made possible in part by a grant from the Cisco
47 | * University Research Program Fund at Community Foundation Silicon Valley.
48 | * More details are available at:
49 | * http://caia.swin.edu.au/urp/newtcp/
50 | */
51 |
52 | #ifndef _NETINET_CC_CC_H_
53 | #define _NETINET_CC_CC_H_
54 |
55 | extern int cc_tcp_do_rfc3390;
56 | extern int cc_tcp_do_rfc3465;
57 | extern int cc_tcp_abc_l_var;
58 | extern int cc_hz;
59 | extern volatile int cc_ticks;
60 |
61 | #define CC_TF_RETRANSMIT 0x1
62 | #define CC_TF_PREVVALID 0x2
63 | #define CC_TF_FASTRECOVERY 0x10 /* in NewReno Fast Recovery */
64 | #define CC_TF_CONGRECOVERY 0x20 /* congestion recovery mode */
65 | #define CC_TF_WASFRECOVERY 0x100 /* in NewReno Fast Recovery */
66 | #define CC_TF_WASCRECOVERY 0x200 /* was in NewReno Fast Recovery */
67 |
68 | #define CC_IN_RECOVERY(t_flags) (t_flags & (CC_TF_CONGRECOVERY | CC_TF_FASTRECOVERY))
69 | #define CC_ENTER_RECOVERY(t_flags) t_flags |= (CC_TF_CONGRECOVERY | CC_TF_FASTRECOVERY)
70 | #define CC_EXIT_RECOVERY(t_flags) t_flags &= ~(CC_TF_CONGRECOVERY | CC_TF_FASTRECOVERY)
71 |
72 | #define CC_IN_FASTRECOVERY(t_flags) (t_flags & CC_TF_FASTRECOVERY)
73 | #define CC_ENTER_FASTRECOVERY(t_flags) t_flags |= CC_TF_FASTRECOVERY
74 | #define CC_EXIT_FASTRECOVERY(t_flags) t_flags &= ~CC_TF_FASTRECOVERY
75 |
76 | #define CC_IN_CONGRECOVERY(t_flags) (t_flags & CC_TF_CONGRECOVERY)
77 | #define CC_ENTER_CONGRECOVERY(t_flags) t_flags |= CC_TF_CONGRECOVERY
78 | #define CC_EXIT_CONGRECOVERY(t_flags) t_flags &= ~CC_TF_CONGRECOVERY
79 |
80 | void *cc_malloc(size_t sz, const char *lbl);
81 | void cc_free(void *p, const char *lbl);
82 |
83 | /* Global CC vars. */
84 | extern struct cc_algo newreno_cc_algo;
85 |
86 | /* control block (mostly taken from sys/netinet/tcp_var.h) */
87 | struct cc_ccv {
88 | unsigned t_flags;
89 | uint32_t snd_pipe;
90 | uint32_t snd_cwnd; /* congestion-controlled window */
91 | uint32_t snd_ssthresh; /* snd_cwnd size threshold for
92 | * for slow start exponential to
93 | * linear switch
94 | */
95 | uint8_t snd_scale; /* window scaling for send window */
96 | int t_bytes_acked; /* # bytes acked during current RTT */
97 | unsigned t_maxseg; /* maximum segment size */
98 | unsigned long t_rttupdated; /* number of times rtt sampled */
99 | int t_srtt; /* smoothed round-trip time */
100 | struct cc_algo *cc_algo;
101 |
102 | unsigned t_badrxtwin; /* window for retransmit recovery */
103 | uint32_t snd_cwnd_prev;
104 | uint32_t snd_ssthresh_prev;
105 | };
106 |
107 | /*
108 | * Wrapper around transport structs that contain same-named congestion
109 | * control variables. Allows algos to be shared amongst multiple CC aware
110 | * transprots.
111 | */
112 | struct cc_var {
113 | void *cc_data; /* Per-connection private CC algorithm data. */
114 | int bytes_this_ack; /* # bytes acked by the current ACK. */
115 | uint32_t flags; /* Flags for cc_var (see below) */
116 | int type; /* Indicates which ptr is valid in ccvc. */
117 | union ccv_container {
118 | struct cc_ccv ccv;
119 | } ccvc;
120 | uint16_t nsegs; /* # segments coalesced into current chain. */
121 | };
122 |
123 | /* cc_var flags. */
124 | #define CCF_ABC_SENTAWND 0x0001 /* ABC counted cwnd worth of bytes? */
125 | #define CCF_CWND_LIMITED 0x0002 /* Are we currently cwnd limited? */
126 | #define CCF_DELACK 0x0004 /* Is this ack delayed? */
127 | #define CCF_ACKNOW 0x0008 /* Will this ack be sent now? */
128 | #define CCF_IPHDR_CE 0x0010 /* Does this packet set CE bit? */
129 | #define CCF_TCPHDR_CWR 0x0020 /* Does this packet set CWR bit? */
130 |
131 | /* ACK types passed to the ack_received() hook. */
132 | #define CC_ACK 0x0001 /* Regular in sequence ACK. */
133 | #define CC_DUPACK 0x0002 /* Duplicate ACK. */
134 | #define CC_PARTIALACK 0x0004 /* Not yet. */
135 | #define CC_SACK 0x0008 /* Not yet. */
136 |
137 | /*
138 | * Congestion signal types passed to the cong_signal() hook. The highest order 8
139 | * bits (0x01000000 - 0x80000000) are reserved for CC algos to declare their own
140 | * congestion signal types.
141 | */
142 | #define CC_ECN 0x00000001 /* ECN marked packet received. */
143 | #define CC_RTO 0x00000002 /* RTO fired. */
144 | #define CC_RTO_ERR 0x00000004 /* RTO fired in error. */
145 | #define CC_NDUPACK 0x00000008 /* Threshold of dupack's reached. */
146 | #define CC_FIRST_RTO 0x00000010 /* first RTO */
147 |
148 | #define CC_SIGPRIVMASK 0xFF000000 /* Mask to check if sig is private. */
149 |
150 | /*
151 | * Structure to hold data and function pointers that together represent a
152 | * congestion control algorithm.
153 | */
154 | struct cc_algo {
155 | char name[16];
156 |
157 | /* Init global module state on kldload. */
158 | int (*mod_init)(void);
159 |
160 | /* Cleanup global module state on kldunload. */
161 | int (*mod_destroy)(void);
162 |
163 | /* Init CC state for a new control block. */
164 | int (*cb_init)(struct cc_var *ccv);
165 |
166 | /* Cleanup CC state for a terminating control block. */
167 | void (*cb_destroy)(struct cc_var *ccv);
168 |
169 | /* Init variables for a newly established connection. */
170 | void (*conn_init)(struct cc_var *ccv);
171 |
172 | /* Called on receipt of an ack. */
173 | void (*ack_received)(struct cc_var *ccv, uint16_t type);
174 |
175 | /* Called on detection of a congestion signal. */
176 | void (*cong_signal)(struct cc_var *ccv, uint32_t type);
177 |
178 | /* Called after exiting congestion recovery. */
179 | void (*post_recovery)(struct cc_var *ccv);
180 |
181 | /* Called when data transfer resumes after an idle period. */
182 | void (*after_idle)(struct cc_var *ccv);
183 |
184 | /* Called for an additional ECN processing apart from RFC3168. */
185 | void (*ecnpkt_handler)(struct cc_var *ccv);
186 | };
187 |
188 | /* Macro to obtain the CC algo's struct ptr. */
189 | #define CC_ALGO(tp) ((tp)->ccvc.ccv.cc_algo)
190 |
191 | /* Macro to obtain the CC algo's data ptr. */
192 | #define CC_DATA(tp) ((tp)->ccv->cc_data)
193 |
194 | int cc_init(struct cc_var *ccv, struct cc_algo *algo, uint32_t cwnd, unsigned maxseg);
195 | void cc_destroy(struct cc_var *ccv);
196 | void cc_ack_received(struct cc_var *ccv, uint16_t type, uint32_t bytes_in_pipe, uint16_t segs_acked, uint32_t bytes_acked, int srtt,
197 | int exit_recovery);
198 | void cc_cong_signal(struct cc_var *ccv, uint32_t type, uint32_t bytes_in_pipe);
199 | static uint32_t cc_get_cwnd(struct cc_var *ccv);
200 | static unsigned cc_get_maxseg(struct cc_var *ccv);
201 | static void cc_set_maxseg(struct cc_var *ccv, unsigned maxseg);
202 |
203 | inline uint32_t cc_get_cwnd(struct cc_var *ccv)
204 | {
205 | return ccv->ccvc.ccv.snd_cwnd;
206 | }
207 |
208 | inline unsigned cc_get_maxseg(struct cc_var *ccv)
209 | {
210 | return ccv->ccvc.ccv.t_maxseg;
211 | }
212 |
213 | inline void cc_set_maxseg(struct cc_var *ccv, unsigned maxseg)
214 | {
215 | ccv->ccvc.ccv.t_maxseg = maxseg;
216 | }
217 |
218 | #endif /* _NETINET_CC_CC_H_ */
219 |
--------------------------------------------------------------------------------
/dcc.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | E9DB664B1F297070001BB10B /* cc.c in Sources */ = {isa = PBXBuildFile; fileRef = E9DB663E1F297070001BB10B /* cc.c */; };
11 | E9DB664C1F297070001BB10B /* cc_newreno.c in Sources */ = {isa = PBXBuildFile; fileRef = E9DB66411F297070001BB10B /* cc_newreno.c */; };
12 | E9DB664D1F297070001BB10B /* cc_cubic.c in Sources */ = {isa = PBXBuildFile; fileRef = E9DB66421F297070001BB10B /* cc_cubic.c */; };
13 | E9DB66571F2970E2001BB10B /* test.c in Sources */ = {isa = PBXBuildFile; fileRef = E9DB66561F2970E2001BB10B /* test.c */; };
14 | E9DB665D1F29716B001BB10B /* picotest.c in Sources */ = {isa = PBXBuildFile; fileRef = E9DB665B1F29716B001BB10B /* picotest.c */; };
15 | /* End PBXBuildFile section */
16 |
17 | /* Begin PBXCopyFilesBuildPhase section */
18 | E9DB66311F297015001BB10B /* CopyFiles */ = {
19 | isa = PBXCopyFilesBuildPhase;
20 | buildActionMask = 2147483647;
21 | dstPath = /usr/share/man/man1/;
22 | dstSubfolderSpec = 0;
23 | files = (
24 | );
25 | runOnlyForDeploymentPostprocessing = 1;
26 | };
27 | /* End PBXCopyFilesBuildPhase section */
28 |
29 | /* Begin PBXFileReference section */
30 | E9DB66331F297015001BB10B /* test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = test; sourceTree = BUILT_PRODUCTS_DIR; };
31 | E9DB663D1F297070001BB10B /* cc_int.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cc_int.h; sourceTree = ""; };
32 | E9DB663E1F297070001BB10B /* cc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cc.c; sourceTree = ""; };
33 | E9DB66401F297070001BB10B /* cc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cc.h; sourceTree = ""; };
34 | E9DB66411F297070001BB10B /* cc_newreno.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cc_newreno.c; sourceTree = ""; };
35 | E9DB66421F297070001BB10B /* cc_cubic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cc_cubic.c; sourceTree = ""; };
36 | E9DB66461F297070001BB10B /* cc_cubic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cc_cubic.h; sourceTree = ""; };
37 | E9DB66561F2970E2001BB10B /* test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = test.c; sourceTree = ""; };
38 | E9DB665B1F29716B001BB10B /* picotest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = picotest.c; sourceTree = ""; };
39 | E9DB665C1F29716B001BB10B /* picotest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = picotest.h; sourceTree = ""; };
40 | /* End PBXFileReference section */
41 |
42 | /* Begin PBXFrameworksBuildPhase section */
43 | E9DB66301F297015001BB10B /* Frameworks */ = {
44 | isa = PBXFrameworksBuildPhase;
45 | buildActionMask = 2147483647;
46 | files = (
47 | );
48 | runOnlyForDeploymentPostprocessing = 0;
49 | };
50 | /* End PBXFrameworksBuildPhase section */
51 |
52 | /* Begin PBXGroup section */
53 | E9DB662A1F297015001BB10B = {
54 | isa = PBXGroup;
55 | children = (
56 | E9DB66581F297107001BB10B /* deps */,
57 | E9DB66401F297070001BB10B /* cc.h */,
58 | E9DB66461F297070001BB10B /* cc_cubic.h */,
59 | E9DB66421F297070001BB10B /* cc_cubic.c */,
60 | E9DB66411F297070001BB10B /* cc_newreno.c */,
61 | E9DB663D1F297070001BB10B /* cc_int.h */,
62 | E9DB663E1F297070001BB10B /* cc.c */,
63 | E9DB66551F2970CE001BB10B /* t */,
64 | E9DB66341F297015001BB10B /* Products */,
65 | );
66 | sourceTree = "";
67 | };
68 | E9DB66341F297015001BB10B /* Products */ = {
69 | isa = PBXGroup;
70 | children = (
71 | E9DB66331F297015001BB10B /* test */,
72 | );
73 | name = Products;
74 | sourceTree = "";
75 | };
76 | E9DB66551F2970CE001BB10B /* t */ = {
77 | isa = PBXGroup;
78 | children = (
79 | E9DB66561F2970E2001BB10B /* test.c */,
80 | );
81 | path = t;
82 | sourceTree = "";
83 | };
84 | E9DB66581F297107001BB10B /* deps */ = {
85 | isa = PBXGroup;
86 | children = (
87 | E9DB665A1F29715E001BB10B /* picotest */,
88 | );
89 | path = deps;
90 | sourceTree = "";
91 | };
92 | E9DB665A1F29715E001BB10B /* picotest */ = {
93 | isa = PBXGroup;
94 | children = (
95 | E9DB665C1F29716B001BB10B /* picotest.h */,
96 | E9DB665B1F29716B001BB10B /* picotest.c */,
97 | );
98 | path = picotest;
99 | sourceTree = "";
100 | };
101 | /* End PBXGroup section */
102 |
103 | /* Begin PBXNativeTarget section */
104 | E9DB66321F297015001BB10B /* test */ = {
105 | isa = PBXNativeTarget;
106 | buildConfigurationList = E9DB663A1F297015001BB10B /* Build configuration list for PBXNativeTarget "test" */;
107 | buildPhases = (
108 | E9DB662F1F297015001BB10B /* Sources */,
109 | E9DB66301F297015001BB10B /* Frameworks */,
110 | E9DB66311F297015001BB10B /* CopyFiles */,
111 | );
112 | buildRules = (
113 | );
114 | dependencies = (
115 | );
116 | name = test;
117 | productName = dcc;
118 | productReference = E9DB66331F297015001BB10B /* test */;
119 | productType = "com.apple.product-type.tool";
120 | };
121 | /* End PBXNativeTarget section */
122 |
123 | /* Begin PBXProject section */
124 | E9DB662B1F297015001BB10B /* Project object */ = {
125 | isa = PBXProject;
126 | attributes = {
127 | LastUpgradeCheck = 0830;
128 | ORGANIZATIONNAME = Fastly;
129 | TargetAttributes = {
130 | E9DB66321F297015001BB10B = {
131 | CreatedOnToolsVersion = 8.3.2;
132 | ProvisioningStyle = Automatic;
133 | };
134 | };
135 | };
136 | buildConfigurationList = E9DB662E1F297015001BB10B /* Build configuration list for PBXProject "dcc" */;
137 | compatibilityVersion = "Xcode 3.2";
138 | developmentRegion = English;
139 | hasScannedForEncodings = 0;
140 | knownRegions = (
141 | en,
142 | );
143 | mainGroup = E9DB662A1F297015001BB10B;
144 | productRefGroup = E9DB66341F297015001BB10B /* Products */;
145 | projectDirPath = "";
146 | projectRoot = "";
147 | targets = (
148 | E9DB66321F297015001BB10B /* test */,
149 | );
150 | };
151 | /* End PBXProject section */
152 |
153 | /* Begin PBXSourcesBuildPhase section */
154 | E9DB662F1F297015001BB10B /* Sources */ = {
155 | isa = PBXSourcesBuildPhase;
156 | buildActionMask = 2147483647;
157 | files = (
158 | E9DB665D1F29716B001BB10B /* picotest.c in Sources */,
159 | E9DB664C1F297070001BB10B /* cc_newreno.c in Sources */,
160 | E9DB664D1F297070001BB10B /* cc_cubic.c in Sources */,
161 | E9DB664B1F297070001BB10B /* cc.c in Sources */,
162 | E9DB66571F2970E2001BB10B /* test.c in Sources */,
163 | );
164 | runOnlyForDeploymentPostprocessing = 0;
165 | };
166 | /* End PBXSourcesBuildPhase section */
167 |
168 | /* Begin XCBuildConfiguration section */
169 | E9DB66381F297015001BB10B /* Debug */ = {
170 | isa = XCBuildConfiguration;
171 | buildSettings = {
172 | ALWAYS_SEARCH_USER_PATHS = NO;
173 | CLANG_ANALYZER_NONNULL = YES;
174 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
175 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
176 | CLANG_CXX_LIBRARY = "libc++";
177 | CLANG_ENABLE_MODULES = YES;
178 | CLANG_ENABLE_OBJC_ARC = YES;
179 | CLANG_WARN_BOOL_CONVERSION = YES;
180 | CLANG_WARN_CONSTANT_CONVERSION = YES;
181 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
182 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
183 | CLANG_WARN_EMPTY_BODY = YES;
184 | CLANG_WARN_ENUM_CONVERSION = YES;
185 | CLANG_WARN_INFINITE_RECURSION = YES;
186 | CLANG_WARN_INT_CONVERSION = YES;
187 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
188 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
189 | CLANG_WARN_UNREACHABLE_CODE = YES;
190 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
191 | CODE_SIGN_IDENTITY = "-";
192 | COPY_PHASE_STRIP = NO;
193 | DEBUG_INFORMATION_FORMAT = dwarf;
194 | ENABLE_STRICT_OBJC_MSGSEND = YES;
195 | ENABLE_TESTABILITY = YES;
196 | GCC_C_LANGUAGE_STANDARD = gnu99;
197 | GCC_DYNAMIC_NO_PIC = NO;
198 | GCC_NO_COMMON_BLOCKS = YES;
199 | GCC_OPTIMIZATION_LEVEL = 0;
200 | GCC_PREPROCESSOR_DEFINITIONS = (
201 | "DEBUG=1",
202 | "$(inherited)",
203 | );
204 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
205 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
206 | GCC_WARN_UNDECLARED_SELECTOR = YES;
207 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
208 | GCC_WARN_UNUSED_FUNCTION = YES;
209 | GCC_WARN_UNUSED_VARIABLE = YES;
210 | MACOSX_DEPLOYMENT_TARGET = 10.12;
211 | MTL_ENABLE_DEBUG_INFO = YES;
212 | ONLY_ACTIVE_ARCH = YES;
213 | SDKROOT = macosx;
214 | };
215 | name = Debug;
216 | };
217 | E9DB66391F297015001BB10B /* Release */ = {
218 | isa = XCBuildConfiguration;
219 | buildSettings = {
220 | ALWAYS_SEARCH_USER_PATHS = NO;
221 | CLANG_ANALYZER_NONNULL = YES;
222 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
223 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
224 | CLANG_CXX_LIBRARY = "libc++";
225 | CLANG_ENABLE_MODULES = YES;
226 | CLANG_ENABLE_OBJC_ARC = YES;
227 | CLANG_WARN_BOOL_CONVERSION = YES;
228 | CLANG_WARN_CONSTANT_CONVERSION = YES;
229 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
230 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
231 | CLANG_WARN_EMPTY_BODY = YES;
232 | CLANG_WARN_ENUM_CONVERSION = YES;
233 | CLANG_WARN_INFINITE_RECURSION = YES;
234 | CLANG_WARN_INT_CONVERSION = YES;
235 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
236 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
237 | CLANG_WARN_UNREACHABLE_CODE = YES;
238 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
239 | CODE_SIGN_IDENTITY = "-";
240 | COPY_PHASE_STRIP = NO;
241 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
242 | ENABLE_NS_ASSERTIONS = NO;
243 | ENABLE_STRICT_OBJC_MSGSEND = YES;
244 | GCC_C_LANGUAGE_STANDARD = gnu99;
245 | GCC_NO_COMMON_BLOCKS = YES;
246 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
247 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
248 | GCC_WARN_UNDECLARED_SELECTOR = YES;
249 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
250 | GCC_WARN_UNUSED_FUNCTION = YES;
251 | GCC_WARN_UNUSED_VARIABLE = YES;
252 | MACOSX_DEPLOYMENT_TARGET = 10.12;
253 | MTL_ENABLE_DEBUG_INFO = NO;
254 | SDKROOT = macosx;
255 | };
256 | name = Release;
257 | };
258 | E9DB663B1F297015001BB10B /* Debug */ = {
259 | isa = XCBuildConfiguration;
260 | buildSettings = {
261 | PRODUCT_NAME = "$(TARGET_NAME)";
262 | };
263 | name = Debug;
264 | };
265 | E9DB663C1F297015001BB10B /* Release */ = {
266 | isa = XCBuildConfiguration;
267 | buildSettings = {
268 | PRODUCT_NAME = "$(TARGET_NAME)";
269 | };
270 | name = Release;
271 | };
272 | /* End XCBuildConfiguration section */
273 |
274 | /* Begin XCConfigurationList section */
275 | E9DB662E1F297015001BB10B /* Build configuration list for PBXProject "dcc" */ = {
276 | isa = XCConfigurationList;
277 | buildConfigurations = (
278 | E9DB66381F297015001BB10B /* Debug */,
279 | E9DB66391F297015001BB10B /* Release */,
280 | );
281 | defaultConfigurationIsVisible = 0;
282 | defaultConfigurationName = Release;
283 | };
284 | E9DB663A1F297015001BB10B /* Build configuration list for PBXNativeTarget "test" */ = {
285 | isa = XCConfigurationList;
286 | buildConfigurations = (
287 | E9DB663B1F297015001BB10B /* Debug */,
288 | E9DB663C1F297015001BB10B /* Release */,
289 | );
290 | defaultConfigurationIsVisible = 0;
291 | defaultConfigurationName = Release;
292 | };
293 | /* End XCConfigurationList section */
294 | };
295 | rootObject = E9DB662B1F297015001BB10B /* Project object */;
296 | }
297 |
--------------------------------------------------------------------------------
/cc_cubic.c:
--------------------------------------------------------------------------------
1 | /*-
2 | * Copyright (c) 2008-2010 Lawrence Stewart
3 | * Copyright (c) 2010 The FreeBSD Foundation
4 | * All rights reserved.
5 | *
6 | * This software was developed by Lawrence Stewart while studying at the Centre
7 | * for Advanced Internet Architectures, Swinburne University of Technology, made
8 | * possible in part by a grant from the Cisco University Research Program Fund
9 | * at Community Foundation Silicon Valley.
10 | *
11 | * Portions of this software were developed at the Centre for Advanced
12 | * Internet Architectures, Swinburne University of Technology, Melbourne,
13 | * Australia by David Hayes under sponsorship from the FreeBSD Foundation.
14 | *
15 | * Redistribution and use in source and binary forms, with or without
16 | * modification, are permitted provided that the following conditions
17 | * are met:
18 | * 1. Redistributions of source code must retain the above copyright
19 | * notice, this list of conditions and the following disclaimer.
20 | * 2. Redistributions in binary form must reproduce the above copyright
21 | * notice, this list of conditions and the following disclaimer in the
22 | * documentation and/or other materials provided with the distribution.
23 | *
24 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 | * SUCH DAMAGE.
35 | */
36 |
37 | /*
38 | * An implementation of the CUBIC congestion control algorithm for FreeBSD,
39 | * based on the Internet Draft "draft-rhee-tcpm-cubic-02" by Rhee, Xu and Ha.
40 | * Originally released as part of the NewTCP research project at Swinburne
41 | * University of Technology's Centre for Advanced Internet Architectures,
42 | * Melbourne, Australia, which was made possible in part by a grant from the
43 | * Cisco University Research Program Fund at Community Foundation Silicon
44 | * Valley. More details are available at:
45 | * http://caia.swin.edu.au/urp/newtcp/
46 | */
47 |
48 | #include "cc_int.h"
49 | #include "cc_cubic.h"
50 |
51 | static void cubic_ack_received(struct cc_var *ccv, uint16_t type);
52 | static void cubic_cb_destroy(struct cc_var *ccv);
53 | static int cubic_cb_init(struct cc_var *ccv);
54 | static void cubic_cong_signal(struct cc_var *ccv, uint32_t type);
55 | static void cubic_conn_init(struct cc_var *ccv);
56 | static int cubic_mod_init(void);
57 | static void cubic_post_recovery(struct cc_var *ccv);
58 | static void cubic_record_rtt(struct cc_var *ccv);
59 | static void cubic_ssthresh_update(struct cc_var *ccv);
60 |
61 | struct cubic {
62 | /* Cubic K in fixed point form with CUBIC_SHIFT worth of precision. */
63 | int64_t K;
64 | /* Sum of RTT samples across an epoch in ticks. */
65 | int64_t sum_rtt_ticks;
66 | /* cwnd at the most recent congestion event. */
67 | unsigned long max_cwnd;
68 | /* cwnd at the previous congestion event. */
69 | unsigned long prev_max_cwnd;
70 | /* Number of congestion events. */
71 | uint32_t num_cong_events;
72 | /* Minimum observed rtt in ticks. */
73 | int min_rtt_ticks;
74 | /* Mean observed rtt between congestion epochs. */
75 | int mean_rtt_ticks;
76 | /* ACKs since last congestion event. */
77 | int epoch_ack_count;
78 | /* Time of last congestion event in ticks. */
79 | int t_last_cong;
80 | };
81 |
82 | static MALLOC_DEFINE(M_CUBIC, "cubic data",
83 | "Per connection data required for the CUBIC congestion control algorithm");
84 |
85 | struct cc_algo cubic_cc_algo = {
86 | .name = "cubic",
87 | .ack_received = cubic_ack_received,
88 | .cb_destroy = cubic_cb_destroy,
89 | .cb_init = cubic_cb_init,
90 | .cong_signal = cubic_cong_signal,
91 | .conn_init = cubic_conn_init,
92 | .mod_init = cubic_mod_init,
93 | .post_recovery = cubic_post_recovery,
94 | };
95 |
96 | static void
97 | cubic_ack_received(struct cc_var *ccv, uint16_t type)
98 | {
99 | struct cubic *cubic_data;
100 | unsigned long w_tf, w_cubic_next;
101 | int ticks_since_cong;
102 |
103 | cubic_data = ccv->cc_data;
104 | cubic_record_rtt(ccv);
105 |
106 | /*
107 | * Regular ACK and we're not in cong/fast recovery and we're cwnd
108 | * limited and we're either not doing ABC or are slow starting or are
109 | * doing ABC and we've sent a cwnd's worth of bytes.
110 | */
111 | if (type == CC_ACK && !IN_RECOVERY(CCV(ccv, t_flags)) &&
112 | (ccv->flags & CCF_CWND_LIMITED) && (!V_tcp_do_rfc3465 ||
113 | CCV(ccv, snd_cwnd) <= CCV(ccv, snd_ssthresh) ||
114 | (V_tcp_do_rfc3465 && ccv->flags & CCF_ABC_SENTAWND))) {
115 | /* Use the logic in NewReno ack_received() for slow start. */
116 | if (CCV(ccv, snd_cwnd) <= CCV(ccv, snd_ssthresh) ||
117 | cubic_data->min_rtt_ticks == TCPTV_SRTTBASE)
118 | newreno_cc_algo.ack_received(ccv, type);
119 | else {
120 | ticks_since_cong = ticks - cubic_data->t_last_cong;
121 |
122 | /*
123 | * The mean RTT is used to best reflect the equations in
124 | * the I-D. Using min_rtt in the tf_cwnd calculation
125 | * causes w_tf to grow much faster than it should if the
126 | * RTT is dominated by network buffering rather than
127 | * propagation delay.
128 | */
129 | w_tf = tf_cwnd(ticks_since_cong,
130 | cubic_data->mean_rtt_ticks, cubic_data->max_cwnd,
131 | CCV(ccv, t_maxseg));
132 |
133 | w_cubic_next = cubic_cwnd(ticks_since_cong +
134 | cubic_data->mean_rtt_ticks, cubic_data->max_cwnd,
135 | CCV(ccv, t_maxseg), cubic_data->K);
136 |
137 | ccv->flags &= ~CCF_ABC_SENTAWND;
138 |
139 | if (w_cubic_next < w_tf)
140 | /*
141 | * TCP-friendly region, follow tf
142 | * cwnd growth.
143 | */
144 | CCV(ccv, snd_cwnd) = w_tf;
145 |
146 | else if (CCV(ccv, snd_cwnd) < w_cubic_next) {
147 | /*
148 | * Concave or convex region, follow CUBIC
149 | * cwnd growth.
150 | */
151 | if (V_tcp_do_rfc3465)
152 | CCV(ccv, snd_cwnd) = w_cubic_next;
153 | else
154 | CCV(ccv, snd_cwnd) += ((w_cubic_next -
155 | CCV(ccv, snd_cwnd)) *
156 | CCV(ccv, t_maxseg)) /
157 | CCV(ccv, snd_cwnd);
158 | }
159 |
160 | /*
161 | * If we're not in slow start and we're probing for a
162 | * new cwnd limit at the start of a connection
163 | * (happens when hostcache has a relevant entry),
164 | * keep updating our current estimate of the
165 | * max_cwnd.
166 | */
167 | if (cubic_data->num_cong_events == 0 &&
168 | cubic_data->max_cwnd < CCV(ccv, snd_cwnd))
169 | cubic_data->max_cwnd = CCV(ccv, snd_cwnd);
170 | }
171 | }
172 | }
173 |
174 | static void
175 | cubic_cb_destroy(struct cc_var *ccv)
176 | {
177 |
178 | if (ccv->cc_data != NULL)
179 | free(ccv->cc_data, M_CUBIC);
180 | }
181 |
182 | static int
183 | cubic_cb_init(struct cc_var *ccv)
184 | {
185 | struct cubic *cubic_data;
186 |
187 | cubic_data = malloc(sizeof(struct cubic), M_CUBIC, M_NOWAIT|M_ZERO);
188 |
189 | if (cubic_data == NULL)
190 | return (ENOMEM);
191 |
192 | /* Init some key variables with sensible defaults. */
193 | cubic_data->t_last_cong = ticks;
194 | cubic_data->min_rtt_ticks = TCPTV_SRTTBASE;
195 | cubic_data->mean_rtt_ticks = 1;
196 |
197 | ccv->cc_data = cubic_data;
198 |
199 | return (0);
200 | }
201 |
202 | /*
203 | * Perform any necessary tasks before we enter congestion recovery.
204 | */
205 | static void
206 | cubic_cong_signal(struct cc_var *ccv, uint32_t type)
207 | {
208 | struct cubic *cubic_data;
209 |
210 | cubic_data = ccv->cc_data;
211 |
212 | switch (type) {
213 | case CC_NDUPACK:
214 | if (!IN_FASTRECOVERY(CCV(ccv, t_flags))) {
215 | if (!IN_CONGRECOVERY(CCV(ccv, t_flags))) {
216 | cubic_ssthresh_update(ccv);
217 | cubic_data->num_cong_events++;
218 | cubic_data->prev_max_cwnd = cubic_data->max_cwnd;
219 | cubic_data->max_cwnd = CCV(ccv, snd_cwnd);
220 | }
221 | ENTER_RECOVERY(CCV(ccv, t_flags));
222 | }
223 | break;
224 |
225 | case CC_ECN:
226 | if (!IN_CONGRECOVERY(CCV(ccv, t_flags))) {
227 | cubic_ssthresh_update(ccv);
228 | cubic_data->num_cong_events++;
229 | cubic_data->prev_max_cwnd = cubic_data->max_cwnd;
230 | cubic_data->max_cwnd = CCV(ccv, snd_cwnd);
231 | cubic_data->t_last_cong = ticks;
232 | CCV(ccv, snd_cwnd) = CCV(ccv, snd_ssthresh);
233 | ENTER_CONGRECOVERY(CCV(ccv, t_flags));
234 | }
235 | break;
236 |
237 | case CC_FIRST_RTO:
238 | break;
239 |
240 | case CC_RTO:
241 | /*
242 | * Grab the current time and record it so we know when the
243 | * most recent congestion event was. Only record it when the
244 | * timeout has fired more than once, as there is a reasonable
245 | * chance the first one is a false alarm and may not indicate
246 | * congestion.
247 | */
248 | cubic_data->num_cong_events++;
249 | cubic_data->t_last_cong = ticks;
250 | break;
251 | }
252 | }
253 |
254 | static void
255 | cubic_conn_init(struct cc_var *ccv)
256 | {
257 | struct cubic *cubic_data;
258 |
259 | cubic_data = ccv->cc_data;
260 |
261 | /*
262 | * Ensure we have a sane initial value for max_cwnd recorded. Without
263 | * this here bad things happen when entries from the TCP hostcache
264 | * get used.
265 | */
266 | cubic_data->max_cwnd = CCV(ccv, snd_cwnd);
267 | }
268 |
269 | static int
270 | cubic_mod_init(void)
271 | {
272 |
273 | cubic_cc_algo.after_idle = newreno_cc_algo.after_idle;
274 |
275 | return (0);
276 | }
277 |
278 | /*
279 | * Perform any necessary tasks before we exit congestion recovery.
280 | */
281 | static void
282 | cubic_post_recovery(struct cc_var *ccv)
283 | {
284 | struct cubic *cubic_data;
285 | int pipe;
286 |
287 | cubic_data = ccv->cc_data;
288 | pipe = 0;
289 |
290 | /* Fast convergence heuristic. */
291 | if (cubic_data->max_cwnd < cubic_data->prev_max_cwnd)
292 | cubic_data->max_cwnd = (cubic_data->max_cwnd * CUBIC_FC_FACTOR)
293 | >> CUBIC_SHIFT;
294 |
295 | if (IN_FASTRECOVERY(CCV(ccv, t_flags))) {
296 | /*
297 | * If inflight data is less than ssthresh, set cwnd
298 | * conservatively to avoid a burst of data, as suggested in
299 | * the NewReno RFC. Otherwise, use the CUBIC method.
300 | *
301 | * XXXLAS: Find a way to do this without needing curack
302 | */
303 | pipe = CCV(ccv, snd_pipe);
304 |
305 | if (pipe < CCV(ccv, snd_ssthresh))
306 | CCV(ccv, snd_cwnd) = pipe + CCV(ccv, t_maxseg);
307 | else
308 | /* Update cwnd based on beta and adjusted max_cwnd. */
309 | CCV(ccv, snd_cwnd) = max(1, ((CUBIC_BETA *
310 | cubic_data->max_cwnd) >> CUBIC_SHIFT));
311 | }
312 | cubic_data->t_last_cong = ticks;
313 |
314 | /* Calculate the average RTT between congestion epochs. */
315 | if (cubic_data->epoch_ack_count > 0 &&
316 | cubic_data->sum_rtt_ticks >= cubic_data->epoch_ack_count) {
317 | cubic_data->mean_rtt_ticks = (int)(cubic_data->sum_rtt_ticks /
318 | cubic_data->epoch_ack_count);
319 | }
320 |
321 | cubic_data->epoch_ack_count = 0;
322 | cubic_data->sum_rtt_ticks = 0;
323 | cubic_data->K = cubic_k(cubic_data->max_cwnd / CCV(ccv, t_maxseg));
324 | }
325 |
326 | /*
327 | * Record the min RTT and sum samples for the epoch average RTT calculation.
328 | */
329 | static void
330 | cubic_record_rtt(struct cc_var *ccv)
331 | {
332 | struct cubic *cubic_data;
333 | int t_srtt_ticks;
334 |
335 | /* Ignore srtt until a min number of samples have been taken. */
336 | if (CCV(ccv, t_rttupdated) >= CUBIC_MIN_RTT_SAMPLES) {
337 | cubic_data = ccv->cc_data;
338 | t_srtt_ticks = CCV(ccv, t_srtt) / TCP_RTT_SCALE;
339 |
340 | /*
341 | * Record the current SRTT as our minrtt if it's the smallest
342 | * we've seen or minrtt is currently equal to its initialised
343 | * value.
344 | *
345 | * XXXLAS: Should there be some hysteresis for minrtt?
346 | */
347 | if ((t_srtt_ticks < cubic_data->min_rtt_ticks ||
348 | cubic_data->min_rtt_ticks == TCPTV_SRTTBASE)) {
349 | cubic_data->min_rtt_ticks = max(1, t_srtt_ticks);
350 |
351 | /*
352 | * If the connection is within its first congestion
353 | * epoch, ensure we prime mean_rtt_ticks with a
354 | * reasonable value until the epoch average RTT is
355 | * calculated in cubic_post_recovery().
356 | */
357 | if (cubic_data->min_rtt_ticks >
358 | cubic_data->mean_rtt_ticks)
359 | cubic_data->mean_rtt_ticks =
360 | cubic_data->min_rtt_ticks;
361 | }
362 |
363 | /* Sum samples for epoch average RTT calculation. */
364 | cubic_data->sum_rtt_ticks += t_srtt_ticks;
365 | cubic_data->epoch_ack_count++;
366 | }
367 | }
368 |
369 | /*
370 | * Update the ssthresh in the event of congestion.
371 | */
372 | static void
373 | cubic_ssthresh_update(struct cc_var *ccv)
374 | {
375 | struct cubic *cubic_data;
376 |
377 | cubic_data = ccv->cc_data;
378 |
379 | /*
380 | * On the first congestion event, set ssthresh to cwnd * 0.5, on
381 | * subsequent congestion events, set it to cwnd * beta.
382 | */
383 | if (cubic_data->num_cong_events == 0)
384 | CCV(ccv, snd_ssthresh) = CCV(ccv, snd_cwnd) >> 1;
385 | else
386 | CCV(ccv, snd_ssthresh) = ((unsigned long)CCV(ccv, snd_cwnd) *
387 | CUBIC_BETA) >> CUBIC_SHIFT;
388 | }
389 |
390 |
391 | DECLARE_CC_MODULE(cubic, &cubic_cc_algo);
392 |
--------------------------------------------------------------------------------