├── .gitignore ├── .gitmodules ├── README.md ├── cc.c ├── cc.h ├── cc_cubic.c ├── cc_cubic.h ├── cc_int.h ├── cc_newreno.c ├── dcc.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata └── t └── test.c /.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 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/picotest"] 2 | path = deps/picotest 3 | url = https://github.com/h2o/picotest.git 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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_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_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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /dcc.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------