Changeset View
Changeset View
Standalone View
Standalone View
head/sys/netinet/cc/cc_cubic.c
Show First 20 Lines • Show All 82 Lines • ▼ Show 20 Lines | struct cubic { | ||||
/* Cubic K in fixed point form with CUBIC_SHIFT worth of precision. */ | /* Cubic K in fixed point form with CUBIC_SHIFT worth of precision. */ | ||||
int64_t K; | int64_t K; | ||||
/* Sum of RTT samples across an epoch in ticks. */ | /* Sum of RTT samples across an epoch in ticks. */ | ||||
int64_t sum_rtt_ticks; | int64_t sum_rtt_ticks; | ||||
/* cwnd at the most recent congestion event. */ | /* cwnd at the most recent congestion event. */ | ||||
unsigned long max_cwnd; | unsigned long max_cwnd; | ||||
/* cwnd at the previous congestion event. */ | /* cwnd at the previous congestion event. */ | ||||
unsigned long prev_max_cwnd; | unsigned long prev_max_cwnd; | ||||
/* Cached value for t_maxseg when K was computed */ | |||||
uint32_t k_maxseg; | |||||
/* Number of congestion events. */ | /* Number of congestion events. */ | ||||
uint32_t num_cong_events; | uint32_t num_cong_events; | ||||
/* 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; | ||||
Show All 20 Lines | |||||
{ | { | ||||
struct cubic *cubic_data; | struct cubic *cubic_data; | ||||
unsigned long w_tf, w_cubic_next; | unsigned long w_tf, w_cubic_next; | ||||
int ticks_since_cong; | int ticks_since_cong; | ||||
cubic_data = ccv->cc_data; | cubic_data = ccv->cc_data; | ||||
cubic_record_rtt(ccv); | cubic_record_rtt(ccv); | ||||
if (ccv->flags & CCF_MAX_CWND) | |||||
return; | |||||
/* | /* | ||||
* Regular ACK and we're not in cong/fast recovery and we're cwnd | * Regular ACK and we're not in cong/fast recovery and we're cwnd | ||||
* limited and we're either not doing ABC or are slow starting or are | * limited and we're either not doing ABC or are slow starting or are | ||||
* doing ABC and we've sent a cwnd's worth of bytes. | * doing ABC and we've sent a cwnd's worth of bytes. | ||||
*/ | */ | ||||
if (type == CC_ACK && !IN_RECOVERY(CCV(ccv, t_flags)) && | if (type == CC_ACK && !IN_RECOVERY(CCV(ccv, t_flags)) && | ||||
(ccv->flags & CCF_CWND_LIMITED) && (!V_tcp_do_rfc3465 || | (ccv->flags & CCF_CWND_LIMITED) && (!V_tcp_do_rfc3465 || | ||||
CCV(ccv, snd_cwnd) <= CCV(ccv, snd_ssthresh) || | CCV(ccv, snd_cwnd) <= CCV(ccv, snd_ssthresh) || | ||||
Show All 11 Lines | else { | ||||
* causes w_tf to grow much faster than it should if the | * causes w_tf to grow much faster than it should if the | ||||
* RTT is dominated by network buffering rather than | * RTT is dominated by network buffering rather than | ||||
* propagation delay. | * propagation delay. | ||||
*/ | */ | ||||
w_tf = tf_cwnd(ticks_since_cong, | w_tf = tf_cwnd(ticks_since_cong, | ||||
cubic_data->mean_rtt_ticks, cubic_data->max_cwnd, | cubic_data->mean_rtt_ticks, cubic_data->max_cwnd, | ||||
CCV(ccv, t_maxseg)); | CCV(ccv, t_maxseg)); | ||||
if (ccv->flags & CCF_CHG_MAX_CWND || cubic_data->k_maxseg != CCV(ccv, t_maxseg)) { | |||||
cubic_data->K = cubic_k(cubic_data->max_cwnd / CCV(ccv, t_maxseg)); | |||||
cubic_data->k_maxseg = CCV(ccv, t_maxseg); | |||||
ccv->flags &= ~(CCF_MAX_CWND|CCF_CHG_MAX_CWND); | |||||
} | |||||
w_cubic_next = cubic_cwnd(ticks_since_cong + | w_cubic_next = cubic_cwnd(ticks_since_cong + | ||||
cubic_data->mean_rtt_ticks, cubic_data->max_cwnd, | cubic_data->mean_rtt_ticks, cubic_data->max_cwnd, | ||||
CCV(ccv, t_maxseg), cubic_data->K); | CCV(ccv, t_maxseg), cubic_data->K); | ||||
ccv->flags &= ~CCF_ABC_SENTAWND; | ccv->flags &= ~CCF_ABC_SENTAWND; | ||||
if (w_cubic_next < w_tf) | if (w_cubic_next < w_tf) | ||||
/* | /* | ||||
* TCP-friendly region, follow tf | * TCP-friendly region, follow tf | ||||
* cwnd growth. | * cwnd growth. | ||||
*/ | */ | ||||
CCV(ccv, snd_cwnd) = w_tf; | CCV(ccv, snd_cwnd) = ulmin(w_tf, TCP_MAXWIN << CCV(ccv, snd_scale)); | ||||
else if (CCV(ccv, snd_cwnd) < w_cubic_next) { | else if (CCV(ccv, snd_cwnd) < w_cubic_next) { | ||||
/* | /* | ||||
* Concave or convex region, follow CUBIC | * Concave or convex region, follow CUBIC | ||||
* cwnd growth. | * cwnd growth. | ||||
*/ | */ | ||||
if (w_cubic_next >= TCP_MAXWIN << CCV(ccv, snd_scale)) { | |||||
w_cubic_next = TCP_MAXWIN << CCV(ccv, snd_scale); | |||||
ccv->flags |= CCF_MAX_CWND; | |||||
} | |||||
w_cubic_next = ulmin(w_cubic_next, TCP_MAXWIN << CCV(ccv, snd_scale)); | |||||
if (V_tcp_do_rfc3465) | if (V_tcp_do_rfc3465) | ||||
CCV(ccv, snd_cwnd) = w_cubic_next; | CCV(ccv, snd_cwnd) = w_cubic_next; | ||||
else | else | ||||
CCV(ccv, snd_cwnd) += ((w_cubic_next - | CCV(ccv, snd_cwnd) += ((w_cubic_next - | ||||
CCV(ccv, snd_cwnd)) * | CCV(ccv, snd_cwnd)) * | ||||
CCV(ccv, t_maxseg)) / | CCV(ccv, t_maxseg)) / | ||||
CCV(ccv, snd_cwnd); | CCV(ccv, snd_cwnd); | ||||
} | } | ||||
/* | /* | ||||
* If we're not in slow start and we're probing for a | * If we're not in slow start and we're probing for a | ||||
* new cwnd limit at the start of a connection | * new cwnd limit at the start of a connection | ||||
* (happens when hostcache has a relevant entry), | * (happens when hostcache has a relevant entry), | ||||
* keep updating our current estimate of the | * keep updating our current estimate of the | ||||
* max_cwnd. | * max_cwnd. | ||||
*/ | */ | ||||
if (cubic_data->num_cong_events == 0 && | if (cubic_data->num_cong_events == 0 && | ||||
cubic_data->max_cwnd < CCV(ccv, snd_cwnd)) | cubic_data->max_cwnd < CCV(ccv, snd_cwnd)) { | ||||
cubic_data->max_cwnd = CCV(ccv, snd_cwnd); | cubic_data->max_cwnd = CCV(ccv, snd_cwnd); | ||||
ccv->flags |= CCF_CHG_MAX_CWND; | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} | |||||
static void | static void | ||||
cubic_cb_destroy(struct cc_var *ccv) | 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); | ||||
} | } | ||||
Show All 31 Lines | cubic_cong_signal(struct cc_var *ccv, uint32_t type) | ||||
switch (type) { | switch (type) { | ||||
case CC_NDUPACK: | case CC_NDUPACK: | ||||
if (!IN_FASTRECOVERY(CCV(ccv, t_flags))) { | if (!IN_FASTRECOVERY(CCV(ccv, t_flags))) { | ||||
if (!IN_CONGRECOVERY(CCV(ccv, t_flags))) { | if (!IN_CONGRECOVERY(CCV(ccv, t_flags))) { | ||||
cubic_ssthresh_update(ccv); | cubic_ssthresh_update(ccv); | ||||
cubic_data->num_cong_events++; | cubic_data->num_cong_events++; | ||||
cubic_data->prev_max_cwnd = cubic_data->max_cwnd; | cubic_data->prev_max_cwnd = cubic_data->max_cwnd; | ||||
cubic_data->max_cwnd = CCV(ccv, snd_cwnd); | cubic_data->max_cwnd = CCV(ccv, snd_cwnd); | ||||
ccv->flags |= CCF_CHG_MAX_CWND; | |||||
} | } | ||||
ENTER_RECOVERY(CCV(ccv, t_flags)); | ENTER_RECOVERY(CCV(ccv, t_flags)); | ||||
} | } | ||||
break; | break; | ||||
case CC_ECN: | case CC_ECN: | ||||
if (!IN_CONGRECOVERY(CCV(ccv, t_flags))) { | if (!IN_CONGRECOVERY(CCV(ccv, t_flags))) { | ||||
cubic_ssthresh_update(ccv); | cubic_ssthresh_update(ccv); | ||||
cubic_data->num_cong_events++; | cubic_data->num_cong_events++; | ||||
cubic_data->prev_max_cwnd = cubic_data->max_cwnd; | cubic_data->prev_max_cwnd = cubic_data->max_cwnd; | ||||
cubic_data->max_cwnd = CCV(ccv, snd_cwnd); | cubic_data->max_cwnd = CCV(ccv, snd_cwnd); | ||||
cubic_data->t_last_cong = ticks; | cubic_data->t_last_cong = ticks; | ||||
ccv->flags |= CCF_CHG_MAX_CWND; | |||||
ccv->flags &= ~CCF_MAX_CWND; | |||||
CCV(ccv, snd_cwnd) = CCV(ccv, snd_ssthresh); | CCV(ccv, snd_cwnd) = CCV(ccv, snd_ssthresh); | ||||
ENTER_CONGRECOVERY(CCV(ccv, t_flags)); | ENTER_CONGRECOVERY(CCV(ccv, t_flags)); | ||||
} | } | ||||
break; | break; | ||||
case CC_RTO: | case CC_RTO: | ||||
/* | /* | ||||
* Grab the current time and record it so we know when the | * Grab the current time and record it so we know when the | ||||
* most recent congestion event was. Only record it when the | * most recent congestion event was. Only record it when the | ||||
* timeout has fired more than once, as there is a reasonable | * timeout has fired more than once, as there is a reasonable | ||||
* chance the first one is a false alarm and may not indicate | * chance the first one is a false alarm and may not indicate | ||||
* congestion. | * congestion. | ||||
*/ | */ | ||||
if (CCV(ccv, t_rxtshift) >= 2) { | if (CCV(ccv, t_rxtshift) >= 2) { | ||||
cubic_data->num_cong_events++; | cubic_data->num_cong_events++; | ||||
cubic_data->t_last_cong = ticks; | cubic_data->t_last_cong = ticks; | ||||
ccv->flags &= ~CCF_MAX_CWND; | |||||
} | } | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
cubic_conn_init(struct cc_var *ccv) | cubic_conn_init(struct cc_var *ccv) | ||||
{ | { | ||||
struct cubic *cubic_data; | struct cubic *cubic_data; | ||||
cubic_data = ccv->cc_data; | cubic_data = ccv->cc_data; | ||||
/* | /* | ||||
* Ensure we have a sane initial value for max_cwnd recorded. Without | * Ensure we have a sane initial value for max_cwnd recorded. Without | ||||
* this here bad things happen when entries from the TCP hostcache | * this here bad things happen when entries from the TCP hostcache | ||||
* get used. | * get used. | ||||
*/ | */ | ||||
cubic_data->max_cwnd = CCV(ccv, snd_cwnd); | cubic_data->max_cwnd = CCV(ccv, snd_cwnd); | ||||
ccv->flags |= CCF_CHG_MAX_CWND; | |||||
} | } | ||||
static int | static int | ||||
cubic_mod_init(void) | cubic_mod_init(void) | ||||
{ | { | ||||
cubic_cc_algo.after_idle = newreno_cc_algo.after_idle; | cubic_cc_algo.after_idle = newreno_cc_algo.after_idle; | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Perform any necessary tasks before we exit congestion recovery. | * Perform any necessary tasks before we exit congestion recovery. | ||||
*/ | */ | ||||
static void | static void | ||||
cubic_post_recovery(struct cc_var *ccv) | cubic_post_recovery(struct cc_var *ccv) | ||||
{ | { | ||||
struct cubic *cubic_data; | struct cubic *cubic_data; | ||||
int pipe; | int pipe; | ||||
cubic_data = ccv->cc_data; | cubic_data = ccv->cc_data; | ||||
pipe = 0; | pipe = 0; | ||||
/* Fast convergence heuristic. */ | /* Fast convergence heuristic. */ | ||||
if (cubic_data->max_cwnd < cubic_data->prev_max_cwnd) | if (cubic_data->max_cwnd < cubic_data->prev_max_cwnd) { | ||||
cubic_data->max_cwnd = (cubic_data->max_cwnd * CUBIC_FC_FACTOR) | cubic_data->max_cwnd = (cubic_data->max_cwnd * CUBIC_FC_FACTOR) | ||||
>> CUBIC_SHIFT; | >> CUBIC_SHIFT; | ||||
ccv->flags |= CCF_CHG_MAX_CWND; | |||||
} | |||||
if (IN_FASTRECOVERY(CCV(ccv, t_flags))) { | if (IN_FASTRECOVERY(CCV(ccv, t_flags))) { | ||||
/* | /* | ||||
* If inflight data is less than ssthresh, set cwnd | * If inflight data is less than ssthresh, set cwnd | ||||
* conservatively to avoid a burst of data, as suggested in | * conservatively to avoid a burst of data, as suggested in | ||||
* the NewReno RFC. Otherwise, use the CUBIC method. | * the NewReno RFC. Otherwise, use the CUBIC method. | ||||
* | * | ||||
* XXXLAS: Find a way to do this without needing curack | * XXXLAS: Find a way to do this without needing curack | ||||
*/ | */ | ||||
if (V_tcp_do_rfc6675_pipe) | if (V_tcp_do_rfc6675_pipe) | ||||
pipe = tcp_compute_pipe(ccv->ccvc.tcp); | pipe = tcp_compute_pipe(ccv->ccvc.tcp); | ||||
else | else | ||||
pipe = CCV(ccv, snd_max) - ccv->curack; | pipe = CCV(ccv, snd_max) - ccv->curack; | ||||
if (pipe < CCV(ccv, snd_ssthresh)) | if (pipe < CCV(ccv, snd_ssthresh)) | ||||
CCV(ccv, snd_cwnd) = pipe + CCV(ccv, t_maxseg); | CCV(ccv, snd_cwnd) = pipe + CCV(ccv, t_maxseg); | ||||
else | else | ||||
/* Update cwnd based on beta and adjusted max_cwnd. */ | /* Update cwnd based on beta and adjusted max_cwnd. */ | ||||
CCV(ccv, snd_cwnd) = max(1, ((CUBIC_BETA * | CCV(ccv, snd_cwnd) = max(1, ((CUBIC_BETA * | ||||
cubic_data->max_cwnd) >> CUBIC_SHIFT)); | cubic_data->max_cwnd) >> CUBIC_SHIFT)); | ||||
} | } | ||||
cubic_data->t_last_cong = ticks; | cubic_data->t_last_cong = ticks; | ||||
ccv->flags &= ~CCF_MAX_CWND; | |||||
/* Calculate the average RTT between congestion epochs. */ | /* Calculate the average RTT between congestion epochs. */ | ||||
if (cubic_data->epoch_ack_count > 0 && | if (cubic_data->epoch_ack_count > 0 && | ||||
cubic_data->sum_rtt_ticks >= cubic_data->epoch_ack_count) { | cubic_data->sum_rtt_ticks >= cubic_data->epoch_ack_count) { | ||||
cubic_data->mean_rtt_ticks = (int)(cubic_data->sum_rtt_ticks / | cubic_data->mean_rtt_ticks = (int)(cubic_data->sum_rtt_ticks / | ||||
cubic_data->epoch_ack_count); | cubic_data->epoch_ack_count); | ||||
} | } | ||||
cubic_data->epoch_ack_count = 0; | cubic_data->epoch_ack_count = 0; | ||||
cubic_data->sum_rtt_ticks = 0; | cubic_data->sum_rtt_ticks = 0; | ||||
cubic_data->K = cubic_k(cubic_data->max_cwnd / CCV(ccv, t_maxseg)); | |||||
} | } | ||||
/* | /* | ||||
* 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) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 60 Lines • Show Last 20 Lines |