From owner-svn-src-stable-7@FreeBSD.ORG Fri Jun 10 18:47:10 2011 Return-Path: Delivered-To: svn-src-stable-7@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 9DDBD1065678; Fri, 10 Jun 2011 18:47:10 +0000 (UTC) (envelope-from jhb@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 8D0088FC12; Fri, 10 Jun 2011 18:47:10 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id p5AIlAIl038334; Fri, 10 Jun 2011 18:47:10 GMT (envelope-from jhb@svn.freebsd.org) Received: (from jhb@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id p5AIlAMM038329; Fri, 10 Jun 2011 18:47:10 GMT (envelope-from jhb@svn.freebsd.org) Message-Id: <201106101847.p5AIlAMM038329@svn.freebsd.org> From: John Baldwin Date: Fri, 10 Jun 2011 18:47:10 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-7@freebsd.org X-SVN-Group: stable-7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r222935 - stable/7/sys/netinet X-BeenThere: svn-src-stable-7@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for only the 7-stable src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 10 Jun 2011 18:47:10 -0000 Author: jhb Date: Fri Jun 10 18:47:10 2011 New Revision: 222935 URL: http://svn.freebsd.org/changeset/base/222935 Log: MFC 221209: TCP reuses t_rxtshift to determine the backoff timer used for both the persist state and the retransmit timer. However, the code that implements "bad retransmit recovery" only checks t_rxtshift to see if an ACK has been received in during the first retransmit timeout window. As a result, if ticks has wrapped over to a negative value and a socket is in the persist state, it can incorrectly treat an ACK from the remote peer as a "bad retransmit recovery" and restore saved values such as snd_ssthresh and snd_cwnd. However, if the socket has never had a retransmit timeout, then these saved values will be zero, so snd_ssthresh and snd_cwnd will be set to 0. If the socket is in fast recovery (this can be caused by excessive duplicate ACKs such as those fixed by 220794), then each ACK that arrives triggers either NewReno or SACK partial ACK handling which clamps snd_cwnd to be no larger than snd_ssthresh. In effect, the socket's send window is permamently stuck at 0 even though the remote peer is advertising a much larger window and pending data is only sent via TCP window probes (so one byte every few seconds). Fix this by adding a new TCP pcb flag (TF_PREVVALID) that indicates that the various snd_*_prev fields in the pcb are valid and only perform "bad retransmit recovery" if this flag is set in the pcb. The flag is set on the first retransmit timeout that occurs and is cleared on subsequent retransmit timeouts or when entering the persist state. Modified: stable/7/sys/netinet/tcp_input.c stable/7/sys/netinet/tcp_output.c stable/7/sys/netinet/tcp_timer.c stable/7/sys/netinet/tcp_var.h Directory Properties: stable/7/sys/ (props changed) stable/7/sys/cddl/contrib/opensolaris/ (props changed) stable/7/sys/contrib/dev/acpica/ (props changed) stable/7/sys/contrib/pf/ (props changed) Modified: stable/7/sys/netinet/tcp_input.c ============================================================================== --- stable/7/sys/netinet/tcp_input.c Fri Jun 10 18:46:40 2011 (r222934) +++ stable/7/sys/netinet/tcp_input.c Fri Jun 10 18:47:10 2011 (r222935) @@ -1030,6 +1030,7 @@ tcp_do_segment(struct mbuf *m, struct tc * "bad retransmit" recovery. */ if (tp->t_rxtshift == 1 && + tp->t_flags & TF_PREVVALID && (int)(ticks - tp->t_badrxtwin) < 0) { ++tcpstat.tcps_sndrexmitbad; tp->snd_cwnd = tp->snd_cwnd_prev; @@ -1039,6 +1040,7 @@ tcp_do_segment(struct mbuf *m, struct tc if (tp->t_flags & TF_WASFRECOVERY) ENTER_FASTRECOVERY(tp); tp->snd_nxt = tp->snd_max; + tp->t_flags &= ~TF_PREVVALID; tp->t_badrxtwin = 0; } @@ -1962,7 +1964,8 @@ process_ACK: * original cwnd and ssthresh, and proceed to transmit where * we left off. */ - if (tp->t_rxtshift == 1 && (int)(ticks - tp->t_badrxtwin) < 0) { + if (tp->t_rxtshift == 1 && tp->t_flags & TF_PREVVALID && + (int)(ticks - tp->t_badrxtwin) < 0) { ++tcpstat.tcps_sndrexmitbad; tp->snd_cwnd = tp->snd_cwnd_prev; tp->snd_ssthresh = tp->snd_ssthresh_prev; @@ -1970,6 +1973,7 @@ process_ACK: if (tp->t_flags & TF_WASFRECOVERY) ENTER_FASTRECOVERY(tp); tp->snd_nxt = tp->snd_max; + tp->t_flags &= ~TF_PREVVALID; tp->t_badrxtwin = 0; /* XXX probably not required */ } Modified: stable/7/sys/netinet/tcp_output.c ============================================================================== --- stable/7/sys/netinet/tcp_output.c Fri Jun 10 18:46:40 2011 (r222934) +++ stable/7/sys/netinet/tcp_output.c Fri Jun 10 18:47:10 2011 (r222935) @@ -1258,6 +1258,7 @@ tcp_setpersist(struct tcpcb *tp) int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1; int tt; + tp->t_flags &= ~TF_PREVVALID; if (tcp_timer_active(tp, TT_REXMT)) panic("tcp_setpersist: retransmit pending"); /* Modified: stable/7/sys/netinet/tcp_timer.c ============================================================================== --- stable/7/sys/netinet/tcp_timer.c Fri Jun 10 18:46:40 2011 (r222934) +++ stable/7/sys/netinet/tcp_timer.c Fri Jun 10 18:47:10 2011 (r222935) @@ -476,7 +476,9 @@ tcp_timer_rexmt(void * xtp) else tp->t_flags &= ~TF_WASFRECOVERY; tp->t_badrxtwin = ticks + (tp->t_srtt >> (TCP_RTT_SHIFT + 1)); - } + tp->t_flags |= TF_PREVVALID; + } else + tp->t_flags &= ~TF_PREVVALID; tcpstat.tcps_rexmttimeo++; if (tp->t_state == TCPS_SYN_SENT) rexmt = TCP_REXMTVAL(tp) * tcp_syn_backoff[tp->t_rxtshift]; Modified: stable/7/sys/netinet/tcp_var.h ============================================================================== --- stable/7/sys/netinet/tcp_var.h Fri Jun 10 18:46:40 2011 (r222934) +++ stable/7/sys/netinet/tcp_var.h Fri Jun 10 18:47:10 2011 (r222935) @@ -201,6 +201,7 @@ struct tcpcb { #define TF_NEEDSYN 0x000400 /* send SYN (implicit state) */ #define TF_NEEDFIN 0x000800 /* send FIN (implicit state) */ #define TF_NOPUSH 0x001000 /* don't push */ +#define TF_PREVVALID 0x002000 /* saved values for bad rxmit valid */ #define TF_MORETOCOME 0x010000 /* More data to be appended to sock */ #define TF_LQ_OVERFLOW 0x020000 /* listen queue overflow */ #define TF_LASTIDLE 0x040000 /* connection was previously idle */