Commit f4805ede authored by Stephen Hemminger's avatar Stephen Hemminger Committed by David S. Miller

[TCP]: fix congestion window update when using TSO deferal

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: default avatarStephen Hemminger <shemminger@osdl.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6e6ece5d
...@@ -810,6 +810,27 @@ static __inline__ __u32 tcp_max_burst(const struct tcp_sock *tp) ...@@ -810,6 +810,27 @@ static __inline__ __u32 tcp_max_burst(const struct tcp_sock *tp)
return 3; 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, static __inline__ void tcp_minshall_update(struct tcp_sock *tp, int mss,
const struct sk_buff *skb) const struct sk_buff *skb)
{ {
......
...@@ -217,7 +217,7 @@ static void bictcp_cong_avoid(struct sock *sk, u32 ack, ...@@ -217,7 +217,7 @@ static void bictcp_cong_avoid(struct sock *sk, u32 ack,
bictcp_low_utilization(sk, data_acked); bictcp_low_utilization(sk, data_acked);
if (in_flight < tp->snd_cwnd) if (!tcp_is_cwnd_limited(sk, in_flight))
return; return;
if (tp->snd_cwnd <= tp->snd_ssthresh) { if (tp->snd_cwnd <= tp->snd_ssthresh) {
......
...@@ -186,7 +186,7 @@ void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 rtt, u32 in_flight, ...@@ -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); struct tcp_sock *tp = tcp_sk(sk);
if (in_flight < tp->snd_cwnd) if (!tcp_is_cwnd_limited(sk, in_flight))
return; return;
if (tp->snd_cwnd <= tp->snd_ssthresh) { if (tp->snd_cwnd <= tp->snd_ssthresh) {
......
...@@ -111,12 +111,12 @@ static void hstcp_init(struct sock *sk) ...@@ -111,12 +111,12 @@ static void hstcp_init(struct sock *sk)
} }
static void hstcp_cong_avoid(struct sock *sk, u32 adk, u32 rtt, 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 tcp_sock *tp = tcp_sk(sk);
struct hstcp *ca = inet_csk_ca(sk); struct hstcp *ca = inet_csk_ca(sk);
if (in_flight < tp->snd_cwnd) if (!tcp_is_cwnd_limited(sk, in_flight))
return; return;
if (tp->snd_cwnd <= tp->snd_ssthresh) { if (tp->snd_cwnd <= tp->snd_ssthresh) {
......
...@@ -207,7 +207,7 @@ static void htcp_cong_avoid(struct sock *sk, u32 ack, u32 rtt, ...@@ -207,7 +207,7 @@ static void htcp_cong_avoid(struct sock *sk, u32 ack, u32 rtt,
struct tcp_sock *tp = tcp_sk(sk); struct tcp_sock *tp = tcp_sk(sk);
struct htcp *ca = inet_csk_ca(sk); struct htcp *ca = inet_csk_ca(sk);
if (in_flight < tp->snd_cwnd) if (!tcp_is_cwnd_limited(sk, in_flight))
return; return;
if (tp->snd_cwnd <= tp->snd_ssthresh) { if (tp->snd_cwnd <= tp->snd_ssthresh) {
......
...@@ -100,12 +100,12 @@ static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 rtt, ...@@ -100,12 +100,12 @@ static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 rtt,
ca->minrtt = tp->srtt; ca->minrtt = tp->srtt;
} }
if (!tcp_is_cwnd_limited(sk, in_flight))
return;
if (!ca->hybla_en) if (!ca->hybla_en)
return tcp_reno_cong_avoid(sk, ack, rtt, in_flight, flag); return tcp_reno_cong_avoid(sk, ack, rtt, in_flight, flag);
if (in_flight < tp->snd_cwnd)
return;
if (ca->rho == 0) if (ca->rho == 0)
hybla_recalc_param(sk); hybla_recalc_param(sk);
......
...@@ -2058,3 +2058,4 @@ EXPORT_SYMBOL(tcp_connect); ...@@ -2058,3 +2058,4 @@ EXPORT_SYMBOL(tcp_connect);
EXPORT_SYMBOL(tcp_make_synack); EXPORT_SYMBOL(tcp_make_synack);
EXPORT_SYMBOL(tcp_simple_retransmit); EXPORT_SYMBOL(tcp_simple_retransmit);
EXPORT_SYMBOL(tcp_sync_mss); EXPORT_SYMBOL(tcp_sync_mss);
EXPORT_SYMBOL(sysctl_tcp_tso_win_divisor);
...@@ -20,7 +20,8 @@ static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 rtt, ...@@ -20,7 +20,8 @@ static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 rtt,
u32 in_flight, int flag) u32 in_flight, int flag)
{ {
struct tcp_sock *tp = tcp_sk(sk); struct tcp_sock *tp = tcp_sk(sk);
if (in_flight < tp->snd_cwnd)
if (!tcp_is_cwnd_limited(sk, in_flight))
return; return;
if (tp->snd_cwnd <= tp->snd_ssthresh) { if (tp->snd_cwnd <= tp->snd_ssthresh) {
......
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