lsquic_cubic.c revision bfc7bfd8
1/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */ 2/* 3 * lsquic_cubic.c -- LSQUIC CUBIC implementation. 4 */ 5 6#include <inttypes.h> 7#include <math.h> 8#include <stddef.h> 9#include <stdlib.h> 10#include <string.h> 11 12#include "lsquic_int_types.h" 13#include "lsquic_types.h" 14#include "lsquic_cubic.h" 15#include "lsquic_util.h" 16 17#define LSQUIC_LOGGER_MODULE LSQLM_CUBIC 18#define LSQUIC_LOG_CONN_ID cubic->cu_cid 19#include "lsquic_logger.h" 20 21#define FAST_CONVERGENCE 1 22#define BETA 205 /* 205/1024 */ 23#define C 410 /* 410/1024 */ 24#define TWO_MINUS_BETA_OVER_TWO 922 /* 922/1024 */ 25#define ONE_MINUS_BETA 819 /* 819/1024 */ 26#define ONE_OVER_C 2560 /* 2560/1024 */ 27 28static void 29cubic_reset (struct lsquic_cubic *cubic) 30{ 31 memset(cubic, 0, offsetof(struct lsquic_cubic, cu_cid)); 32 cubic->cu_cwnd = 32 * TCP_MSS; 33 cubic->cu_last_max_cwnd = 32 * TCP_MSS; 34 cubic->cu_tcp_cwnd = 32 * TCP_MSS; 35} 36 37 38static void 39cubic_update (struct lsquic_cubic *cubic, lsquic_time_t now, unsigned n_bytes) 40{ 41 double delta_t, t; 42 lsquic_time_t target; 43 44 if (0 == cubic->cu_epoch_start) 45 { 46 cubic->cu_epoch_start = now; 47 if (cubic->cu_cwnd < cubic->cu_last_max_cwnd) 48 { 49 cubic->cu_K = cbrt(cubic->cu_last_max_cwnd / TCP_MSS / 2); 50 cubic->cu_origin_point = cubic->cu_last_max_cwnd; 51 } 52 else 53 { 54 cubic->cu_K = 0; 55 cubic->cu_origin_point = cubic->cu_cwnd; 56 } 57 LSQ_DEBUG("cwnd: %lu; last_max_cwnd: %lu; K: %lf; origin_point: %lu", 58 cubic->cu_cwnd, cubic->cu_last_max_cwnd, cubic->cu_K, cubic->cu_origin_point); 59 } 60 61 delta_t = (double) (now + cubic->cu_min_delay - cubic->cu_epoch_start) / 1000000; 62 if (delta_t < cubic->cu_K) 63 { 64 t = cubic->cu_K - delta_t; 65 target = cubic->cu_origin_point - t * t * t * 0.4 * TCP_MSS; 66 LSQ_DEBUG("delta_t: %lf; t: %lf; target 1: %"PRIu64, delta_t, t, target); 67 } 68 else 69 { 70 t = delta_t - cubic->cu_K; 71 target = cubic->cu_origin_point + t * t * t * 0.4 * TCP_MSS; 72 LSQ_DEBUG("target 2: %"PRIu64, target); 73 } 74 75 if (cubic->cu_flags & CU_TCP_FRIENDLY) 76 { 77 cubic->cu_tcp_cwnd += n_bytes * TCP_MSS * ONE_MINUS_BETA / 1024 78 / cubic->cu_tcp_cwnd; 79 LSQ_DEBUG("delta_t: %lf; last_max: %lu; cu_tcp_cwnd: %lu; target: " 80 "%"PRIu64"; over: %d; left: %d", delta_t, cubic->cu_last_max_cwnd, 81 cubic->cu_tcp_cwnd, target, cubic->cu_tcp_cwnd > target, 82 delta_t < cubic->cu_K); 83 if (cubic->cu_tcp_cwnd > target) 84 target = cubic->cu_tcp_cwnd; 85 } 86 87 if (target == 0) 88 target = TCP_MSS; 89 90 cubic->cu_cwnd = target; 91} 92 93 94void 95lsquic_cubic_init_ext (struct lsquic_cubic *cubic, lsquic_cid_t cid, 96 enum cubic_flags flags) 97{ 98 cubic_reset(cubic); 99 cubic->cu_ssthresh = 10000 * TCP_MSS; /* Emulate "unbounded" slow start */ 100 cubic->cu_cid = cid; 101 cubic->cu_flags = flags; 102#ifndef NDEBUG 103 const char *s; 104 s = getenv("LSQUIC_CUBIC_SAMPLING_RATE"); 105 if (s) 106 cubic->cu_sampling_rate = atoi(s); 107 else 108#endif 109 cubic->cu_sampling_rate = 100000; 110 LSQ_DEBUG("%s(cubic, %"PRIu64", 0x%X)", __func__, cid, flags); 111 LSQ_INFO("initialized"); 112} 113 114 115#define LOG_CWND(c) do { \ 116 if (LSQ_LOG_ENABLED(LSQ_LOG_INFO)) { \ 117 lsquic_time_t now = lsquic_time_now(); \ 118 now -= now % (c)->cu_sampling_rate; \ 119 if (now > (c)->cu_last_logged) { \ 120 LSQ_INFO("CWND: %lu", (c)->cu_cwnd); \ 121 (c)->cu_last_logged = now; \ 122 } \ 123 } \ 124} while (0) 125 126 127void 128lsquic_cubic_was_quiet (struct lsquic_cubic *cubic, lsquic_time_t now) 129{ 130 LSQ_DEBUG("%s(cubic, %"PRIu64")", __func__, now); 131 cubic->cu_epoch_start = 0; 132} 133 134 135void 136lsquic_cubic_ack (struct lsquic_cubic *cubic, lsquic_time_t now, 137 lsquic_time_t rtt, int app_limited, unsigned n_bytes) 138{ 139 LSQ_DEBUG("%s(cubic, %"PRIu64", %"PRIu64", %d, %u)", __func__, now, rtt, 140 app_limited, n_bytes); 141 if (0 == cubic->cu_min_delay || rtt < cubic->cu_min_delay) 142 { 143 cubic->cu_min_delay = rtt; 144 LSQ_INFO("min_delay: %"PRIu64, rtt); 145 } 146 147 if (cubic->cu_cwnd <= cubic->cu_ssthresh) 148 { 149 cubic->cu_cwnd += TCP_MSS; 150 LSQ_DEBUG("ACK: slow threshold, cwnd: %lu", cubic->cu_cwnd); 151 } 152 else if (!app_limited) 153 { 154 cubic_update(cubic, now, n_bytes); 155 LSQ_DEBUG("ACK: cwnd: %lu", cubic->cu_cwnd); 156 } 157 158 LOG_CWND(cubic); 159} 160 161 162void 163lsquic_cubic_loss (struct lsquic_cubic *cubic) 164{ 165 LSQ_DEBUG("%s(cubic)", __func__); 166 cubic->cu_epoch_start = 0; 167 if (FAST_CONVERGENCE && cubic->cu_cwnd < cubic->cu_last_max_cwnd) 168 cubic->cu_last_max_cwnd = cubic->cu_cwnd * TWO_MINUS_BETA_OVER_TWO / 1024; 169 else 170 cubic->cu_last_max_cwnd = cubic->cu_cwnd; 171 cubic->cu_cwnd = cubic->cu_cwnd * ONE_MINUS_BETA / 1024; 172 cubic->cu_tcp_cwnd = cubic->cu_cwnd; 173 cubic->cu_ssthresh = cubic->cu_cwnd; 174 LSQ_INFO("loss detected, last_max_cwnd: %lu, cwnd: %lu", 175 cubic->cu_last_max_cwnd, cubic->cu_cwnd); 176 LOG_CWND(cubic); 177} 178 179 180void 181lsquic_cubic_timeout (struct lsquic_cubic *cubic) 182{ 183 LSQ_DEBUG("%s(cubic)", __func__); 184 cubic_reset(cubic); 185 cubic->cu_ssthresh = cubic->cu_cwnd; 186 cubic->cu_tcp_cwnd = cubic->cu_cwnd; 187 LSQ_INFO("timeout, cwnd: %lu", cubic->cu_cwnd); 188 LOG_CWND(cubic); 189} 190