lsquic_cubic.c revision 50aadb33
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;
33    cubic->cu_last_max_cwnd = 32;
34}
35
36
37static void
38cubic_update (struct lsquic_cubic *cubic, lsquic_time_t now)
39{
40    lsquic_time_t delta_t, t, target;
41    unsigned tcp_cwnd;
42
43    if (0 == cubic->cu_epoch_start)
44    {
45        cubic->cu_epoch_start = now;
46        if (cubic->cu_cwnd < cubic->cu_last_max_cwnd)
47        {
48            cubic->cu_K = cbrt((cubic->cu_last_max_cwnd - cubic->cu_cwnd) *
49                                                ONE_OVER_C / 1024) * 1000000;
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    }
58    else if ((cubic->cu_flags & CU_SHIFT_EPOCH) && cubic->cu_app_limited)
59    {
60        LSQ_DEBUG("increment epoch_start by %"PRIu64" microseconds", now - cubic->cu_app_limited);
61        cubic->cu_epoch_start += now - cubic->cu_app_limited;
62    }
63
64    delta_t = now + cubic->cu_min_delay - cubic->cu_epoch_start;
65    if (delta_t < cubic->cu_K)
66    {
67        t = cubic->cu_K - delta_t;
68        t /= 62500;
69        target = cubic->cu_origin_point - C * t * t * t / 1024 / 4096;
70    }
71    else
72    {
73        t = delta_t - cubic->cu_K;
74        t /= 62500;
75        target = cubic->cu_origin_point + C * t * t * t / 1024 / 4096;
76        if (cubic->cu_flags & CU_TCP_FRIENDLY)
77        {
78            tcp_cwnd = cubic->cu_last_max_cwnd * ONE_MINUS_BETA / 1024 +
79                    (delta_t - cubic->cu_K) * C / 1024 / cubic->cu_min_delay;
80            if (tcp_cwnd > target)
81                target = tcp_cwnd;
82        }
83    }
84
85    if (target == 0)
86        target = 1;
87
88    cubic->cu_cwnd = target;
89}
90
91
92void
93lsquic_cubic_init_ext (struct lsquic_cubic *cubic, lsquic_cid_t cid,
94                                                        enum cubic_flags flags)
95{
96    cubic_reset(cubic);
97    cubic->cu_ssthresh = 10000;         /* Emulate "unbounded" slow start */
98    cubic->cu_cid   = cid;
99    cubic->cu_flags = flags;
100    LSQ_DEBUG("%s(cubic, %"PRIu64", 0x%X)", __func__, cid, flags);
101#ifndef NDEBUG
102    {
103        const char *shift;
104        shift = getenv("LSQUIC_CUBIC_SHIFT_EPOCH");
105        if (shift)
106        {
107            if (atoi(shift))
108                cubic->cu_flags |=  CU_SHIFT_EPOCH;
109            else
110                cubic->cu_flags &= ~CU_SHIFT_EPOCH;
111        }
112    }
113#endif
114    LSQ_INFO("initialized");
115}
116
117
118#define LOG_CWND(c) do {                                                    \
119    if (LSQ_LOG_ENABLED(LSQ_LOG_INFO)) {                                    \
120        lsquic_time_t now = lsquic_time_now();                              \
121        now -= now % 100000;                                                \
122        if (now > (c)->cu_last_logged) {                                    \
123            LSQ_INFO("CWND: %u", (c)->cu_cwnd);                             \
124            (c)->cu_last_logged = now;                                      \
125        }                                                                   \
126    }                                                                       \
127} while (0)
128
129void
130lsquic_cubic_ack (struct lsquic_cubic *cubic, lsquic_time_t now,
131                  lsquic_time_t rtt, int app_limited)
132{
133    LSQ_DEBUG("%s(cubic, %"PRIu64", %"PRIu64", %d)", __func__, now, rtt,
134                                                                app_limited);
135    if (0 == cubic->cu_min_delay || rtt < cubic->cu_min_delay)
136    {
137        cubic->cu_min_delay = rtt;
138        LSQ_INFO("min_delay: %"PRIu64, rtt);
139    }
140
141    if (cubic->cu_cwnd <= cubic->cu_ssthresh)
142    {
143        ++cubic->cu_cwnd;
144        LSQ_DEBUG("ACK: slow threshold, cwnd: %u", cubic->cu_cwnd);
145    }
146    else
147    {
148        if (app_limited)
149        {
150            if (cubic->cu_flags & CU_SHIFT_EPOCH)
151            {
152                if (0 == cubic->cu_app_limited)
153                {
154                    cubic->cu_app_limited = now;
155                    LSQ_DEBUG("set app_limited to %"PRIu64, now);
156                }
157            }
158            else
159                cubic->cu_epoch_start = 0;
160        }
161        else
162        {
163            cubic_update(cubic, now);
164            cubic->cu_app_limited = 0;
165        }
166        LSQ_DEBUG("ACK: cwnd: %u", cubic->cu_cwnd);
167    }
168    LOG_CWND(cubic);
169}
170
171
172void
173lsquic_cubic_loss (struct lsquic_cubic *cubic)
174{
175    LSQ_DEBUG("%s(cubic)", __func__);
176    cubic->cu_epoch_start = 0;
177    cubic->cu_app_limited = 0;
178    if (FAST_CONVERGENCE && cubic->cu_cwnd < cubic->cu_last_max_cwnd)
179        cubic->cu_last_max_cwnd = cubic->cu_cwnd * TWO_MINUS_BETA_OVER_TWO / 1024;
180    else
181        cubic->cu_last_max_cwnd = cubic->cu_cwnd;
182    cubic->cu_cwnd = cubic->cu_cwnd * ONE_MINUS_BETA / 1024;
183    cubic->cu_ssthresh = cubic->cu_cwnd;
184    LSQ_INFO("loss detected, last_max_cwnd: %u, cwnd: %u",
185        cubic->cu_last_max_cwnd, cubic->cu_cwnd);
186    LOG_CWND(cubic);
187}
188
189
190void
191lsquic_cubic_timeout (struct lsquic_cubic *cubic)
192{
193    LSQ_DEBUG("%s(cubic)", __func__);
194    cubic_reset(cubic);
195    cubic->cu_ssthresh = cubic->cu_cwnd;
196    LSQ_INFO("timeout, cwnd: %u", cubic->cu_cwnd);
197    LOG_CWND(cubic);
198}
199