Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet/cc/cc_cubic.c
Show First 20 Lines • Show All 90 Lines • ▼ Show 20 Lines | struct cubic { | ||||
/* Minimum observed rtt in ticks. */ | /* Minimum observed rtt in ticks. */ | ||||
int min_rtt_ticks; | int min_rtt_ticks; | ||||
/* Mean observed rtt between congestion epochs. */ | /* Mean observed rtt between congestion epochs. */ | ||||
int mean_rtt_ticks; | int mean_rtt_ticks; | ||||
/* ACKs since last congestion event. */ | /* ACKs since last congestion event. */ | ||||
int epoch_ack_count; | int epoch_ack_count; | ||||
/* Time of last congestion event in ticks. */ | /* Time of last congestion event in ticks. */ | ||||
int t_last_cong; | int t_last_cong; | ||||
/* Hybrid Slow Start values */ | |||||
/* Minimum observed rtt in ms. */ | |||||
int min_rtt_ms; | |||||
/* number of samples taken since start of packet train */ | |||||
uint8_t sample_count; | |||||
/* an early exit condition for hybrid slow start has been found */ | |||||
uint8_t exit_found; | |||||
/* start of hybrid slow start packet train */ | |||||
uint32_t train_start; | |||||
/* value of snd_nxt at reset - used to determine ack value of next reset */ | |||||
uint32_t end_seq; | |||||
/* millisecond granularity value of timestamp at the prior ack */ | |||||
uint32_t last_ack; | |||||
/* the smallest measured RTT within the first HYSTART_MIN_SAMPLES (8) */ | |||||
uint32_t curr_rtt; | |||||
/* assume hz == 1000 for first pass */ | |||||
#define min_rtt_ms min_rtt_ticks | |||||
}; | }; | ||||
#define USEC_PER_MSEC 1000 | |||||
#define HYSTART_ACK_TRAIN 0x1 | |||||
#define HYSTART_DELAY 0x2 | |||||
#define HYSTART_MIN_SAMPLES 8 | |||||
#define HYSTART_DELAY_MIN (4U<<3) | |||||
#define HYSTART_DELAY_MAX (16U<<3) | |||||
static inline int | |||||
clamp(int x, int min, int max) { | |||||
if (x < min) | |||||
return min; | |||||
if (x > max) | |||||
return max; | |||||
return x; | |||||
} | |||||
#define HYSTART_DELAY_THRESH(x) clamp(x, HYSTART_DELAY_MIN, HYSTART_DELAY_MAX) | |||||
SYSCTL_NODE(_net_inet_tcp_cc, OID_AUTO, cubic, CTLFLAG_RW, NULL, | |||||
"cubic congestion control related settings"); | |||||
//static int hystart = 1; | |||||
static int hystart_detect = HYSTART_ACK_TRAIN | HYSTART_DELAY; | |||||
static int hystart_low_window = 16; | |||||
static int hystart_ack_delta = 2; | |||||
static MALLOC_DEFINE(M_CUBIC, "cubic data", | static MALLOC_DEFINE(M_CUBIC, "cubic data", | ||||
"Per connection data required for the CUBIC congestion control algorithm"); | "Per connection data required for the CUBIC congestion control algorithm"); | ||||
struct cc_algo cubic_cc_algo = { | struct cc_algo cubic_cc_algo = { | ||||
.name = "cubic", | .name = "cubic", | ||||
.ack_received = cubic_ack_received, | .ack_received = cubic_ack_received, | ||||
.cb_destroy = cubic_cb_destroy, | .cb_destroy = cubic_cb_destroy, | ||||
.cb_init = cubic_cb_init, | .cb_init = cubic_cb_init, | ||||
▲ Show 20 Lines • Show All 87 Lines • ▼ Show 20 Lines | cubic_cb_destroy(struct cc_var *ccv) | ||||
if (ccv->cc_data != NULL) | if (ccv->cc_data != NULL) | ||||
free(ccv->cc_data, M_CUBIC); | free(ccv->cc_data, M_CUBIC); | ||||
} | } | ||||
static int | static int | ||||
cubic_cb_init(struct cc_var *ccv) | cubic_cb_init(struct cc_var *ccv) | ||||
{ | { | ||||
struct cubic *cubic_data; | struct cubic *cubic_data = ccv->cc_data; | ||||
if (cubic_data == NULL) | |||||
cubic_data = malloc(sizeof(struct cubic), M_CUBIC, M_NOWAIT|M_ZERO); | cubic_data = malloc(sizeof(struct cubic), M_CUBIC, M_NOWAIT|M_ZERO); | ||||
if (cubic_data == NULL) | if (cubic_data == NULL) | ||||
return (ENOMEM); | return (ENOMEM); | ||||
/* Init some key variables with sensible defaults. */ | /* Init some key variables with sensible defaults. */ | ||||
cubic_data->t_last_cong = ticks; | cubic_data->t_last_cong = ticks; | ||||
cubic_data->min_rtt_ticks = TCPTV_SRTTBASE; | cubic_data->min_rtt_ticks = TCPTV_SRTTBASE; | ||||
cubic_data->mean_rtt_ticks = 1; | cubic_data->mean_rtt_ticks = 1; | ||||
ccv->cc_data = cubic_data; | ccv->cc_data = cubic_data; | ||||
return (0); | return (0); | ||||
} | } | ||||
static inline uint32_t | |||||
cubic_clock(void) | |||||
{ | |||||
if (hz == 1000) | |||||
return (ticks); | |||||
return (((ticks + hz) * 1000)/hz); | |||||
} | |||||
static void | |||||
cubic_hystart_init(struct cc_var *ccv) | |||||
{ | |||||
struct cubic *cd; | |||||
cd = ccv->cc_data; | |||||
cd->train_start = cd->last_ack = cubic_clock(); | |||||
cd->end_seq = CCV(ccv, snd_nxt); | |||||
cd->curr_rtt = 0; | |||||
cd->sample_count = cd->exit_found = 0; | |||||
} | |||||
static void | |||||
cubic_hystart_update(struct cc_var *ccv, uint32_t delay) | |||||
{ | |||||
struct cubic *cd; | |||||
uint32_t now; | |||||
cd = ccv->cc_data; | |||||
if (cd->exit_found & hystart_detect) | |||||
return; | |||||
if (CCV(ccv, snd_cwnd) < hystart_low_window) | |||||
return; | |||||
if (hystart_detect & HYSTART_ACK_TRAIN) { | |||||
now = cubic_clock(); | |||||
if ((int32_t)(now - cd->last_ack) <= hystart_ack_delta) { | |||||
cd->last_ack = now; | |||||
if ((int32_t)(now - cd->train_start) > cd->min_rtt_ms >> 4) { | |||||
cd->exit_found |= HYSTART_ACK_TRAIN; | |||||
CCV(ccv, snd_ssthresh) = CCV(ccv, snd_cwnd); | |||||
} | |||||
} | |||||
} | |||||
if (hystart_detect & HYSTART_DELAY) { | |||||
if (cd->sample_count < HYSTART_MIN_SAMPLES) { | |||||
if (cd->curr_rtt == 0 || cd->curr_rtt > delay) | |||||
cd->curr_rtt = delay; | |||||
cd->sample_count++; | |||||
} else { | |||||
if (cd->curr_rtt > cd->min_rtt_ms + HYSTART_DELAY_THRESH(cd->min_rtt_ms >> 3)) { | |||||
cd->exit_found |= HYSTART_DELAY; | |||||
CCV(ccv, snd_ssthresh) = CCV(ccv, snd_cwnd); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
/* | /* | ||||
* Perform any necessary tasks before we enter congestion recovery. | * Perform any necessary tasks before we enter congestion recovery. | ||||
*/ | */ | ||||
static void | static void | ||||
cubic_cong_signal(struct cc_var *ccv, uint32_t type) | cubic_cong_signal(struct cc_var *ccv, uint32_t type) | ||||
{ | { | ||||
struct cubic *cubic_data; | struct cubic *cubic_data; | ||||
uint32_t cwin; | uint32_t cwin; | ||||
▲ Show 20 Lines • Show All 126 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Record the min RTT and sum samples for the epoch average RTT calculation. | * Record the min RTT and sum samples for the epoch average RTT calculation. | ||||
*/ | */ | ||||
static void | static void | ||||
cubic_record_rtt(struct cc_var *ccv) | cubic_record_rtt(struct cc_var *ccv) | ||||
{ | { | ||||
struct cubic *cubic_data; | struct cubic *cubic_data; | ||||
int t_srtt_ticks; | int t_srtt_ticks; | ||||
int rtt_us, rtt_s3_ms; | |||||
cubic_data = ccv->cc_data; | |||||
if (ccv->sample_rtt_us >= 0) { | |||||
rtt_us = ccv->sample_rtt_us; | |||||
rtt_s3_ms = (rtt_us << 3)/USEC_PER_MSEC; | |||||
if (SEQ_GT(ccv->curack, cubic_data->end_seq)) | |||||
cubic_hystart_init(ccv); | |||||
cubic_hystart_update(ccv, rtt_s3_ms); | |||||
} | |||||
/* Ignore srtt until a min number of samples have been taken. */ | /* Ignore srtt until a min number of samples have been taken. */ | ||||
if (CCV(ccv, t_rttupdated) >= CUBIC_MIN_RTT_SAMPLES) { | if (CCV(ccv, t_rttupdated) >= CUBIC_MIN_RTT_SAMPLES) { | ||||
cubic_data = ccv->cc_data; | |||||
t_srtt_ticks = CCV(ccv, t_srtt) / TCP_RTT_SCALE; | t_srtt_ticks = CCV(ccv, t_srtt) / TCP_RTT_SCALE; | ||||
/* | /* | ||||
* Record the current SRTT as our minrtt if it's the smallest | * Record the current SRTT as our minrtt if it's the smallest | ||||
* we've seen or minrtt is currently equal to its initialised | * we've seen or minrtt is currently equal to its initialised | ||||
* value. | * value. | ||||
* | * | ||||
* XXXLAS: Should there be some hysteresis for minrtt? | * XXXLAS: Should there be some hysteresis for minrtt? | ||||
▲ Show 20 Lines • Show All 46 Lines • Show Last 20 Lines |