Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: David S. Miller <davem@davemloft.net>

+26 -23
+6 -1
include/net/tcp.h
··· 989 989 990 990 #define TCP_INFINITE_SSTHRESH 0x7fffffff 991 991 992 + static inline bool tcp_in_slow_start(const struct tcp_sock *tp) 993 + { 994 + return tp->snd_cwnd < tp->snd_ssthresh; 995 + } 996 + 992 997 static inline bool tcp_in_initial_slowstart(const struct tcp_sock *tp) 993 998 { 994 999 return tp->snd_ssthresh >= TCP_INFINITE_SSTHRESH; ··· 1070 1065 const struct tcp_sock *tp = tcp_sk(sk); 1071 1066 1072 1067 /* If in slow start, ensure cwnd grows to twice what was ACKed. */ 1073 - if (tp->snd_cwnd <= tp->snd_ssthresh) 1068 + if (tcp_in_slow_start(tp)) 1074 1069 return tp->snd_cwnd < 2 * tp->max_packets_out; 1075 1070 1076 1071 return tp->is_cwnd_limited;
+1 -1
net/ipv4/tcp_bic.c
··· 146 146 if (!tcp_is_cwnd_limited(sk)) 147 147 return; 148 148 149 - if (tp->snd_cwnd <= tp->snd_ssthresh) 149 + if (tcp_in_slow_start(tp)) 150 150 tcp_slow_start(tp, acked); 151 151 else { 152 152 bictcp_update(ca, tp->snd_cwnd);
+1 -1
net/ipv4/tcp_cdg.c
··· 264 264 u32 prior_snd_cwnd; 265 265 u32 incr; 266 266 267 - if (tp->snd_cwnd < tp->snd_ssthresh && hystart_detect) 267 + if (tcp_in_slow_start(tp) && hystart_detect) 268 268 tcp_cdg_hystart_update(sk); 269 269 270 270 if (after(ack, ca->rtt_seq) && ca->rtt.v64) {
+2 -4
net/ipv4/tcp_cong.c
··· 365 365 */ 366 366 u32 tcp_slow_start(struct tcp_sock *tp, u32 acked) 367 367 { 368 - u32 cwnd = tp->snd_cwnd + acked; 368 + u32 cwnd = min(tp->snd_cwnd + acked, tp->snd_ssthresh); 369 369 370 - if (cwnd > tp->snd_ssthresh) 371 - cwnd = tp->snd_ssthresh + 1; 372 370 acked -= cwnd - tp->snd_cwnd; 373 371 tp->snd_cwnd = min(cwnd, tp->snd_cwnd_clamp); 374 372 ··· 411 413 return; 412 414 413 415 /* In "safe" area, increase. */ 414 - if (tp->snd_cwnd <= tp->snd_ssthresh) { 416 + if (tcp_in_slow_start(tp)) { 415 417 acked = tcp_slow_start(tp, acked); 416 418 if (!acked) 417 419 return;
+2 -2
net/ipv4/tcp_cubic.c
··· 320 320 if (!tcp_is_cwnd_limited(sk)) 321 321 return; 322 322 323 - if (tp->snd_cwnd <= tp->snd_ssthresh) { 323 + if (tcp_in_slow_start(tp)) { 324 324 if (hystart && after(ack, ca->end_seq)) 325 325 bictcp_hystart_reset(sk); 326 326 acked = tcp_slow_start(tp, acked); ··· 439 439 ca->delay_min = delay; 440 440 441 441 /* hystart triggers when cwnd is larger than some threshold */ 442 - if (hystart && tp->snd_cwnd <= tp->snd_ssthresh && 442 + if (hystart && tcp_in_slow_start(tp) && 443 443 tp->snd_cwnd >= hystart_low_window) 444 444 hystart_update(sk, delay); 445 445 }
+1 -1
net/ipv4/tcp_highspeed.c
··· 116 116 if (!tcp_is_cwnd_limited(sk)) 117 117 return; 118 118 119 - if (tp->snd_cwnd <= tp->snd_ssthresh) 119 + if (tcp_in_slow_start(tp)) 120 120 tcp_slow_start(tp, acked); 121 121 else { 122 122 /* Update AIMD parameters.
+1 -1
net/ipv4/tcp_htcp.c
··· 236 236 if (!tcp_is_cwnd_limited(sk)) 237 237 return; 238 238 239 - if (tp->snd_cwnd <= tp->snd_ssthresh) 239 + if (tcp_in_slow_start(tp)) 240 240 tcp_slow_start(tp, acked); 241 241 else { 242 242 /* In dangerous area, increase slowly.
+1 -1
net/ipv4/tcp_hybla.c
··· 112 112 113 113 rho_fractions = ca->rho_3ls - (ca->rho << 3); 114 114 115 - if (tp->snd_cwnd < tp->snd_ssthresh) { 115 + if (tcp_in_slow_start(tp)) { 116 116 /* 117 117 * slow start 118 118 * INC = 2^RHO - 1
+1 -1
net/ipv4/tcp_illinois.c
··· 268 268 return; 269 269 270 270 /* In slow start */ 271 - if (tp->snd_cwnd <= tp->snd_ssthresh) 271 + if (tcp_in_slow_start(tp)) 272 272 tcp_slow_start(tp, acked); 273 273 274 274 else {
+4 -4
net/ipv4/tcp_input.c
··· 3568 3568 &sack_state); 3569 3569 acked -= tp->packets_out; 3570 3570 3571 - /* Advance cwnd if state allows */ 3572 - if (tcp_may_raise_cwnd(sk, flag)) 3573 - tcp_cong_avoid(sk, ack, acked); 3574 - 3575 3571 if (tcp_ack_is_dubious(sk, flag)) { 3576 3572 is_dupack = !(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP)); 3577 3573 tcp_fastretrans_alert(sk, acked, prior_unsacked, ··· 3575 3579 } 3576 3580 if (tp->tlp_high_seq) 3577 3581 tcp_process_tlp_ack(sk, ack, flag); 3582 + 3583 + /* Advance cwnd if state allows */ 3584 + if (tcp_may_raise_cwnd(sk, flag)) 3585 + tcp_cong_avoid(sk, ack, acked); 3578 3586 3579 3587 if ((flag & FLAG_FORWARD_PROGRESS) || !(flag & FLAG_NOT_DUP)) { 3580 3588 struct dst_entry *dst = __sk_dst_get(sk);
+1 -1
net/ipv4/tcp_metrics.c
··· 461 461 tcp_metric_set(tm, TCP_METRIC_CWND, 462 462 tp->snd_cwnd); 463 463 } 464 - } else if (tp->snd_cwnd > tp->snd_ssthresh && 464 + } else if (!tcp_in_slow_start(tp) && 465 465 icsk->icsk_ca_state == TCP_CA_Open) { 466 466 /* Cong. avoidance phase, cwnd is reliable. */ 467 467 if (!tcp_metric_locked(tm, TCP_METRIC_SSTHRESH))
+1 -1
net/ipv4/tcp_scalable.c
··· 22 22 if (!tcp_is_cwnd_limited(sk)) 23 23 return; 24 24 25 - if (tp->snd_cwnd <= tp->snd_ssthresh) 25 + if (tcp_in_slow_start(tp)) 26 26 tcp_slow_start(tp, acked); 27 27 else 28 28 tcp_cong_avoid_ai(tp, min(tp->snd_cwnd, TCP_SCALABLE_AI_CNT),
+3 -3
net/ipv4/tcp_vegas.c
··· 225 225 */ 226 226 diff = tp->snd_cwnd * (rtt-vegas->baseRTT) / vegas->baseRTT; 227 227 228 - if (diff > gamma && tp->snd_cwnd <= tp->snd_ssthresh) { 228 + if (diff > gamma && tcp_in_slow_start(tp)) { 229 229 /* Going too fast. Time to slow down 230 230 * and switch to congestion avoidance. 231 231 */ ··· 240 240 tp->snd_cwnd = min(tp->snd_cwnd, (u32)target_cwnd+1); 241 241 tp->snd_ssthresh = tcp_vegas_ssthresh(tp); 242 242 243 - } else if (tp->snd_cwnd <= tp->snd_ssthresh) { 243 + } else if (tcp_in_slow_start(tp)) { 244 244 /* Slow start. */ 245 245 tcp_slow_start(tp, acked); 246 246 } else { ··· 281 281 vegas->minRTT = 0x7fffffff; 282 282 } 283 283 /* Use normal slow start */ 284 - else if (tp->snd_cwnd <= tp->snd_ssthresh) 284 + else if (tcp_in_slow_start(tp)) 285 285 tcp_slow_start(tp, acked); 286 286 } 287 287
+1 -1
net/ipv4/tcp_veno.c
··· 150 150 151 151 veno->diff = (tp->snd_cwnd << V_PARAM_SHIFT) - target_cwnd; 152 152 153 - if (tp->snd_cwnd <= tp->snd_ssthresh) { 153 + if (tcp_in_slow_start(tp)) { 154 154 /* Slow start. */ 155 155 tcp_slow_start(tp, acked); 156 156 } else {