PPP is slow (and so is TCP!) [with fixes]

Eric Schenk (schenk@rnode84.cs.toronto.edu)
Fri, 12 Apr 1996 02:52:07 -0400


I've spent the last couple of days giving the Linux TCP stack a hard stare,
and I believe I have fixed a number of problems that have existed for
quite some time. While I was at it I implemented a basic version
of the fast retransmit algorithm for single dropped packets.

For those who don't care about explanations, the patches are attached at
the end of this message. They should result in improved performance for
TCP in a variety of situations, including the notorious slow interactive
PPP connections. On FTP gets from SOLARIS machines I see close to a factor
of 2 increase in performance. On puts I see a smaller increase, but this
is limited by the fact that both are now running pretty close to the maximum
possible speed. There's still a bit of room for improvement, but I suspect
that the remaining difficulty is just that Solaris has a lousy algorithm
for estimating round trip times. FTP's to prep.ai.mit.edu now run
full out for me.

The attached patches should be applied over top of the pp87 patch that
Linus posted to linux-kernel on April 10th. Sorry, I'd make it over 1.3.86,
but I wanted to make it easier for Linus to put these patches into the
distribution. If people really want a patch against 1.3.86 ask
me about it.

So, on with the explanations of the patches.

First, let me address the numerous complaints that since 1.3.73 Linux
PPP has been had a very bad interactive response, particularly with
Solaris boxes. (I have been one of those people plagued by this problem,
which was why I started looking into this stuff in the first place.)

It turns out that the slow interactive performance is caused by the
correct application of the Nagle algorithm on the senders side.
The problem is that the Nagle algorithm requires that the
send not send the next character until it gets the ack for the
previous one. Since ACKs are delayed this really slows down
the arrival of the response to interactively typed characters.

There are two possible fixes:
(1) Every time a character gets taken out of the receive buffer
send an ACK to the other side informing them that the receive
window has changed.
(2) Assume that packets with the PSH flag set are part of an interactive
connection, and don't delay ACKs for these packets.

Fix (1) is what the pre 1.3.73 Linux stack did. It is also what
the fix posted by Linus yesterday does. The problem with this fix
is that it can result in multiple ACK packets for each packet
we receive. It turns out that in order to get good aggregate performance
we don't want to inform the other side about changes in the window
unless we have received a packet or a zero window probe from the other side.
Therefore, I followed the BSD lead and implemented fix (2) in these patches.

Now, on to the real bugs. For quite some time (over a year) I have
noticed that FTP gets from Solaris boxes never achieve full throughput,
although if I look at the number of bytes crossing my PPP link the modem
is running full bore. When we still had some SUNOS boxes around here
I was always able to get 1.5-1.6 KBps across an FTP. After looking at
reams of tcpdump output I think I have found and fixed the problems.

First, I found that Solaris TCP was resending packets that it has already
sent. Lots of them. In fact almost every packet is getting sent at
least twice.

It seems that the way we are dealing with out of sequence TCP packets
was triggering a fast retransmit on the Solaris host when we didn't
want it. In particular, a duplicate of a packet we had seen some time ago
was being treated the same as a packet that ended either at or beyond
the currently acknowledged sequence number.
This was resulting in situations where we were sending
three ACKs for a packet, which was resulting in the remote
TCP sending extra copies of the next packet, which was resulting
in our sending three ACKs for the next packet, and so on.
This ends up cutting your bandwidth in half.
I've fixed things by ignoring duplicate packets (those that are to
the left of the last acked sequence number). Note that
we still send ACKs if we get a duplicate of the last packet
we saw, since otherwise a dropped ACK frame would stall the TCP.
We're only ignoring really old packets with this change.

Second, after I fixed the above problem I found that delayed ACKs
where not being dealt with correctly. In particular we're
violating RFC1122 in a couple of places:

o It is required that an ACK packet that is delayed be delayed
by no more than 0.5 seconds. The implementation can be forced
to violate this easily. The problem is that every time new data
arrives we reset the timeout for the ACK. An ACK can get delayed
for as long as we continue to received data packets in order.

o Second, an ACK SHOULD be sent every 2 full sized segments.
We used to test for this, but somewhere along the line the
test got dropped. This will mess up the RTT calculations
for the remote side. I restored the test.

After fixing these problems I was still seeing slow throughput,
although now the link was sitting idle rather than having packet
retransmits. To fix this I found that it was necessary to rewrite
the implementation of receiver side silly window avoidance.
The original code was following the RFC1122 suggested algorithm closely.
This seems to interact badly with TCP implementations descended from BSD code,
which uses a completely different algorithm for receiver side
silly window avoidance. I've changed things to follow the BSD lead
here as well. Strictly speaking this does not violate RFC1122, and
it leads to much better performance in the tests I've been able
to perform so far.

While I was mucking around under the hood and trying to get better
performance, I decided I should implement a "fast retransmit"
algorithm as well. It turned out to be fairly straight forward,
so I've included this in the patch as well. This isn't quite
full blown fast retransmit, but it still makes quite a difference.

So, without further ado, here are the patches, uuencoded and gzipped.

-- eric

---------------------------------------------------------------------------
Eric Schenk www: http://www.cs.toronto.edu/~schenk
Department of Computer Science email: schenk@cs.toronto.edu
University of Toronto

begin 644 tcp_patch.1.3.p87.gz
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
5'!3\H*EY)?BXI!(7`(.%M$PL0P``
`
end