aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYuchung Cheng <ycheng@google.com>2017-12-12 16:10:40 -0500
committerDavid S. Miller <davem@davemloft.net>2017-12-13 15:51:12 -0500
commit7268586baa530312041e597b518b5c6a05110df1 (patch)
tree09075b8fb35fcc5ad90f744345d1582fe7d62f4c
parent8a83c5d7969b8433584e3cf658a8d76c4dc37f4d (diff)
tcp: pause Fast Open globally after third consecutive timeout
Prior to this patch, active Fast Open is paused on a specific destination IP address if the previous connections to the IP address have experienced recurring timeouts . But recent experiments by Microsoft (https://goo.gl/cykmn7) and Mozilla browsers indicate the isssue is often caused by broken middle-boxes sitting close to the client. Therefore it is much better user experience if Fast Open is disabled out-right globally to avoid experiencing further timeouts on connections toward other destinations. This patch changes the destination-IP disablement to global disablement if a connection experiencing recurring timeouts or aborts due to timeout. Repeated incidents would still exponentially increase the pause time, starting from an hour. This is extremely conservative but an unfortunate compromise to minimize bad experience due to broken middle-boxes. Reported-by: Dragana Damjanovic <ddamjanovic@mozilla.com> Reported-by: Patrick McManus <mcmanus@ducksong.com> Signed-off-by: Yuchung Cheng <ycheng@google.com> Reviewed-by: Wei Wang <weiwan@google.com> Reviewed-by: Neal Cardwell <ncardwell@google.com> Reviewed-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--Documentation/networking/ip-sysctl.txt1
-rw-r--r--include/net/tcp.h5
-rw-r--r--net/ipv4/tcp_fastopen.c30
-rw-r--r--net/ipv4/tcp_metrics.c5
-rw-r--r--net/ipv4/tcp_timer.c17
5 files changed, 25 insertions, 33 deletions
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index 46c7e1085efc..3f2c40d8e6aa 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -606,6 +606,7 @@ tcp_fastopen_blackhole_timeout_sec - INTEGER
606 This time period will grow exponentially when more blackhole issues 606 This time period will grow exponentially when more blackhole issues
607 get detected right after Fastopen is re-enabled and will reset to 607 get detected right after Fastopen is re-enabled and will reset to
608 initial value when the blackhole issue goes away. 608 initial value when the blackhole issue goes away.
609 0 to disable the blackhole detection.
609 By default, it is set to 1hr. 610 By default, it is set to 1hr.
610 611
611tcp_syn_retries - INTEGER 612tcp_syn_retries - INTEGER
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 3c3744e52cd1..6939e69d3c37 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1507,8 +1507,7 @@ int tcp_md5_hash_key(struct tcp_md5sig_pool *hp,
1507 1507
1508/* From tcp_fastopen.c */ 1508/* From tcp_fastopen.c */
1509void tcp_fastopen_cache_get(struct sock *sk, u16 *mss, 1509void tcp_fastopen_cache_get(struct sock *sk, u16 *mss,
1510 struct tcp_fastopen_cookie *cookie, int *syn_loss, 1510 struct tcp_fastopen_cookie *cookie);
1511 unsigned long *last_syn_loss);
1512void tcp_fastopen_cache_set(struct sock *sk, u16 mss, 1511void tcp_fastopen_cache_set(struct sock *sk, u16 mss,
1513 struct tcp_fastopen_cookie *cookie, bool syn_lost, 1512 struct tcp_fastopen_cookie *cookie, bool syn_lost,
1514 u16 try_exp); 1513 u16 try_exp);
@@ -1546,7 +1545,7 @@ extern unsigned int sysctl_tcp_fastopen_blackhole_timeout;
1546void tcp_fastopen_active_disable(struct sock *sk); 1545void tcp_fastopen_active_disable(struct sock *sk);
1547bool tcp_fastopen_active_should_disable(struct sock *sk); 1546bool tcp_fastopen_active_should_disable(struct sock *sk);
1548void tcp_fastopen_active_disable_ofo_check(struct sock *sk); 1547void tcp_fastopen_active_disable_ofo_check(struct sock *sk);
1549void tcp_fastopen_active_timeout_reset(void); 1548void tcp_fastopen_active_detect_blackhole(struct sock *sk, bool expired);
1550 1549
1551/* Latencies incurred by various limits for a sender. They are 1550/* Latencies incurred by various limits for a sender. They are
1552 * chronograph-like stats that are mutually exclusive. 1551 * chronograph-like stats that are mutually exclusive.
diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c
index 78c192ee03a4..018a48477355 100644
--- a/net/ipv4/tcp_fastopen.c
+++ b/net/ipv4/tcp_fastopen.c
@@ -379,18 +379,9 @@ fastopen:
379bool tcp_fastopen_cookie_check(struct sock *sk, u16 *mss, 379bool tcp_fastopen_cookie_check(struct sock *sk, u16 *mss,
380 struct tcp_fastopen_cookie *cookie) 380 struct tcp_fastopen_cookie *cookie)
381{ 381{
382 unsigned long last_syn_loss = 0;
383 const struct dst_entry *dst; 382 const struct dst_entry *dst;
384 int syn_loss = 0;
385 383
386 tcp_fastopen_cache_get(sk, mss, cookie, &syn_loss, &last_syn_loss); 384 tcp_fastopen_cache_get(sk, mss, cookie);
387
388 /* Recurring FO SYN losses: no cookie or data in SYN */
389 if (syn_loss > 1 &&
390 time_before(jiffies, last_syn_loss + (60*HZ << syn_loss))) {
391 cookie->len = -1;
392 return false;
393 }
394 385
395 /* Firewall blackhole issue check */ 386 /* Firewall blackhole issue check */
396 if (tcp_fastopen_active_should_disable(sk)) { 387 if (tcp_fastopen_active_should_disable(sk)) {
@@ -448,6 +439,8 @@ EXPORT_SYMBOL(tcp_fastopen_defer_connect);
448 * following circumstances: 439 * following circumstances:
449 * 1. client side TFO socket receives out of order FIN 440 * 1. client side TFO socket receives out of order FIN
450 * 2. client side TFO socket receives out of order RST 441 * 2. client side TFO socket receives out of order RST
442 * 3. client side TFO socket has timed out three times consecutively during
443 * or after handshake
451 * We disable active side TFO globally for 1hr at first. Then if it 444 * We disable active side TFO globally for 1hr at first. Then if it
452 * happens again, we disable it for 2h, then 4h, 8h, ... 445 * happens again, we disable it for 2h, then 4h, 8h, ...
453 * And we reset the timeout back to 1hr when we see a successful active 446 * And we reset the timeout back to 1hr when we see a successful active
@@ -524,3 +517,20 @@ void tcp_fastopen_active_disable_ofo_check(struct sock *sk)
524 dst_release(dst); 517 dst_release(dst);
525 } 518 }
526} 519}
520
521void tcp_fastopen_active_detect_blackhole(struct sock *sk, bool expired)
522{
523 u32 timeouts = inet_csk(sk)->icsk_retransmits;
524 struct tcp_sock *tp = tcp_sk(sk);
525
526 /* Broken middle-boxes may black-hole Fast Open connection during or
527 * even after the handshake. Be extremely conservative and pause
528 * Fast Open globally after hitting the third consecutive timeout or
529 * exceeding the configured timeout limit.
530 */
531 if ((tp->syn_fastopen || tp->syn_data || tp->syn_data_acked) &&
532 (timeouts == 2 || (timeouts < 2 && expired))) {
533 tcp_fastopen_active_disable(sk);
534 NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPFASTOPENACTIVEFAIL);
535 }
536}
diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c
index 7097f92d16e5..759e6bc8327b 100644
--- a/net/ipv4/tcp_metrics.c
+++ b/net/ipv4/tcp_metrics.c
@@ -546,8 +546,7 @@ bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst)
546static DEFINE_SEQLOCK(fastopen_seqlock); 546static DEFINE_SEQLOCK(fastopen_seqlock);
547 547
548void tcp_fastopen_cache_get(struct sock *sk, u16 *mss, 548void tcp_fastopen_cache_get(struct sock *sk, u16 *mss,
549 struct tcp_fastopen_cookie *cookie, 549 struct tcp_fastopen_cookie *cookie)
550 int *syn_loss, unsigned long *last_syn_loss)
551{ 550{
552 struct tcp_metrics_block *tm; 551 struct tcp_metrics_block *tm;
553 552
@@ -564,8 +563,6 @@ void tcp_fastopen_cache_get(struct sock *sk, u16 *mss,
564 *cookie = tfom->cookie; 563 *cookie = tfom->cookie;
565 if (cookie->len <= 0 && tfom->try_exp == 1) 564 if (cookie->len <= 0 && tfom->try_exp == 1)
566 cookie->exp = true; 565 cookie->exp = true;
567 *syn_loss = tfom->syn_loss;
568 *last_syn_loss = *syn_loss ? tfom->last_syn_loss : 0;
569 } while (read_seqretry(&fastopen_seqlock, seq)); 566 } while (read_seqretry(&fastopen_seqlock, seq));
570 } 567 }
571 rcu_read_unlock(); 568 rcu_read_unlock();
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index 16df6dd44b98..c9a63417af48 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -183,11 +183,6 @@ static int tcp_write_timeout(struct sock *sk)
183 if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) { 183 if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
184 if (icsk->icsk_retransmits) { 184 if (icsk->icsk_retransmits) {
185 dst_negative_advice(sk); 185 dst_negative_advice(sk);
186 if (tp->syn_fastopen || tp->syn_data)
187 tcp_fastopen_cache_set(sk, 0, NULL, true, 0);
188 if (tp->syn_data && icsk->icsk_retransmits == 1)
189 NET_INC_STATS(sock_net(sk),
190 LINUX_MIB_TCPFASTOPENACTIVEFAIL);
191 } else if (!tp->syn_data && !tp->syn_fastopen) { 186 } else if (!tp->syn_data && !tp->syn_fastopen) {
192 sk_rethink_txhash(sk); 187 sk_rethink_txhash(sk);
193 } 188 }
@@ -195,17 +190,6 @@ static int tcp_write_timeout(struct sock *sk)
195 expired = icsk->icsk_retransmits >= retry_until; 190 expired = icsk->icsk_retransmits >= retry_until;
196 } else { 191 } else {
197 if (retransmits_timed_out(sk, net->ipv4.sysctl_tcp_retries1, 0)) { 192 if (retransmits_timed_out(sk, net->ipv4.sysctl_tcp_retries1, 0)) {
198 /* Some middle-boxes may black-hole Fast Open _after_
199 * the handshake. Therefore we conservatively disable
200 * Fast Open on this path on recurring timeouts after
201 * successful Fast Open.
202 */
203 if (tp->syn_data_acked) {
204 tcp_fastopen_cache_set(sk, 0, NULL, true, 0);
205 if (icsk->icsk_retransmits == net->ipv4.sysctl_tcp_retries1)
206 NET_INC_STATS(sock_net(sk),
207 LINUX_MIB_TCPFASTOPENACTIVEFAIL);
208 }
209 /* Black hole detection */ 193 /* Black hole detection */
210 tcp_mtu_probing(icsk, sk); 194 tcp_mtu_probing(icsk, sk);
211 195
@@ -228,6 +212,7 @@ static int tcp_write_timeout(struct sock *sk)
228 expired = retransmits_timed_out(sk, retry_until, 212 expired = retransmits_timed_out(sk, retry_until,
229 icsk->icsk_user_timeout); 213 icsk->icsk_user_timeout);
230 } 214 }
215 tcp_fastopen_active_detect_blackhole(sk, expired);
231 if (expired) { 216 if (expired) {
232 /* Has it gone just too far? */ 217 /* Has it gone just too far? */
233 tcp_write_err(sk); 218 tcp_write_err(sk);