2021-01-06 14:00:05 +00:00
|
|
|
/* Copyright (c) 2017 - 2021 LiteSpeed Technologies Inc. See LICENSE. */
|
2017-09-22 21:00:03 +00:00
|
|
|
/*
|
|
|
|
* lsquic_cubic.c -- LSQUIC CUBIC implementation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2019-09-11 15:27:58 +00:00
|
|
|
#include <sys/queue.h>
|
2018-03-12 22:25:01 +00:00
|
|
|
#ifdef WIN32
|
|
|
|
#include <vc_compat.h>
|
|
|
|
#endif
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
#include "lsquic_int_types.h"
|
|
|
|
#include "lsquic_types.h"
|
2019-09-11 15:27:58 +00:00
|
|
|
#include "lsquic_hash.h"
|
2017-09-22 21:00:03 +00:00
|
|
|
#include "lsquic_util.h"
|
2019-09-11 15:27:58 +00:00
|
|
|
#include "lsquic_cong_ctl.h"
|
|
|
|
#include "lsquic_sfcw.h"
|
|
|
|
#include "lsquic_conn_flow.h"
|
|
|
|
#include "lsquic_varint.h"
|
|
|
|
#include "lsquic_hq.h"
|
|
|
|
#include "lsquic_stream.h"
|
|
|
|
#include "lsquic_rtt.h"
|
|
|
|
#include "lsquic_conn_public.h"
|
|
|
|
#include "lsquic_packet_common.h"
|
|
|
|
#include "lsquic_packet_out.h"
|
|
|
|
#include "lsquic_cubic.h"
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
#define LSQUIC_LOGGER_MODULE LSQLM_CUBIC
|
2019-09-11 15:27:58 +00:00
|
|
|
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(cubic->cu_conn)
|
2017-09-22 21:00:03 +00:00
|
|
|
#include "lsquic_logger.h"
|
|
|
|
|
|
|
|
#define FAST_CONVERGENCE 1
|
|
|
|
#define BETA 205 /* 205/1024 */
|
|
|
|
#define C 410 /* 410/1024 */
|
|
|
|
#define TWO_MINUS_BETA_OVER_TWO 922 /* 922/1024 */
|
|
|
|
#define ONE_MINUS_BETA 819 /* 819/1024 */
|
|
|
|
#define ONE_OVER_C 2560 /* 2560/1024 */
|
|
|
|
|
|
|
|
static void
|
|
|
|
cubic_reset (struct lsquic_cubic *cubic)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
memset(cubic, 0, offsetof(struct lsquic_cubic, cu_conn));
|
2018-02-26 21:01:16 +00:00
|
|
|
cubic->cu_cwnd = 32 * TCP_MSS;
|
|
|
|
cubic->cu_last_max_cwnd = 32 * TCP_MSS;
|
|
|
|
cubic->cu_tcp_cwnd = 32 * TCP_MSS;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2018-02-26 21:01:16 +00:00
|
|
|
cubic_update (struct lsquic_cubic *cubic, lsquic_time_t now, unsigned n_bytes)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2018-02-26 21:01:16 +00:00
|
|
|
double delta_t, t;
|
|
|
|
lsquic_time_t target;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
if (0 == cubic->cu_epoch_start)
|
|
|
|
{
|
|
|
|
cubic->cu_epoch_start = now;
|
|
|
|
if (cubic->cu_cwnd < cubic->cu_last_max_cwnd)
|
|
|
|
{
|
2018-02-26 21:01:16 +00:00
|
|
|
cubic->cu_K = cbrt(cubic->cu_last_max_cwnd / TCP_MSS / 2);
|
2017-09-22 21:00:03 +00:00
|
|
|
cubic->cu_origin_point = cubic->cu_last_max_cwnd;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cubic->cu_K = 0;
|
|
|
|
cubic->cu_origin_point = cubic->cu_cwnd;
|
|
|
|
}
|
2018-02-26 21:01:16 +00:00
|
|
|
LSQ_DEBUG("cwnd: %lu; last_max_cwnd: %lu; K: %lf; origin_point: %lu",
|
|
|
|
cubic->cu_cwnd, cubic->cu_last_max_cwnd, cubic->cu_K, cubic->cu_origin_point);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
2018-02-26 21:01:16 +00:00
|
|
|
delta_t = (double) (now + cubic->cu_min_delay - cubic->cu_epoch_start) / 1000000;
|
2017-09-22 21:00:03 +00:00
|
|
|
if (delta_t < cubic->cu_K)
|
|
|
|
{
|
|
|
|
t = cubic->cu_K - delta_t;
|
2018-02-26 21:01:16 +00:00
|
|
|
target = cubic->cu_origin_point - t * t * t * 0.4 * TCP_MSS;
|
|
|
|
LSQ_DEBUG("delta_t: %lf; t: %lf; target 1: %"PRIu64, delta_t, t, target);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
t = delta_t - cubic->cu_K;
|
2018-02-26 21:01:16 +00:00
|
|
|
target = cubic->cu_origin_point + t * t * t * 0.4 * TCP_MSS;
|
|
|
|
LSQ_DEBUG("target 2: %"PRIu64, target);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cubic->cu_flags & CU_TCP_FRIENDLY)
|
|
|
|
{
|
|
|
|
cubic->cu_tcp_cwnd += n_bytes * TCP_MSS * ONE_MINUS_BETA / 1024
|
|
|
|
/ cubic->cu_tcp_cwnd;
|
|
|
|
LSQ_DEBUG("delta_t: %lf; last_max: %lu; cu_tcp_cwnd: %lu; target: "
|
|
|
|
"%"PRIu64"; over: %d; left: %d", delta_t, cubic->cu_last_max_cwnd,
|
|
|
|
cubic->cu_tcp_cwnd, target, cubic->cu_tcp_cwnd > target,
|
|
|
|
delta_t < cubic->cu_K);
|
|
|
|
if (cubic->cu_tcp_cwnd > target)
|
|
|
|
target = cubic->cu_tcp_cwnd;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (target == 0)
|
2018-02-26 21:01:16 +00:00
|
|
|
target = TCP_MSS;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
cubic->cu_cwnd = target;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_cubic_set_flags (struct lsquic_cubic *cubic, enum cubic_flags flags)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("%s(cubic, 0x%X)", __func__, flags);
|
|
|
|
cubic->cu_flags = flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
lsquic_cubic_init (void *cong_ctl, const struct lsquic_conn_public *conn_pub,
|
|
|
|
enum quic_ft_bit UNUSED_retx_frames)
|
|
|
|
{
|
|
|
|
struct lsquic_cubic *const cubic = cong_ctl;
|
2017-09-22 21:00:03 +00:00
|
|
|
cubic_reset(cubic);
|
2018-02-26 21:01:16 +00:00
|
|
|
cubic->cu_ssthresh = 10000 * TCP_MSS; /* Emulate "unbounded" slow start */
|
2019-09-11 15:27:58 +00:00
|
|
|
cubic->cu_conn = conn_pub->lconn;
|
|
|
|
cubic->cu_rtt_stats = &conn_pub->rtt_stats;
|
|
|
|
cubic->cu_flags = DEFAULT_CUBIC_FLAGS;
|
2017-09-22 21:00:03 +00:00
|
|
|
#ifndef NDEBUG
|
2018-02-26 21:01:16 +00:00
|
|
|
const char *s;
|
|
|
|
s = getenv("LSQUIC_CUBIC_SAMPLING_RATE");
|
|
|
|
if (s)
|
|
|
|
cubic->cu_sampling_rate = atoi(s);
|
|
|
|
else
|
2017-09-22 21:00:03 +00:00
|
|
|
#endif
|
2018-02-26 21:01:16 +00:00
|
|
|
cubic->cu_sampling_rate = 100000;
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("%s(cubic, $conn)", __func__);
|
2017-09-22 21:00:03 +00:00
|
|
|
LSQ_INFO("initialized");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-01-28 14:35:09 +00:00
|
|
|
static void
|
|
|
|
lsquic_cubic_reinit (void *cong_ctl)
|
|
|
|
{
|
|
|
|
struct lsquic_cubic *const cubic = cong_ctl;
|
|
|
|
cubic_reset(cubic);
|
|
|
|
cubic->cu_ssthresh = 10000 * TCP_MSS; /* Emulate "unbounded" slow start */
|
|
|
|
LSQ_DEBUG("re-initialized");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
#define LOG_CWND(c) do { \
|
|
|
|
if (LSQ_LOG_ENABLED(LSQ_LOG_INFO)) { \
|
|
|
|
lsquic_time_t now = lsquic_time_now(); \
|
2018-02-26 21:01:16 +00:00
|
|
|
now -= now % (c)->cu_sampling_rate; \
|
2017-09-22 21:00:03 +00:00
|
|
|
if (now > (c)->cu_last_logged) { \
|
2018-02-26 21:01:16 +00:00
|
|
|
LSQ_INFO("CWND: %lu", (c)->cu_cwnd); \
|
2017-09-22 21:00:03 +00:00
|
|
|
(c)->cu_last_logged = now; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
2018-02-26 21:01:16 +00:00
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static void
|
|
|
|
lsquic_cubic_was_quiet (void *cong_ctl, lsquic_time_t now, uint64_t in_flight)
|
2018-02-26 21:01:16 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
struct lsquic_cubic *const cubic = cong_ctl;
|
2018-02-26 21:01:16 +00:00
|
|
|
LSQ_DEBUG("%s(cubic, %"PRIu64")", __func__, now);
|
|
|
|
cubic->cu_epoch_start = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static void
|
|
|
|
lsquic_cubic_ack (void *cong_ctl, struct lsquic_packet_out *packet_out,
|
|
|
|
unsigned n_bytes, lsquic_time_t now_time, int app_limited)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
struct lsquic_cubic *const cubic = cong_ctl;
|
|
|
|
lsquic_time_t rtt;
|
|
|
|
|
|
|
|
rtt = now_time - packet_out->po_sent;
|
2018-03-12 22:25:01 +00:00
|
|
|
LSQ_DEBUG("%s(cubic, %"PRIu64", %"PRIu64", %d, %u)", __func__, now_time, rtt,
|
2018-02-26 21:01:16 +00:00
|
|
|
app_limited, n_bytes);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (0 == cubic->cu_min_delay || rtt < cubic->cu_min_delay)
|
|
|
|
{
|
|
|
|
cubic->cu_min_delay = rtt;
|
|
|
|
LSQ_INFO("min_delay: %"PRIu64, rtt);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cubic->cu_cwnd <= cubic->cu_ssthresh)
|
|
|
|
{
|
2018-02-26 21:01:16 +00:00
|
|
|
cubic->cu_cwnd += TCP_MSS;
|
|
|
|
LSQ_DEBUG("ACK: slow threshold, cwnd: %lu", cubic->cu_cwnd);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
2018-02-26 21:01:16 +00:00
|
|
|
else if (!app_limited)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2018-03-12 22:25:01 +00:00
|
|
|
cubic_update(cubic, now_time, n_bytes);
|
2018-02-26 21:01:16 +00:00
|
|
|
LSQ_DEBUG("ACK: cwnd: %lu", cubic->cu_cwnd);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
2018-02-26 21:01:16 +00:00
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
LOG_CWND(cubic);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static void
|
|
|
|
lsquic_cubic_loss (void *cong_ctl)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
struct lsquic_cubic *const cubic = cong_ctl;
|
2017-09-22 21:00:03 +00:00
|
|
|
LSQ_DEBUG("%s(cubic)", __func__);
|
|
|
|
cubic->cu_epoch_start = 0;
|
|
|
|
if (FAST_CONVERGENCE && cubic->cu_cwnd < cubic->cu_last_max_cwnd)
|
|
|
|
cubic->cu_last_max_cwnd = cubic->cu_cwnd * TWO_MINUS_BETA_OVER_TWO / 1024;
|
|
|
|
else
|
|
|
|
cubic->cu_last_max_cwnd = cubic->cu_cwnd;
|
|
|
|
cubic->cu_cwnd = cubic->cu_cwnd * ONE_MINUS_BETA / 1024;
|
2018-02-26 21:01:16 +00:00
|
|
|
cubic->cu_tcp_cwnd = cubic->cu_cwnd;
|
2017-09-22 21:00:03 +00:00
|
|
|
cubic->cu_ssthresh = cubic->cu_cwnd;
|
2018-02-26 21:01:16 +00:00
|
|
|
LSQ_INFO("loss detected, last_max_cwnd: %lu, cwnd: %lu",
|
2017-09-22 21:00:03 +00:00
|
|
|
cubic->cu_last_max_cwnd, cubic->cu_cwnd);
|
|
|
|
LOG_CWND(cubic);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static void
|
|
|
|
lsquic_cubic_timeout (void *cong_ctl)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
struct lsquic_cubic *const cubic = cong_ctl;
|
2019-02-18 13:40:51 +00:00
|
|
|
unsigned long cwnd;
|
|
|
|
|
|
|
|
cwnd = cubic->cu_cwnd;
|
2017-09-22 21:00:03 +00:00
|
|
|
LSQ_DEBUG("%s(cubic)", __func__);
|
|
|
|
cubic_reset(cubic);
|
2019-02-18 13:40:51 +00:00
|
|
|
cubic->cu_ssthresh = cwnd / 2;
|
|
|
|
cubic->cu_tcp_cwnd = 2 * TCP_MSS;
|
|
|
|
cubic->cu_cwnd = 2 * TCP_MSS;
|
2018-02-26 21:01:16 +00:00
|
|
|
LSQ_INFO("timeout, cwnd: %lu", cubic->cu_cwnd);
|
2017-09-22 21:00:03 +00:00
|
|
|
LOG_CWND(cubic);
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
lsquic_cubic_cleanup (void *cong_ctl)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static uint64_t
|
|
|
|
lsquic_cubic_get_cwnd (void *cong_ctl)
|
|
|
|
{
|
|
|
|
struct lsquic_cubic *const cubic = cong_ctl;
|
|
|
|
return cubic->cu_cwnd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
in_slow_start (void *cong_ctl)
|
|
|
|
{
|
|
|
|
struct lsquic_cubic *const cubic = cong_ctl;
|
|
|
|
return cubic->cu_cwnd < cubic->cu_ssthresh;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static uint64_t
|
|
|
|
lsquic_cubic_pacing_rate (void *cong_ctl, int in_recovery)
|
|
|
|
{
|
|
|
|
struct lsquic_cubic *const cubic = cong_ctl;
|
|
|
|
uint64_t bandwidth, pacing_rate;
|
|
|
|
lsquic_time_t srtt;
|
|
|
|
|
|
|
|
srtt = lsquic_rtt_stats_get_srtt(cubic->cu_rtt_stats);
|
|
|
|
if (srtt == 0)
|
|
|
|
srtt = 50000;
|
|
|
|
bandwidth = cubic->cu_cwnd * 1000000 / srtt;
|
|
|
|
if (in_slow_start(cubic))
|
|
|
|
pacing_rate = bandwidth * 2;
|
|
|
|
else if (in_recovery)
|
|
|
|
pacing_rate = bandwidth;
|
|
|
|
else
|
|
|
|
pacing_rate = bandwidth + bandwidth / 4;
|
|
|
|
|
|
|
|
return pacing_rate;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const struct cong_ctl_if lsquic_cong_cubic_if =
|
|
|
|
{
|
|
|
|
.cci_ack = lsquic_cubic_ack,
|
|
|
|
.cci_cleanup = lsquic_cubic_cleanup,
|
|
|
|
.cci_get_cwnd = lsquic_cubic_get_cwnd,
|
|
|
|
.cci_init = lsquic_cubic_init,
|
|
|
|
.cci_pacing_rate = lsquic_cubic_pacing_rate,
|
|
|
|
.cci_loss = lsquic_cubic_loss,
|
2020-01-28 14:35:09 +00:00
|
|
|
.cci_reinit = lsquic_cubic_reinit,
|
2019-09-11 15:27:58 +00:00
|
|
|
.cci_timeout = lsquic_cubic_timeout,
|
|
|
|
.cci_was_quiet = lsquic_cubic_was_quiet,
|
|
|
|
};
|