diff options
| author | Yuchung Cheng <ycheng@google.com> | 2012-12-06 08:45:32 +0000 | 
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2012-12-07 14:39:28 -0500 | 
| commit | 93b174ad71b08e504c2cf6e8a58ecce778b77a40 (patch) | |
| tree | f9e7fd386880ae14d4625de9801a57c5bf5fa105 /net/ipv4/tcp_output.c | |
| parent | 1afa471706963643ceeda7cbbe9c605a1e883d53 (diff) | |
| download | olio-linux-3.10-93b174ad71b08e504c2cf6e8a58ecce778b77a40.tar.xz olio-linux-3.10-93b174ad71b08e504c2cf6e8a58ecce778b77a40.zip  | |
tcp: bug fix Fast Open client retransmission
If SYN-ACK partially acks SYN-data, the client retransmits the
remaining data by tcp_retransmit_skb(). This increments lost recovery
state variables like tp->retrans_out in Open state. If loss recovery
happens before the retransmission is acked, it triggers the WARN_ON
check in tcp_fastretrans_alert(). For example: the client sends
SYN-data, gets SYN-ACK acking only ISN, retransmits data, sends
another 4 data packets and get 3 dupacks.
Since the retransmission is not caused by network drop it should not
update the recovery state variables. Further the server may return a
smaller MSS than the cached MSS used for SYN-data, so the retranmission
needs a loop. Otherwise some data will not be retransmitted until timeout
or other loss recovery events.
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Acked-by: Neal Cardwell <ncardwell@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp_output.c')
| -rw-r--r-- | net/ipv4/tcp_output.c | 15 | 
1 files changed, 10 insertions, 5 deletions
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 2798706cb06..948ac275b9b 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2309,12 +2309,11 @@ static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *to,   * state updates are done by the caller.  Returns non-zero if an   * error occurred which prevented the send.   */ -int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb) +int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)  {  	struct tcp_sock *tp = tcp_sk(sk);  	struct inet_connection_sock *icsk = inet_csk(sk);  	unsigned int cur_mss; -	int err;  	/* Inconslusive MTU probe */  	if (icsk->icsk_mtup.probe_size) { @@ -2387,11 +2386,17 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)  	if (unlikely(NET_IP_ALIGN && ((unsigned long)skb->data & 3))) {  		struct sk_buff *nskb = __pskb_copy(skb, MAX_TCP_HEADER,  						   GFP_ATOMIC); -		err = nskb ? tcp_transmit_skb(sk, nskb, 0, GFP_ATOMIC) : -			     -ENOBUFS; +		return nskb ? tcp_transmit_skb(sk, nskb, 0, GFP_ATOMIC) : +			      -ENOBUFS;  	} else { -		err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC); +		return tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC);  	} +} + +int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb) +{ +	struct tcp_sock *tp = tcp_sk(sk); +	int err = __tcp_retransmit_skb(sk, skb);  	if (err == 0) {  		/* Update global TCP statistics. */  |