Commit 986ca37e authored by David S. Miller's avatar David S. Miller

Merge branch 'tcp-in-slow-start'

Yuchung Cheng says:

====================
tcp: fixes some congestion control corner cases

This patch series fixes corner cases of TCP congestion control.
First issue is to avoid continuing slow start when cwnd reaches ssthresh.
Second issue is incorrectly processing order of congestion state and
cwnd update when entering fast recovery or undoing cwnd.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 1007f59d b20a3fa3
...@@ -989,6 +989,11 @@ static inline unsigned int tcp_packets_in_flight(const struct tcp_sock *tp) ...@@ -989,6 +989,11 @@ static inline unsigned int tcp_packets_in_flight(const struct tcp_sock *tp)
#define TCP_INFINITE_SSTHRESH 0x7fffffff #define TCP_INFINITE_SSTHRESH 0x7fffffff
static inline bool tcp_in_slow_start(const struct tcp_sock *tp)
{
return tp->snd_cwnd < tp->snd_ssthresh;
}
static inline bool tcp_in_initial_slowstart(const struct tcp_sock *tp) static inline bool tcp_in_initial_slowstart(const struct tcp_sock *tp)
{ {
return tp->snd_ssthresh >= TCP_INFINITE_SSTHRESH; return tp->snd_ssthresh >= TCP_INFINITE_SSTHRESH;
...@@ -1065,7 +1070,7 @@ static inline bool tcp_is_cwnd_limited(const struct sock *sk) ...@@ -1065,7 +1070,7 @@ static inline bool tcp_is_cwnd_limited(const struct sock *sk)
const struct tcp_sock *tp = tcp_sk(sk); const struct tcp_sock *tp = tcp_sk(sk);
/* If in slow start, ensure cwnd grows to twice what was ACKed. */ /* If in slow start, ensure cwnd grows to twice what was ACKed. */
if (tp->snd_cwnd <= tp->snd_ssthresh) if (tcp_in_slow_start(tp))
return tp->snd_cwnd < 2 * tp->max_packets_out; return tp->snd_cwnd < 2 * tp->max_packets_out;
return tp->is_cwnd_limited; return tp->is_cwnd_limited;
......
...@@ -146,7 +146,7 @@ static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked) ...@@ -146,7 +146,7 @@ static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
if (!tcp_is_cwnd_limited(sk)) if (!tcp_is_cwnd_limited(sk))
return; return;
if (tp->snd_cwnd <= tp->snd_ssthresh) if (tcp_in_slow_start(tp))
tcp_slow_start(tp, acked); tcp_slow_start(tp, acked);
else { else {
bictcp_update(ca, tp->snd_cwnd); bictcp_update(ca, tp->snd_cwnd);
......
...@@ -264,7 +264,7 @@ static void tcp_cdg_cong_avoid(struct sock *sk, u32 ack, u32 acked) ...@@ -264,7 +264,7 @@ static void tcp_cdg_cong_avoid(struct sock *sk, u32 ack, u32 acked)
u32 prior_snd_cwnd; u32 prior_snd_cwnd;
u32 incr; u32 incr;
if (tp->snd_cwnd < tp->snd_ssthresh && hystart_detect) if (tcp_in_slow_start(tp) && hystart_detect)
tcp_cdg_hystart_update(sk); tcp_cdg_hystart_update(sk);
if (after(ack, ca->rtt_seq) && ca->rtt.v64) { if (after(ack, ca->rtt_seq) && ca->rtt.v64) {
......
...@@ -365,10 +365,8 @@ int tcp_set_congestion_control(struct sock *sk, const char *name) ...@@ -365,10 +365,8 @@ int tcp_set_congestion_control(struct sock *sk, const char *name)
*/ */
u32 tcp_slow_start(struct tcp_sock *tp, u32 acked) u32 tcp_slow_start(struct tcp_sock *tp, u32 acked)
{ {
u32 cwnd = tp->snd_cwnd + acked; u32 cwnd = min(tp->snd_cwnd + acked, tp->snd_ssthresh);
if (cwnd > tp->snd_ssthresh)
cwnd = tp->snd_ssthresh + 1;
acked -= cwnd - tp->snd_cwnd; acked -= cwnd - tp->snd_cwnd;
tp->snd_cwnd = min(cwnd, tp->snd_cwnd_clamp); tp->snd_cwnd = min(cwnd, tp->snd_cwnd_clamp);
...@@ -413,7 +411,7 @@ void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked) ...@@ -413,7 +411,7 @@ void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked)
return; return;
/* In "safe" area, increase. */ /* In "safe" area, increase. */
if (tp->snd_cwnd <= tp->snd_ssthresh) { if (tcp_in_slow_start(tp)) {
acked = tcp_slow_start(tp, acked); acked = tcp_slow_start(tp, acked);
if (!acked) if (!acked)
return; return;
......
...@@ -320,7 +320,7 @@ static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked) ...@@ -320,7 +320,7 @@ static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
if (!tcp_is_cwnd_limited(sk)) if (!tcp_is_cwnd_limited(sk))
return; return;
if (tp->snd_cwnd <= tp->snd_ssthresh) { if (tcp_in_slow_start(tp)) {
if (hystart && after(ack, ca->end_seq)) if (hystart && after(ack, ca->end_seq))
bictcp_hystart_reset(sk); bictcp_hystart_reset(sk);
acked = tcp_slow_start(tp, acked); acked = tcp_slow_start(tp, acked);
...@@ -439,7 +439,7 @@ static void bictcp_acked(struct sock *sk, u32 cnt, s32 rtt_us) ...@@ -439,7 +439,7 @@ static void bictcp_acked(struct sock *sk, u32 cnt, s32 rtt_us)
ca->delay_min = delay; ca->delay_min = delay;
/* hystart triggers when cwnd is larger than some threshold */ /* hystart triggers when cwnd is larger than some threshold */
if (hystart && tp->snd_cwnd <= tp->snd_ssthresh && if (hystart && tcp_in_slow_start(tp) &&
tp->snd_cwnd >= hystart_low_window) tp->snd_cwnd >= hystart_low_window)
hystart_update(sk, delay); hystart_update(sk, delay);
} }
......
...@@ -116,7 +116,7 @@ static void hstcp_cong_avoid(struct sock *sk, u32 ack, u32 acked) ...@@ -116,7 +116,7 @@ static void hstcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
if (!tcp_is_cwnd_limited(sk)) if (!tcp_is_cwnd_limited(sk))
return; return;
if (tp->snd_cwnd <= tp->snd_ssthresh) if (tcp_in_slow_start(tp))
tcp_slow_start(tp, acked); tcp_slow_start(tp, acked);
else { else {
/* Update AIMD parameters. /* Update AIMD parameters.
......
...@@ -236,7 +236,7 @@ static void htcp_cong_avoid(struct sock *sk, u32 ack, u32 acked) ...@@ -236,7 +236,7 @@ static void htcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
if (!tcp_is_cwnd_limited(sk)) if (!tcp_is_cwnd_limited(sk))
return; return;
if (tp->snd_cwnd <= tp->snd_ssthresh) if (tcp_in_slow_start(tp))
tcp_slow_start(tp, acked); tcp_slow_start(tp, acked);
else { else {
/* In dangerous area, increase slowly. /* In dangerous area, increase slowly.
......
...@@ -112,7 +112,7 @@ static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 acked) ...@@ -112,7 +112,7 @@ static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 acked)
rho_fractions = ca->rho_3ls - (ca->rho << 3); rho_fractions = ca->rho_3ls - (ca->rho << 3);
if (tp->snd_cwnd < tp->snd_ssthresh) { if (tcp_in_slow_start(tp)) {
/* /*
* slow start * slow start
* INC = 2^RHO - 1 * INC = 2^RHO - 1
......
...@@ -268,7 +268,7 @@ static void tcp_illinois_cong_avoid(struct sock *sk, u32 ack, u32 acked) ...@@ -268,7 +268,7 @@ static void tcp_illinois_cong_avoid(struct sock *sk, u32 ack, u32 acked)
return; return;
/* In slow start */ /* In slow start */
if (tp->snd_cwnd <= tp->snd_ssthresh) if (tcp_in_slow_start(tp))
tcp_slow_start(tp, acked); tcp_slow_start(tp, acked);
else { else {
......
...@@ -3568,10 +3568,6 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) ...@@ -3568,10 +3568,6 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
&sack_state); &sack_state);
acked -= tp->packets_out; acked -= tp->packets_out;
/* Advance cwnd if state allows */
if (tcp_may_raise_cwnd(sk, flag))
tcp_cong_avoid(sk, ack, acked);
if (tcp_ack_is_dubious(sk, flag)) { if (tcp_ack_is_dubious(sk, flag)) {
is_dupack = !(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP)); is_dupack = !(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP));
tcp_fastretrans_alert(sk, acked, prior_unsacked, tcp_fastretrans_alert(sk, acked, prior_unsacked,
...@@ -3580,6 +3576,10 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) ...@@ -3580,6 +3576,10 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
if (tp->tlp_high_seq) if (tp->tlp_high_seq)
tcp_process_tlp_ack(sk, ack, flag); tcp_process_tlp_ack(sk, ack, flag);
/* Advance cwnd if state allows */
if (tcp_may_raise_cwnd(sk, flag))
tcp_cong_avoid(sk, ack, acked);
if ((flag & FLAG_FORWARD_PROGRESS) || !(flag & FLAG_NOT_DUP)) { if ((flag & FLAG_FORWARD_PROGRESS) || !(flag & FLAG_NOT_DUP)) {
struct dst_entry *dst = __sk_dst_get(sk); struct dst_entry *dst = __sk_dst_get(sk);
if (dst) if (dst)
......
...@@ -461,7 +461,7 @@ void tcp_update_metrics(struct sock *sk) ...@@ -461,7 +461,7 @@ void tcp_update_metrics(struct sock *sk)
tcp_metric_set(tm, TCP_METRIC_CWND, tcp_metric_set(tm, TCP_METRIC_CWND,
tp->snd_cwnd); tp->snd_cwnd);
} }
} else if (tp->snd_cwnd > tp->snd_ssthresh && } else if (!tcp_in_slow_start(tp) &&
icsk->icsk_ca_state == TCP_CA_Open) { icsk->icsk_ca_state == TCP_CA_Open) {
/* Cong. avoidance phase, cwnd is reliable. */ /* Cong. avoidance phase, cwnd is reliable. */
if (!tcp_metric_locked(tm, TCP_METRIC_SSTHRESH)) if (!tcp_metric_locked(tm, TCP_METRIC_SSTHRESH))
......
...@@ -22,7 +22,7 @@ static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 acked) ...@@ -22,7 +22,7 @@ static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 acked)
if (!tcp_is_cwnd_limited(sk)) if (!tcp_is_cwnd_limited(sk))
return; return;
if (tp->snd_cwnd <= tp->snd_ssthresh) if (tcp_in_slow_start(tp))
tcp_slow_start(tp, acked); tcp_slow_start(tp, acked);
else else
tcp_cong_avoid_ai(tp, min(tp->snd_cwnd, TCP_SCALABLE_AI_CNT), tcp_cong_avoid_ai(tp, min(tp->snd_cwnd, TCP_SCALABLE_AI_CNT),
......
...@@ -225,7 +225,7 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked) ...@@ -225,7 +225,7 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked)
*/ */
diff = tp->snd_cwnd * (rtt-vegas->baseRTT) / vegas->baseRTT; diff = tp->snd_cwnd * (rtt-vegas->baseRTT) / vegas->baseRTT;
if (diff > gamma && tp->snd_cwnd <= tp->snd_ssthresh) { if (diff > gamma && tcp_in_slow_start(tp)) {
/* Going too fast. Time to slow down /* Going too fast. Time to slow down
* and switch to congestion avoidance. * and switch to congestion avoidance.
*/ */
...@@ -240,7 +240,7 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked) ...@@ -240,7 +240,7 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked)
tp->snd_cwnd = min(tp->snd_cwnd, (u32)target_cwnd+1); tp->snd_cwnd = min(tp->snd_cwnd, (u32)target_cwnd+1);
tp->snd_ssthresh = tcp_vegas_ssthresh(tp); tp->snd_ssthresh = tcp_vegas_ssthresh(tp);
} else if (tp->snd_cwnd <= tp->snd_ssthresh) { } else if (tcp_in_slow_start(tp)) {
/* Slow start. */ /* Slow start. */
tcp_slow_start(tp, acked); tcp_slow_start(tp, acked);
} else { } else {
...@@ -281,7 +281,7 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked) ...@@ -281,7 +281,7 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked)
vegas->minRTT = 0x7fffffff; vegas->minRTT = 0x7fffffff;
} }
/* Use normal slow start */ /* Use normal slow start */
else if (tp->snd_cwnd <= tp->snd_ssthresh) else if (tcp_in_slow_start(tp))
tcp_slow_start(tp, acked); tcp_slow_start(tp, acked);
} }
......
...@@ -150,7 +150,7 @@ static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 acked) ...@@ -150,7 +150,7 @@ static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 acked)
veno->diff = (tp->snd_cwnd << V_PARAM_SHIFT) - target_cwnd; veno->diff = (tp->snd_cwnd << V_PARAM_SHIFT) - target_cwnd;
if (tp->snd_cwnd <= tp->snd_ssthresh) { if (tcp_in_slow_start(tp)) {
/* Slow start. */ /* Slow start. */
tcp_slow_start(tp, acked); tcp_slow_start(tp, acked);
} else { } else {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment