[TCP]: fix congestion window update when using TSO deferal
authorStephen Hemminger <shemminger@osdl.org>
Fri, 11 Nov 2005 00:53:30 +0000 (16:53 -0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 11 Nov 2005 00:53:30 +0000 (16:53 -0800)
TCP peformance with TSO over networks with delay is awful.
On a 100Mbit link with 150ms delay, we get 4Mbits/sec with TSO and
50Mbits/sec without TSO.

The problem is with TSO, we intentionally do not keep the maximum
number of packets in flight to fill the window, we hold out to until
we can send a MSS chunk. But, we also don't update the congestion window
unless we have filled, as per RFC2861.

This patch replaces the check for the congestion window being full
with something smarter that accounts for TSO.

Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
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_highspeed.c
net/ipv4/tcp_htcp.c
net/ipv4/tcp_hybla.c
net/ipv4/tcp_output.c
net/ipv4/tcp_scalable.c

index 96cc3b434e40f7f3e68ccd1f6b06a5c564736777..15bdbc6bd571cbeec4de4b6c077df222461d71cb 100644 (file)
@@ -810,6 +810,27 @@ static __inline__ __u32 tcp_max_burst(const struct tcp_sock *tp)
        return 3;
 }
 
+/* RFC2861 Check whether we are limited by application or congestion window
+ * This is the inverse of cwnd check in tcp_tso_should_defer
+ */
+static inline int tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight)
+{
+       const struct tcp_sock *tp = tcp_sk(sk);
+       u32 left;
+
+       if (in_flight >= tp->snd_cwnd)
+               return 1;
+
+       if (!(sk->sk_route_caps & NETIF_F_TSO))
+               return 0;
+
+       left = tp->snd_cwnd - in_flight;
+       if (sysctl_tcp_tso_win_divisor)
+               return left * sysctl_tcp_tso_win_divisor < tp->snd_cwnd;
+       else
+               return left <= tcp_max_burst(tp);
+}
+
 static __inline__ void tcp_minshall_update(struct tcp_sock *tp, int mss, 
                                           const struct sk_buff *skb)
 {
index ae35e06090476a88bc9b6b6a4ba963d1aff9af9b..5af99b3ef5d706b62c95027667fc8a7e64e0f05f 100644 (file)
@@ -217,7 +217,7 @@ static void bictcp_cong_avoid(struct sock *sk, u32 ack,
 
        bictcp_low_utilization(sk, data_acked);
 
-       if (in_flight < tp->snd_cwnd)
+       if (!tcp_is_cwnd_limited(sk, in_flight))
                return;
 
        if (tp->snd_cwnd <= tp->snd_ssthresh) {
index bbf2d6624e894b927a169d92ae2be882c851b91a..0705b496c6b3117de49e34b99f86731664a109ed 100644 (file)
@@ -186,7 +186,7 @@ void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 rtt, u32 in_flight,
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
-       if (in_flight < tp->snd_cwnd)
+       if (!tcp_is_cwnd_limited(sk, in_flight))
                return;
 
         if (tp->snd_cwnd <= tp->snd_ssthresh) {
index 6acc04bde08099c598e633a21518ed51e5b75bd2..5e56ad368dd260a819c636eb1733e0877ad37ffa 100644 (file)
@@ -111,12 +111,12 @@ static void hstcp_init(struct sock *sk)
 }
 
 static void hstcp_cong_avoid(struct sock *sk, u32 adk, u32 rtt,
-                            u32 in_flight, int good)
+                            u32 in_flight, u32 pkts_acked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct hstcp *ca = inet_csk_ca(sk);
 
-       if (in_flight < tp->snd_cwnd)
+       if (!tcp_is_cwnd_limited(sk, in_flight))
                return;
 
        if (tp->snd_cwnd <= tp->snd_ssthresh) {
index e47b37984e951e087cc2185b97336a5e3dadc21f..404a326ba3457ced90ef06b983c13625a1d93a66 100644 (file)
@@ -207,7 +207,7 @@ static void htcp_cong_avoid(struct sock *sk, u32 ack, u32 rtt,
        struct tcp_sock *tp = tcp_sk(sk);
        struct htcp *ca = inet_csk_ca(sk);
 
-       if (in_flight < tp->snd_cwnd)
+       if (!tcp_is_cwnd_limited(sk, in_flight))
                return;
 
         if (tp->snd_cwnd <= tp->snd_ssthresh) {
index 77add63623df2a1034a21bfdb6fbaddf82385a71..40dbb38775101c02b4c285b887f13d1ff0aecebb 100644 (file)
@@ -100,12 +100,12 @@ static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 rtt,
                ca->minrtt = tp->srtt;
        }
 
+       if (!tcp_is_cwnd_limited(sk, in_flight))
+               return;
+
        if (!ca->hybla_en)
                return tcp_reno_cong_avoid(sk, ack, rtt, in_flight, flag);
 
-       if (in_flight < tp->snd_cwnd)
-               return;
-
        if (ca->rho == 0)
                hybla_recalc_param(sk);
 
index b907456a79f46373bb81aac30ff9123910901952..998f6416ef8b59e308af1b23b01b654405219691 100644 (file)
@@ -2058,3 +2058,4 @@ EXPORT_SYMBOL(tcp_connect);
 EXPORT_SYMBOL(tcp_make_synack);
 EXPORT_SYMBOL(tcp_simple_retransmit);
 EXPORT_SYMBOL(tcp_sync_mss);
+EXPORT_SYMBOL(sysctl_tcp_tso_win_divisor);
index 327770bf552230211f2db5d799183fa08dd7e440..a2fd25617d241179345c7883ca62decd93f5ee6c 100644 (file)
@@ -20,7 +20,8 @@ static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 rtt,
                                    u32 in_flight, int flag)
 {
        struct tcp_sock *tp = tcp_sk(sk);
-       if (in_flight < tp->snd_cwnd)
+
+       if (!tcp_is_cwnd_limited(sk, in_flight))
                return;
 
        if (tp->snd_cwnd <= tp->snd_ssthresh) {