Messages in this thread Patch in this message |  | | | Date | Sat, 31 May 1997 00:14:28 -0700 | | From | (Matthew Ghio) | | Subject | TCP timer bug in 2.0.x |
| |
There's an annoying bug in the TCP driver up thru 2.0.30 which can cause ftp sessions to hang. The general symptom is an ftp download that starts out fast, and gets slower, and slower, and slower until it's basically stuck. This only affects outgoing sends, so you might not notice yourself, but if you have a web/ftp server, your users likely will.
The problem is in the retransmit timer backoff. The timer is set based on the roundtrip time when a packet is transmitted/acked. When a sent packet is not acked, it will retransmit, doubling the time between each retransmit, up to a maximum of 120 seconds.
The bug is that when a retransmitted packet gets acked, the timer doesn't get reset. So if the timer was at 120 seconds, then we'll have to wait a whole 120 seconds before sending the next packet, no matter how quickly the packet was acked.
Unfortulately, it's even worse than that. The retransmit flag will never get cleared as long as there's data in the send queue. This means that if the ftpd keeps the pipe full, the timer will never get reset, and your ftp session will be slowed to a rate of one packet every two minutes. Chances are, you aren't willing to wait that long.
(If you want to test this, start a file transfer to somewhere then type route add somewhere dummy, wait for a few packets to get lost, then correct the routing, and notice how painfully slow your ftp transfer goes afterwards.)
I'm not quite sure what is the best way to fix this. I patched my kernel so that whenever an ack comes in, it resets the timer to 200ms. This does solve the problem, but probably causes a lot of unnecessary retransmits. It would be better to calculate a timeout based on roundtrip time, but that won't work if you have to retransmit the packet.
--- linux/net/ipv4/tcp_input.c.orig Mon Apr 21 14:53:17 1997 +++ linux/net/ipv4/tcp_input.c Mon Apr 21 14:54:15 1997 @@ -1654,6 +1654,9 @@
if (sk->send_head != NULL && (flag&2) && sk->retransmits) { + sk->rto = HZ/5; /* 200 ms */ + tcp_reset_xmit_timer(sk, TIME_WRITE, sk->rto); + tcp_do_retransmit(sk, 1); }
|  |