tcp: stretch ACK fixes prep
authorNeal Cardwell <ncardwell@google.com>
Thu, 29 Jan 2015 01:01:35 +0000 (20:01 -0500)
committerDavid S. Miller <davem@davemloft.net>
Thu, 29 Jan 2015 06:18:37 +0000 (22:18 -0800)
LRO, GRO, delayed ACKs, and middleboxes can cause "stretch ACKs" that
cover more than the RFC-specified maximum of 2 packets. These stretch
ACKs can cause serious performance shortfalls in common congestion
control algorithms that were designed and tuned years ago with
receiver hosts that were not using LRO or GRO, and were instead
politely ACKing every other packet.

This patch series fixes Reno and CUBIC to handle stretch ACKs.

This patch prepares for the upcoming stretch ACK bug fix patches. It
adds an "acked" parameter to tcp_cong_avoid_ai() to allow for future
fixes to tcp_cong_avoid_ai() to correctly handle stretch ACKs, and
changes all congestion control algorithms to pass in 1 for the ACKed
count. It also changes tcp_slow_start() to return the number of packet
ACK "credits" that were not processed in slow start mode, and can be
processed by the congestion control module in additive increase mode.

In future patches we will fix tcp_cong_avoid_ai() to handle stretch
ACKs, and fix Reno and CUBIC handling of stretch ACKs in slow start
and additive increase mode.

Reported-by: Eyal Perry <eyalpe@mellanox.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/tcp.h
net/ipv4/tcp_bic.c
net/ipv4/tcp_cong.c
net/ipv4/tcp_cubic.c
net/ipv4/tcp_scalable.c
net/ipv4/tcp_veno.c
net/ipv4/tcp_yeah.c

index f50f29faf76f1fbcc5237de63c76f7c8200f4a6b..9d9111ef43ae305ad60e11c4b848b2b5f0a7eec3 100644 (file)
@@ -834,8 +834,8 @@ void tcp_get_available_congestion_control(char *buf, size_t len);
 void tcp_get_allowed_congestion_control(char *buf, size_t len);
 int tcp_set_allowed_congestion_control(char *allowed);
 int tcp_set_congestion_control(struct sock *sk, const char *name);
-void tcp_slow_start(struct tcp_sock *tp, u32 acked);
-void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w);
+u32 tcp_slow_start(struct tcp_sock *tp, u32 acked);
+void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w, u32 acked);
 
 u32 tcp_reno_ssthresh(struct sock *sk);
 void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked);
index bb395d46a3898136afe615c73ac311fd2832f6f1..c037644eafb7caadcb196b1c8b676bbc42abdb93 100644 (file)
@@ -150,7 +150,7 @@ static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
                tcp_slow_start(tp, acked);
        else {
                bictcp_update(ca, tp->snd_cwnd);
-               tcp_cong_avoid_ai(tp, ca->cnt);
+               tcp_cong_avoid_ai(tp, ca->cnt, 1);
        }
 }
 
index 27ead0dd16bc7e444e96781ff01b10c444678396..6826017c12d1e38bd0cd7301f369fb33ebd3dbd0 100644 (file)
@@ -291,25 +291,28 @@ int tcp_set_congestion_control(struct sock *sk, const char *name)
  * ABC caps N to 2. Slow start exits when cwnd grows over ssthresh and
  * returns the leftover acks to adjust cwnd in congestion avoidance mode.
  */
-void 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;
 
        if (cwnd > tp->snd_ssthresh)
                cwnd = tp->snd_ssthresh + 1;
+       acked -= cwnd - tp->snd_cwnd;
        tp->snd_cwnd = min(cwnd, tp->snd_cwnd_clamp);
+
+       return acked;
 }
 EXPORT_SYMBOL_GPL(tcp_slow_start);
 
 /* In theory this is tp->snd_cwnd += 1 / tp->snd_cwnd (or alternative w) */
-void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w)
+void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w, u32 acked)
 {
        if (tp->snd_cwnd_cnt >= w) {
                if (tp->snd_cwnd < tp->snd_cwnd_clamp)
                        tp->snd_cwnd++;
                tp->snd_cwnd_cnt = 0;
        } else {
-               tp->snd_cwnd_cnt++;
+               tp->snd_cwnd_cnt += acked;
        }
 }
 EXPORT_SYMBOL_GPL(tcp_cong_avoid_ai);
@@ -333,7 +336,7 @@ void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked)
                tcp_slow_start(tp, acked);
        /* In dangerous area, increase slowly. */
        else
-               tcp_cong_avoid_ai(tp, tp->snd_cwnd);
+               tcp_cong_avoid_ai(tp, tp->snd_cwnd, 1);
 }
 EXPORT_SYMBOL_GPL(tcp_reno_cong_avoid);
 
index 6b6002416a73950d493661ea1459870f49917efc..df4bc4d87e584928ce50fcc1e50ffef9fe4c9905 100644 (file)
@@ -320,7 +320,7 @@ static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
                tcp_slow_start(tp, acked);
        } else {
                bictcp_update(ca, tp->snd_cwnd);
-               tcp_cong_avoid_ai(tp, ca->cnt);
+               tcp_cong_avoid_ai(tp, ca->cnt, 1);
        }
 }
 
index 6824afb65d9335532fe2bf61edce82cab8c3fd9c..333bcb2415ffca51e06f3042ae3d94b8e21c0725 100644 (file)
@@ -25,7 +25,8 @@ static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 acked)
        if (tp->snd_cwnd <= tp->snd_ssthresh)
                tcp_slow_start(tp, acked);
        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),
+                                 1);
 }
 
 static u32 tcp_scalable_ssthresh(struct sock *sk)
index a4d2d2d88dcae7c00cf4db83d8b13ce6b143b3b4..112151eeee45bff0c37ac92d78d165ba92bd4d0a 100644 (file)
@@ -159,7 +159,7 @@ static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 acked)
                                /* In the "non-congestive state", increase cwnd
                                 *  every rtt.
                                 */
-                               tcp_cong_avoid_ai(tp, tp->snd_cwnd);
+                               tcp_cong_avoid_ai(tp, tp->snd_cwnd, 1);
                        } else {
                                /* In the "congestive state", increase cwnd
                                 * every other rtt.
index cd72732185989b41d1a3f9167eacbf44c235cc01..17d35662930d054fb6fb379a2cddb9600e6b75e3 100644 (file)
@@ -92,7 +92,7 @@ static void tcp_yeah_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 
        } else {
                /* Reno */
-               tcp_cong_avoid_ai(tp, tp->snd_cwnd);
+               tcp_cong_avoid_ai(tp, tp->snd_cwnd, 1);
        }
 
        /* The key players are v_vegas.beg_snd_una and v_beg_snd_nxt.