Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet/cc/cc_newreno.c
Show First 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | |||||
#include <netinet/tcp_seq.h> | #include <netinet/tcp_seq.h> | ||||
#include <netinet/tcp_var.h> | #include <netinet/tcp_var.h> | ||||
#include <netinet/tcp_log_buf.h> | #include <netinet/tcp_log_buf.h> | ||||
#include <netinet/tcp_hpts.h> | #include <netinet/tcp_hpts.h> | ||||
#include <netinet/cc/cc.h> | #include <netinet/cc/cc.h> | ||||
#include <netinet/cc/cc_module.h> | #include <netinet/cc/cc_module.h> | ||||
#include <netinet/cc/cc_newreno.h> | #include <netinet/cc/cc_newreno.h> | ||||
static MALLOC_DEFINE(M_NEWRENO, "newreno data", | |||||
"newreno beta values"); | |||||
static void newreno_cb_destroy(struct cc_var *ccv); | static void newreno_cb_destroy(struct cc_var *ccv); | ||||
static void newreno_ack_received(struct cc_var *ccv, uint16_t type); | static void newreno_ack_received(struct cc_var *ccv, uint16_t type); | ||||
static void newreno_after_idle(struct cc_var *ccv); | static void newreno_after_idle(struct cc_var *ccv); | ||||
static void newreno_cong_signal(struct cc_var *ccv, uint32_t type); | static void newreno_cong_signal(struct cc_var *ccv, uint32_t type); | ||||
static void newreno_post_recovery(struct cc_var *ccv); | |||||
static int newreno_ctl_output(struct cc_var *ccv, struct sockopt *sopt, void *buf); | static int newreno_ctl_output(struct cc_var *ccv, struct sockopt *sopt, void *buf); | ||||
static void newreno_newround(struct cc_var *ccv, uint32_t round_cnt); | static void newreno_newround(struct cc_var *ccv, uint32_t round_cnt); | ||||
static void newreno_rttsample(struct cc_var *ccv, uint32_t usec_rtt, uint32_t rxtcnt, uint32_t fas); | static void newreno_rttsample(struct cc_var *ccv, uint32_t usec_rtt, uint32_t rxtcnt, uint32_t fas); | ||||
static int newreno_cb_init(struct cc_var *ccv); | static int newreno_cb_init(struct cc_var *ccv, void *); | ||||
static size_t newreno_data_sz(void); | |||||
VNET_DEFINE(uint32_t, newreno_beta) = 50; | VNET_DEFINE(uint32_t, newreno_beta) = 50; | ||||
VNET_DEFINE(uint32_t, newreno_beta_ecn) = 80; | VNET_DEFINE(uint32_t, newreno_beta_ecn) = 80; | ||||
#define V_newreno_beta VNET(newreno_beta) | #define V_newreno_beta VNET(newreno_beta) | ||||
#define V_newreno_beta_ecn VNET(newreno_beta_ecn) | #define V_newreno_beta_ecn VNET(newreno_beta_ecn) | ||||
struct cc_algo newreno_cc_algo = { | struct cc_algo newreno_cc_algo = { | ||||
.name = "newreno", | .name = "newreno", | ||||
.cb_destroy = newreno_cb_destroy, | .cb_destroy = newreno_cb_destroy, | ||||
.ack_received = newreno_ack_received, | .ack_received = newreno_ack_received, | ||||
.after_idle = newreno_after_idle, | .after_idle = newreno_after_idle, | ||||
.cong_signal = newreno_cong_signal, | .cong_signal = newreno_cong_signal, | ||||
.post_recovery = newreno_post_recovery, | .post_recovery = common_cc_post_recovery, | ||||
.ctl_output = newreno_ctl_output, | .ctl_output = newreno_ctl_output, | ||||
.newround = newreno_newround, | .newround = newreno_newround, | ||||
.rttsample = newreno_rttsample, | .rttsample = newreno_rttsample, | ||||
.cb_init = newreno_cb_init, | .cb_init = newreno_cb_init, | ||||
.cc_data_sz = newreno_data_sz, | |||||
}; | }; | ||||
static uint32_t hystart_lowcwnd = 16; | static uint32_t hystart_lowcwnd = 16; | ||||
static uint32_t hystart_minrtt_thresh = 4000; | static uint32_t hystart_minrtt_thresh = 4000; | ||||
static uint32_t hystart_maxrtt_thresh = 16000; | static uint32_t hystart_maxrtt_thresh = 16000; | ||||
static uint32_t hystart_n_rttsamples = 8; | static uint32_t hystart_n_rttsamples = 8; | ||||
static uint32_t hystart_css_growth_div = 4; | static uint32_t hystart_css_growth_div = 4; | ||||
static uint32_t hystart_css_rounds = 5; | static uint32_t hystart_css_rounds = 5; | ||||
Show All 40 Lines | if (tp->t_logstate != TCP_LOG_STATE_OFF) { | ||||
TCP_LOG_EVENTP(tp, NULL, | TCP_LOG_EVENTP(tp, NULL, | ||||
&tp->t_inpcb->inp_socket->so_rcv, | &tp->t_inpcb->inp_socket->so_rcv, | ||||
&tp->t_inpcb->inp_socket->so_snd, | &tp->t_inpcb->inp_socket->so_snd, | ||||
TCP_HYSTART, 0, | TCP_HYSTART, 0, | ||||
0, &log, false, &tv); | 0, &log, false, &tv); | ||||
} | } | ||||
} | } | ||||
static size_t | |||||
newreno_data_sz(void) | |||||
{ | |||||
return (sizeof(struct newreno)); | |||||
} | |||||
static int | static int | ||||
newreno_cb_init(struct cc_var *ccv) | newreno_cb_init(struct cc_var *ccv, void *ptr) | ||||
{ | { | ||||
struct newreno *nreno; | struct newreno *nreno; | ||||
ccv->cc_data = malloc(sizeof(struct newreno), M_NEWRENO, M_NOWAIT); | if (ptr == NULL) { | ||||
ccv->cc_data = malloc(sizeof(struct newreno), M_CC_MEM, M_NOWAIT); | |||||
if (ccv->cc_data == NULL) | if (ccv->cc_data == NULL) | ||||
return (ENOMEM); | return (ENOMEM); | ||||
} else | |||||
ccv->cc_data = ptr; | |||||
nreno = (struct newreno *)ccv->cc_data; | nreno = (struct newreno *)ccv->cc_data; | ||||
/* NB: nreno is not zeroed, so initialise all fields. */ | /* NB: nreno is not zeroed, so initialise all fields. */ | ||||
nreno->beta = V_newreno_beta; | nreno->beta = V_newreno_beta; | ||||
nreno->beta_ecn = V_newreno_beta_ecn; | nreno->beta_ecn = V_newreno_beta_ecn; | ||||
/* | /* | ||||
* We set the enabled flag so that if | * We set the enabled flag so that if | ||||
* the socket option gets strobed and | * the socket option gets strobed and | ||||
* we have not hit a loss | * we have not hit a loss | ||||
Show All 10 Lines | newreno_cb_init(struct cc_var *ccv, void *ptr) | ||||
nreno->css_lowrtt_fas = 0; | nreno->css_lowrtt_fas = 0; | ||||
nreno->css_last_fas = 0; | nreno->css_last_fas = 0; | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
newreno_cb_destroy(struct cc_var *ccv) | newreno_cb_destroy(struct cc_var *ccv) | ||||
{ | { | ||||
free(ccv->cc_data, M_NEWRENO); | free(ccv->cc_data, M_CC_MEM); | ||||
} | } | ||||
static void | static void | ||||
newreno_ack_received(struct cc_var *ccv, uint16_t type) | newreno_ack_received(struct cc_var *ccv, uint16_t type) | ||||
{ | { | ||||
struct newreno *nreno; | struct newreno *nreno; | ||||
/* | nreno = ccv->cc_data; | ||||
* Other TCP congestion controls use newreno_ack_received(), but | |||||
* with their own private cc_data. Make sure the cc_data is used | |||||
* correctly. | |||||
*/ | |||||
nreno = (CC_ALGO(ccv->ccvc.tcp) == &newreno_cc_algo) ? ccv->cc_data : NULL; | |||||
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)) { | (ccv->flags & CCF_CWND_LIMITED)) { | ||||
u_int cw = CCV(ccv, snd_cwnd); | u_int cw = CCV(ccv, snd_cwnd); | ||||
u_int incr = CCV(ccv, t_maxseg); | u_int incr = CCV(ccv, t_maxseg); | ||||
/* | /* | ||||
* Regular in-order ACK, open the congestion window. | * Regular in-order ACK, open the congestion window. | ||||
* Method depends on which congestion control state we're | * Method depends on which congestion control state we're | ||||
Show All 17 Lines | if (type == CC_ACK && !IN_RECOVERY(CCV(ccv, t_flags)) && | ||||
* | * | ||||
* cong avoid without ABC (RFC 5681): | * cong avoid without ABC (RFC 5681): | ||||
* Grow cwnd linearly by approximately maxseg per RTT using | * Grow cwnd linearly by approximately maxseg per RTT using | ||||
* maxseg^2 / cwnd per ACK as the increment. | * maxseg^2 / cwnd per ACK as the increment. | ||||
* If cwnd > maxseg^2, fix the cwnd increment at 1 byte to | * If cwnd > maxseg^2, fix the cwnd increment at 1 byte to | ||||
* avoid capping cwnd. | * avoid capping cwnd. | ||||
*/ | */ | ||||
if (cw > CCV(ccv, snd_ssthresh)) { | if (cw > CCV(ccv, snd_ssthresh)) { | ||||
if ((nreno != NULL) && | if (nreno->newreno_flags & CC_NEWRENO_HYSTART_IN_CSS) { | ||||
(nreno->newreno_flags & CC_NEWRENO_HYSTART_IN_CSS)) { | |||||
/* | /* | ||||
* We have slipped into CA with | * We have slipped into CA with | ||||
* CSS active. Deactivate all. | * CSS active. Deactivate all. | ||||
*/ | */ | ||||
/* Turn off the CSS flag */ | /* Turn off the CSS flag */ | ||||
nreno->newreno_flags &= ~CC_NEWRENO_HYSTART_IN_CSS; | nreno->newreno_flags &= ~CC_NEWRENO_HYSTART_IN_CSS; | ||||
/* Disable use of CSS in the future except long idle */ | /* Disable use of CSS in the future except long idle */ | ||||
nreno->newreno_flags &= ~CC_NEWRENO_HYSTART_ENABLED; | nreno->newreno_flags &= ~CC_NEWRENO_HYSTART_ENABLED; | ||||
Show All 17 Lines | if (cw > CCV(ccv, snd_ssthresh)) { | ||||
* doesn't rely on tcpcb vars. | * doesn't rely on tcpcb vars. | ||||
*/ | */ | ||||
uint16_t abc_val; | uint16_t abc_val; | ||||
if (ccv->flags & CCF_USE_LOCAL_ABC) | if (ccv->flags & CCF_USE_LOCAL_ABC) | ||||
abc_val = ccv->labc; | abc_val = ccv->labc; | ||||
else | else | ||||
abc_val = V_tcp_abc_l_var; | abc_val = V_tcp_abc_l_var; | ||||
if ((nreno != NULL) && | if ((nreno->newreno_flags & CC_NEWRENO_HYSTART_ALLOWED) && | ||||
(nreno->newreno_flags & CC_NEWRENO_HYSTART_ALLOWED) && | |||||
(nreno->newreno_flags & CC_NEWRENO_HYSTART_ENABLED) && | (nreno->newreno_flags & CC_NEWRENO_HYSTART_ENABLED) && | ||||
((nreno->newreno_flags & CC_NEWRENO_HYSTART_IN_CSS) == 0)) { | ((nreno->newreno_flags & CC_NEWRENO_HYSTART_IN_CSS) == 0)) { | ||||
/* | /* | ||||
* Hystart is allowed and still enabled and we are not yet | * Hystart is allowed and still enabled and we are not yet | ||||
* in CSS. Lets check to see if we can make a decision on | * in CSS. Lets check to see if we can make a decision on | ||||
* if we need to go into CSS. | * if we need to go into CSS. | ||||
*/ | */ | ||||
if ((nreno->css_rttsample_count >= hystart_n_rttsamples) && | if ((nreno->css_rttsample_count >= hystart_n_rttsamples) && | ||||
Show All 21 Lines | if (cw > CCV(ccv, snd_ssthresh)) { | ||||
if (CCV(ccv, snd_nxt) == CCV(ccv, snd_max)) | if (CCV(ccv, snd_nxt) == CCV(ccv, snd_max)) | ||||
incr = min(ccv->bytes_this_ack, | incr = min(ccv->bytes_this_ack, | ||||
ccv->nsegs * abc_val * | ccv->nsegs * abc_val * | ||||
CCV(ccv, t_maxseg)); | CCV(ccv, t_maxseg)); | ||||
else | else | ||||
incr = min(ccv->bytes_this_ack, CCV(ccv, t_maxseg)); | incr = min(ccv->bytes_this_ack, CCV(ccv, t_maxseg)); | ||||
/* Only if Hystart is enabled will the flag get set */ | /* Only if Hystart is enabled will the flag get set */ | ||||
if ((nreno != NULL) && | if (nreno->newreno_flags & CC_NEWRENO_HYSTART_IN_CSS) { | ||||
(nreno->newreno_flags & CC_NEWRENO_HYSTART_IN_CSS)) { | |||||
incr /= hystart_css_growth_div; | incr /= hystart_css_growth_div; | ||||
newreno_log_hystart_event(ccv, nreno, 3, incr); | newreno_log_hystart_event(ccv, nreno, 3, incr); | ||||
} | } | ||||
} | } | ||||
/* ABC is on by default, so incr equals 0 frequently. */ | /* ABC is on by default, so incr equals 0 frequently. */ | ||||
if (incr > 0) | if (incr > 0) | ||||
CCV(ccv, snd_cwnd) = min(cw + incr, | CCV(ccv, snd_cwnd) = min(cw + incr, | ||||
TCP_MAXWIN << CCV(ccv, snd_scale)); | TCP_MAXWIN << CCV(ccv, snd_scale)); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
newreno_after_idle(struct cc_var *ccv) | newreno_after_idle(struct cc_var *ccv) | ||||
{ | { | ||||
struct newreno *nreno; | struct newreno *nreno; | ||||
uint32_t rw; | |||||
/* | nreno = ccv->cc_data; | ||||
* Other TCP congestion controls use newreno_after_idle(), but | common_cc_after_idle(ccv); | ||||
* with their own private cc_data. Make sure the cc_data is used | if ((nreno->newreno_flags & CC_NEWRENO_HYSTART_ENABLED) == 0) { | ||||
* correctly. | |||||
*/ | |||||
nreno = (CC_ALGO(ccv->ccvc.tcp) == &newreno_cc_algo) ? ccv->cc_data : NULL; | |||||
/* | |||||
* If we've been idle for more than one retransmit timeout the old | |||||
* congestion window is no longer current and we have to reduce it to | |||||
* the restart window before we can transmit again. | |||||
* | |||||
* The restart window is the initial window or the last CWND, whichever | |||||
* is smaller. | |||||
* | |||||
* This is done to prevent us from flooding the path with a full CWND at | |||||
* wirespeed, overloading router and switch buffers along the way. | |||||
* | |||||
* See RFC5681 Section 4.1. "Restarting Idle Connections". | |||||
* | |||||
* In addition, per RFC2861 Section 2, the ssthresh is set to the | |||||
* maximum of the former ssthresh or 3/4 of the old cwnd, to | |||||
* not exit slow-start prematurely. | |||||
*/ | |||||
rw = tcp_compute_initwnd(tcp_maxseg(ccv->ccvc.tcp)); | |||||
CCV(ccv, snd_ssthresh) = max(CCV(ccv, snd_ssthresh), | |||||
CCV(ccv, snd_cwnd)-(CCV(ccv, snd_cwnd)>>2)); | |||||
CCV(ccv, snd_cwnd) = min(rw, CCV(ccv, snd_cwnd)); | |||||
if ((nreno != NULL) && | |||||
(nreno->newreno_flags & CC_NEWRENO_HYSTART_ENABLED) == 0) { | |||||
if (CCV(ccv, snd_cwnd) <= (hystart_lowcwnd * tcp_fixed_maxseg(ccv->ccvc.tcp))) { | if (CCV(ccv, snd_cwnd) <= (hystart_lowcwnd * tcp_fixed_maxseg(ccv->ccvc.tcp))) { | ||||
/* | /* | ||||
* Re-enable hystart if our cwnd has fallen below | * Re-enable hystart if our cwnd has fallen below | ||||
* the hystart lowcwnd point. | * the hystart lowcwnd point. | ||||
*/ | */ | ||||
nreno->newreno_flags &= ~CC_NEWRENO_HYSTART_IN_CSS; | nreno->newreno_flags &= ~CC_NEWRENO_HYSTART_IN_CSS; | ||||
nreno->newreno_flags |= CC_NEWRENO_HYSTART_ENABLED; | nreno->newreno_flags |= CC_NEWRENO_HYSTART_ENABLED; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Perform any necessary tasks before we enter congestion recovery. | * Perform any necessary tasks before we enter congestion recovery. | ||||
*/ | */ | ||||
static void | static void | ||||
newreno_cong_signal(struct cc_var *ccv, uint32_t type) | newreno_cong_signal(struct cc_var *ccv, uint32_t type) | ||||
{ | { | ||||
struct newreno *nreno; | struct newreno *nreno; | ||||
uint32_t beta, beta_ecn, cwin, factor; | uint32_t beta, beta_ecn, cwin, factor; | ||||
u_int mss; | u_int mss; | ||||
cwin = CCV(ccv, snd_cwnd); | cwin = CCV(ccv, snd_cwnd); | ||||
mss = tcp_fixed_maxseg(ccv->ccvc.tcp); | mss = tcp_fixed_maxseg(ccv->ccvc.tcp); | ||||
/* | nreno = ccv->cc_data; | ||||
* Other TCP congestion controls use newreno_cong_signal(), but | |||||
* with their own private cc_data. Make sure the cc_data is used | |||||
* correctly. | |||||
*/ | |||||
nreno = (CC_ALGO(ccv->ccvc.tcp) == &newreno_cc_algo) ? ccv->cc_data : NULL; | |||||
beta = (nreno == NULL) ? V_newreno_beta : nreno->beta;; | beta = (nreno == NULL) ? V_newreno_beta : nreno->beta;; | ||||
beta_ecn = (nreno == NULL) ? V_newreno_beta_ecn : nreno->beta_ecn; | beta_ecn = (nreno == NULL) ? V_newreno_beta_ecn : nreno->beta_ecn; | ||||
/* | /* | ||||
* Note that we only change the backoff for ECN if the | * Note that we only change the backoff for ECN if the | ||||
* global sysctl V_cc_do_abe is set <or> the stack itself | * global sysctl V_cc_do_abe is set <or> the stack itself | ||||
* has set a flag in our newreno_flags (due to pacing) telling | * has set a flag in our newreno_flags (due to pacing) telling | ||||
* us to use the lower valued back-off. | * us to use the lower valued back-off. | ||||
*/ | */ | ||||
if ((type == CC_ECN) && | if ((type == CC_ECN) && | ||||
(V_cc_do_abe || | (V_cc_do_abe || | ||||
((nreno != NULL) && (nreno->newreno_flags & CC_NEWRENO_BETA_ECN_ENABLED)))) | ((nreno != NULL) && (nreno->newreno_flags & CC_NEWRENO_BETA_ECN_ENABLED)))) | ||||
factor = beta_ecn; | factor = beta_ecn; | ||||
else | else | ||||
factor = beta; | factor = beta; | ||||
/* Catch algos which mistakenly leak private signal types. */ | /* Catch algos which mistakenly leak private signal types. */ | ||||
KASSERT((type & CC_SIGPRIVMASK) == 0, | KASSERT((type & CC_SIGPRIVMASK) == 0, | ||||
("%s: congestion signal type 0x%08x is private\n", __func__, type)); | ("%s: congestion signal type 0x%08x is private\n", __func__, type)); | ||||
cwin = max(((uint64_t)cwin * (uint64_t)factor) / (100ULL * (uint64_t)mss), | cwin = max(((uint64_t)cwin * (uint64_t)factor) / (100ULL * (uint64_t)mss), | ||||
2) * mss; | 2) * mss; | ||||
switch (type) { | switch (type) { | ||||
case CC_NDUPACK: | case CC_NDUPACK: | ||||
if ((nreno != NULL) && | if (nreno->newreno_flags & CC_NEWRENO_HYSTART_ENABLED) { | ||||
(nreno->newreno_flags & CC_NEWRENO_HYSTART_ENABLED)) { | |||||
/* Make sure the flags are all off we had a loss */ | /* Make sure the flags are all off we had a loss */ | ||||
nreno->newreno_flags &= ~CC_NEWRENO_HYSTART_ENABLED; | nreno->newreno_flags &= ~CC_NEWRENO_HYSTART_ENABLED; | ||||
nreno->newreno_flags &= ~CC_NEWRENO_HYSTART_IN_CSS; | nreno->newreno_flags &= ~CC_NEWRENO_HYSTART_IN_CSS; | ||||
} | } | ||||
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) && | ||||
V_cc_do_abe && V_cc_abe_frlossreduce)) { | V_cc_do_abe && V_cc_abe_frlossreduce)) { | ||||
CCV(ccv, snd_ssthresh) = | CCV(ccv, snd_ssthresh) = | ||||
((uint64_t)CCV(ccv, snd_ssthresh) * | ((uint64_t)CCV(ccv, snd_ssthresh) * | ||||
(uint64_t)beta) / (uint64_t)beta_ecn; | (uint64_t)beta) / (uint64_t)beta_ecn; | ||||
} | } | ||||
if (!IN_CONGRECOVERY(CCV(ccv, t_flags))) | if (!IN_CONGRECOVERY(CCV(ccv, t_flags))) | ||||
CCV(ccv, snd_ssthresh) = cwin; | CCV(ccv, snd_ssthresh) = cwin; | ||||
ENTER_RECOVERY(CCV(ccv, t_flags)); | ENTER_RECOVERY(CCV(ccv, t_flags)); | ||||
} | } | ||||
break; | break; | ||||
case CC_ECN: | case CC_ECN: | ||||
if ((nreno != NULL) && | if (nreno->newreno_flags & CC_NEWRENO_HYSTART_ENABLED) { | ||||
(nreno->newreno_flags & CC_NEWRENO_HYSTART_ENABLED)) { | |||||
/* Make sure the flags are all off we had a loss */ | /* Make sure the flags are all off we had a loss */ | ||||
nreno->newreno_flags &= ~CC_NEWRENO_HYSTART_ENABLED; | nreno->newreno_flags &= ~CC_NEWRENO_HYSTART_ENABLED; | ||||
nreno->newreno_flags &= ~CC_NEWRENO_HYSTART_IN_CSS; | nreno->newreno_flags &= ~CC_NEWRENO_HYSTART_IN_CSS; | ||||
} | } | ||||
if (!IN_CONGRECOVERY(CCV(ccv, t_flags))) { | if (!IN_CONGRECOVERY(CCV(ccv, t_flags))) { | ||||
CCV(ccv, snd_ssthresh) = cwin; | CCV(ccv, snd_ssthresh) = cwin; | ||||
CCV(ccv, snd_cwnd) = cwin; | CCV(ccv, snd_cwnd) = cwin; | ||||
ENTER_CONGRECOVERY(CCV(ccv, t_flags)); | ENTER_CONGRECOVERY(CCV(ccv, t_flags)); | ||||
} | } | ||||
break; | break; | ||||
case CC_RTO: | case CC_RTO: | ||||
CCV(ccv, snd_ssthresh) = max(min(CCV(ccv, snd_wnd), | CCV(ccv, snd_ssthresh) = max(min(CCV(ccv, snd_wnd), | ||||
CCV(ccv, snd_cwnd)) / 2 / mss, | CCV(ccv, snd_cwnd)) / 2 / mss, | ||||
2) * mss; | 2) * mss; | ||||
CCV(ccv, snd_cwnd) = mss; | CCV(ccv, snd_cwnd) = mss; | ||||
break; | break; | ||||
} | |||||
} | |||||
/* | |||||
* Perform any necessary tasks before we exit congestion recovery. | |||||
*/ | |||||
static void | |||||
newreno_post_recovery(struct cc_var *ccv) | |||||
{ | |||||
int pipe; | |||||
if (IN_FASTRECOVERY(CCV(ccv, t_flags))) { | |||||
/* | |||||
* Fast recovery will conclude after returning from this | |||||
* function. Window inflation should have left us with | |||||
* approximately snd_ssthresh outstanding data. But in case we | |||||
* would be inclined to send a burst, better to do it via the | |||||
* slow start mechanism. | |||||
* | |||||
* XXXLAS: Find a way to do this without needing curack | |||||
*/ | |||||
if (V_tcp_do_newsack) | |||||
pipe = tcp_compute_pipe(ccv->ccvc.tcp); | |||||
else | |||||
pipe = CCV(ccv, snd_max) - ccv->curack; | |||||
if (pipe < CCV(ccv, snd_ssthresh)) | |||||
/* | |||||
* Ensure that cwnd does not collapse to 1 MSS under | |||||
* adverse conditons. Implements RFC6582 | |||||
*/ | |||||
CCV(ccv, snd_cwnd) = max(pipe, CCV(ccv, t_maxseg)) + | |||||
CCV(ccv, t_maxseg); | |||||
else | |||||
CCV(ccv, snd_cwnd) = CCV(ccv, snd_ssthresh); | |||||
} | } | ||||
} | } | ||||
static int | static int | ||||
newreno_ctl_output(struct cc_var *ccv, struct sockopt *sopt, void *buf) | newreno_ctl_output(struct cc_var *ccv, struct sockopt *sopt, void *buf) | ||||
{ | { | ||||
struct newreno *nreno; | struct newreno *nreno; | ||||
struct cc_newreno_opts *opt; | struct cc_newreno_opts *opt; | ||||
▲ Show 20 Lines • Show All 218 Lines • Show Last 20 Lines |