/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2008-2010 Lawrence Stewart <[email protected]>4* Copyright (c) 2010 The FreeBSD Foundation5* All rights reserved.6*7* This software was developed by Lawrence Stewart while studying at the Centre8* for Advanced Internet Architectures, Swinburne University of Technology, made9* possible in part by a grant from the Cisco University Research Program Fund10* at Community Foundation Silicon Valley.11*12* Portions of this software were developed at the Centre for Advanced13* Internet Architectures, Swinburne University of Technology, Melbourne,14* Australia by David Hayes under sponsorship from the FreeBSD Foundation.15*16* Redistribution and use in source and binary forms, with or without17* modification, are permitted provided that the following conditions18* are met:19* 1. Redistributions of source code must retain the above copyright20* notice, this list of conditions and the following disclaimer.21* 2. Redistributions in binary form must reproduce the above copyright22* notice, this list of conditions and the following disclaimer in the23* documentation and/or other materials provided with the distribution.24*25* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND26* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE27* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE28* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE29* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL30* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS31* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)32* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT33* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY34* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF35* SUCH DAMAGE.36*/3738#ifndef _NETINET_CC_CUBIC_H_39#define _NETINET_CC_CUBIC_H_4041#include <sys/limits.h>4243/* Number of bits of precision for fixed point math calcs. */44#define CUBIC_SHIFT 84546#define CUBIC_SHIFT_4 324748/* 0.5 << CUBIC_SHIFT. */49#define RENO_BETA 1285051/* ~0.7 << CUBIC_SHIFT. */52#define CUBIC_BETA 1795354/* ~0.3 << CUBIC_SHIFT. */55#define ONE_SUB_CUBIC_BETA 775657/* 3 * ONE_SUB_CUBIC_BETA. */58#define THREE_X_PT3 2315960/* (2 << CUBIC_SHIFT) - ONE_SUB_CUBIC_BETA. */61#define TWO_SUB_PT3 4356263/* ~0.4 << CUBIC_SHIFT. */64#define CUBIC_C_FACTOR 1026566/* CUBIC fast convergence factor: (1+beta_cubic)/2. */67#define CUBIC_FC_FACTOR 2176869/* Don't trust s_rtt until this many rtt samples have been taken. */70#define CUBIC_MIN_RTT_SAMPLES 87172/*73* (2^21)^3 is long max. Dividing (2^63) by Cubic_C_factor74* and taking cube-root yields 448845 as the effective useful limit75*/76#define CUBED_ROOT_MAX_ULONG 4488457778/* Flags used in the cubic structure */79#define CUBICFLAG_CONG_EVENT 0x00000001 /* congestion experienced */80#define CUBICFLAG_IN_SLOWSTART 0x00000002 /* in slow start */81#define CUBICFLAG_IN_APPLIMIT 0x00000004 /* application limited */82#define CUBICFLAG_RTO_EVENT 0x00000008 /* RTO experienced */83#define CUBICFLAG_HYSTART_ENABLED 0x00000010 /* Hystart++ is enabled */84#define CUBICFLAG_HYSTART_IN_CSS 0x00000020 /* We are in Hystart++ CSS */85#define CUBICFLAG_IN_TF 0x00000040 /* We are in TCP friendly region */8687/* Kernel only bits */88#ifdef _KERNEL89struct cubic {90/*91* CUBIC K in fixed point form with CUBIC_SHIFT worth of precision.92* Also means the time period in seconds it takes to increase the93* congestion window size at the beginning of the current congestion94* avoidance stage to W_max.95*/96int64_t K;97/* Sum of RTT samples across an epoch in usecs. */98int64_t sum_rtt_usecs;99/* Size of cwnd (in bytes) just before cwnd was reduced in the last congestion event. */100uint32_t W_max;101/* An estimate (in bytes) for the congestion window in the Reno-friendly region */102uint32_t W_est;103/* An estimate (in bytes) for the congestion window in the CUBIC region */104uint32_t W_cubic;105/* The cwnd (in bytes) at the beginning of the current congestion avoidance stage. */106uint32_t cwnd_epoch;107/* various flags */108uint32_t flags;109/* Minimum observed rtt in usecs. */110int min_rtt_usecs;111/* Mean observed rtt between congestion epochs. */112int mean_rtt_usecs;113/* ACKs since last congestion event. */114int epoch_ack_count;115/* Timestamp (in ticks) at which the current CA epoch started. */116int t_epoch;117/* Timestamp (in ticks) at which the previous CA epoch started. */118int undo_t_epoch;119/* Few variables to restore the state after RTO_ERR */120int64_t undo_K;121uint32_t undo_W_max;122uint32_t undo_cwnd_epoch;123uint32_t css_baseline_minrtt;124uint32_t css_current_round_minrtt;125uint32_t css_lastround_minrtt;126uint32_t css_rttsample_count;127uint32_t css_entered_at_round;128uint32_t css_current_round;129uint32_t css_fas_at_css_entry;130uint32_t css_lowrtt_fas;131uint32_t css_last_fas;132};133#endif134135/* Userland only bits. */136#ifndef _KERNEL137138extern int hz;139140/*141* Implementation based on the formulas in RFC9438.142*143*/144145146/*147* Returns K, the time period in seconds it takes to increase the congestion148* window size at the beginning of the current congestion avoidance stage to149* W_max.150*/151static inline float152theoretical_cubic_k(uint32_t wmax_segs, uint32_t cwnd_epoch_segs)153{154double C;155156C = 0.4;157if (wmax_segs <= cwnd_epoch_segs)158return 0.0;159160/*161* Figure 2: K = ((W_max - cwnd_epoch) / C)^(1/3)162*/163return (pow((wmax_segs - cwnd_epoch_segs) / C, (1.0 / 3.0)) * pow(2, CUBIC_SHIFT));164}165166/*167* Returns the congestion window in segments at time t in seconds based on the168* cubic increase function, where t is the elapsed time in seconds from the169* beginning of the current congestion avoidance stage, as described in RFC9438170* Section 4.2.171*/172static inline unsigned long173theoretical_cubic_cwnd(int ticks_elapsed, uint32_t wmax_segs, uint32_t cwnd_epoch_segs)174{175double C, t;176float K;177178C = 0.4;179t = ticks_elapsed / (double)hz;180K = theoretical_cubic_k(wmax_segs, cwnd_epoch_segs);181182/*183* Figure 1: W_cubic(t) = C * (t - K)^3 + W_max184*/185return (C * pow(t - K / pow(2, CUBIC_SHIFT), 3.0) + wmax_segs);186}187188/*189* Returns estimated Reno congestion window in segments.190*/191static inline unsigned long192theoretical_reno_cwnd(int ticks_elapsed, int rtt_ticks, uint32_t wmax_segs)193{194195return (wmax_segs * 0.5 + ticks_elapsed / (float)rtt_ticks);196}197198/*199* Returns an estimate for the congestion window in segments in the200* Reno-friendly region -- that is, an estimate for the congestion window of201* Reno, as described in RFC9438 Section 4.3, where:202* cwnd: Current congestion window in segments.203* cwnd_prior: Size of cwnd in segments at the time of setting ssthresh most204* recently, either upon exiting the first slow start or just before205* cwnd was reduced in the last congestion event.206* W_est: An estimate for the congestion window in segments in the Reno-friendly207* region -- that is, an estimate for the congestion window of Reno.208*/209static inline unsigned long210theoretical_tf_cwnd(unsigned long W_est, unsigned long segs_acked, unsigned long cwnd,211unsigned long cwnd_prior)212{213float cubic_alpha, cubic_beta;214215/* RFC9438 Section 4.6: The parameter β_cubic SHOULD be set to 0.7. */216cubic_beta = 0.7;217218if (W_est >= cwnd_prior)219cubic_alpha = 1.0;220else221cubic_alpha = (3.0 * (1.0 - cubic_beta)) / (1.0 + cubic_beta);222223/*224* Figure 4: W_est = W_est + α_cubic * segments_acked / cwnd225*/226return (W_est + cubic_alpha * segs_acked / cwnd);227}228229#endif /* !_KERNEL */230231/*232* Compute the CUBIC K value used in the cwnd calculation, using an233* implementation mentioned in Figure. 2 of RFC9438.234* The method used here is adapted from Apple Computer Technical Report #KT-32.235*/236static inline int64_t237cubic_k(uint32_t wmax_segs, uint32_t cwnd_epoch_segs)238{239int64_t s, K;240uint16_t p;241242K = s = 0;243p = 0;244245/* Handle the corner case where W_max <= cwnd_epoch */246if (wmax_segs <= cwnd_epoch_segs) {247return 0;248}249250/* (wmax - cwnd_epoch) / C with CUBIC_SHIFT worth of precision. */251s = ((wmax_segs - cwnd_epoch_segs) << (2 * CUBIC_SHIFT)) / CUBIC_C_FACTOR;252253/* Rebase s to be between 1 and 1/8 with a shift of CUBIC_SHIFT. */254while (s >= 256) {255s >>= 3;256p++;257}258259/*260* Some magic constants taken from the Apple TR with appropriate261* shifts: 275 == 1.072302 << CUBIC_SHIFT, 98 == 0.3812513 <<262* CUBIC_SHIFT, 120 == 0.46946116 << CUBIC_SHIFT.263*/264K = (((s * 275) >> CUBIC_SHIFT) + 98) -265(((s * s * 120) >> CUBIC_SHIFT) >> CUBIC_SHIFT);266267/* Multiply by 2^p to undo the rebasing of s from above. */268return (K <<= p);269}270271/*272* Compute and return the new cwnd value in bytes using an implementation273* mentioned in Figure. 1 of RFC9438.274* Thanks to Kip Macy for help debugging this function.275*276* XXXLAS: Characterise bounds for overflow.277*/278static inline uint32_t279cubic_cwnd(int usecs_since_epoch, uint32_t wmax, uint32_t smss, int64_t K)280{281int64_t cwnd;282283/* K is in fixed point form with CUBIC_SHIFT worth of precision. */284285/* t - K, with CUBIC_SHIFT worth of precision. */286cwnd = (((int64_t)usecs_since_epoch << CUBIC_SHIFT) - (K * hz * tick)) /287(hz * tick);288289if (cwnd > CUBED_ROOT_MAX_ULONG)290return INT_MAX;291if (cwnd < -CUBED_ROOT_MAX_ULONG)292return 0;293294/* (t - K)^3, with CUBIC_SHIFT^3 worth of precision. */295cwnd *= (cwnd * cwnd);296297/*298* Figure 1: C * (t - K)^3 + wmax299* The down shift by CUBIC_SHIFT_4 is because cwnd has 4 lots of300* CUBIC_SHIFT included in the value. 3 from the cubing of cwnd above,301* and an extra from multiplying through by CUBIC_C_FACTOR.302*/303304cwnd = ((cwnd * CUBIC_C_FACTOR) >> CUBIC_SHIFT_4) * smss + wmax;305306/*307* for negative cwnd, limiting to zero as lower bound308*/309return (lmax(0,cwnd));310}311312/*313* Compute the "TCP friendly" cwnd by newreno in congestion avoidance state.314*/315static inline uint32_t316tf_cwnd(struct cc_var *ccv)317{318/* newreno is "TCP friendly" */319return newreno_cc_cwnd_in_cong_avoid(ccv);320}321322#endif /* _NETINET_CC_CUBIC_H_ */323324325