diff options
author | jhb <jhb@FreeBSD.org> | 2011-04-29 15:40:12 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2011-04-29 15:40:12 +0000 |
commit | 4d70387af28c1c096dfc6978e5367620cda69ff2 (patch) | |
tree | 0fae3721504af39587a4b5740b6628dc072aab74 /sys/netinet/tcp_timer.c | |
parent | bcef521d66b1338ea7358d1a0093997a550257dd (diff) | |
download | FreeBSD-src-4d70387af28c1c096dfc6978e5367620cda69ff2.zip FreeBSD-src-4d70387af28c1c096dfc6978e5367620cda69ff2.tar.gz |
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.
Reviewed by: bz
MFC after: 2 weeks
Diffstat (limited to 'sys/netinet/tcp_timer.c')
-rw-r--r-- | sys/netinet/tcp_timer.c | 4 |
1 files changed, 3 insertions, 1 deletions
diff --git a/sys/netinet/tcp_timer.c b/sys/netinet/tcp_timer.c index 6d0fa1c..5c2c5c2 100644 --- a/sys/netinet/tcp_timer.c +++ b/sys/netinet/tcp_timer.c @@ -524,7 +524,9 @@ tcp_timer_rexmt(void * xtp) else tp->t_flags &= ~TF_WASCRECOVERY; tp->t_badrxtwin = ticks + (tp->t_srtt >> (TCP_RTT_SHIFT + 1)); - } + tp->t_flags |= TF_PREVVALID; + } else + tp->t_flags &= ~TF_PREVVALID; TCPSTAT_INC(tcps_rexmttimeo); if (tp->t_state == TCPS_SYN_SENT) rexmt = TCP_REXMTVAL(tp) * tcp_syn_backoff[tp->t_rxtshift]; |