diff options
author | Ian Gudger <igudger@google.com> | 2018-11-13 18:01:26 -0800 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2018-11-13 18:02:43 -0800 |
commit | 7f60294a7367ee62cc5e0bd21648a68184c4ca5e (patch) | |
tree | 4de6614555660418c24b7c1b88c35e6fe7dcc173 /pkg/tcpip/transport/tcp/snd.go | |
parent | 40f843fc7802271654314e1c339c372e72900845 (diff) |
Implement TCP_NODELAY and TCP_CORK
Previously, TCP_NODELAY was always enabled and we would lie about it being
configurable. TCP_NODELAY is now disabled by default (to match Linux) in the
socket layer so that non-gVisor users don't automatically start using this
questionable optimization.
PiperOrigin-RevId: 221368472
Change-Id: Ib0240f66d94455081f4e0ca94f09d9338b2c1356
Diffstat (limited to 'pkg/tcpip/transport/tcp/snd.go')
-rw-r--r-- | pkg/tcpip/transport/tcp/snd.go | 40 |
1 files changed, 38 insertions, 2 deletions
diff --git a/pkg/tcpip/transport/tcp/snd.go b/pkg/tcpip/transport/tcp/snd.go index 4482d8d07..f6dc7520b 100644 --- a/pkg/tcpip/transport/tcp/snd.go +++ b/pkg/tcpip/transport/tcp/snd.go @@ -17,6 +17,7 @@ package tcp import ( "math" "sync" + "sync/atomic" "time" "gvisor.googlesource.com/gvisor/pkg/sleep" @@ -409,8 +410,6 @@ func (s *sender) sendData() { // We abuse the flags field to determine if we have already // assigned a sequence number to this segment. if seg.flags == 0 { - seg.sequenceNumber = s.sndNxt - seg.flags = flagAck | flagPsh // Merge segments if allowed. if seg.data.Size() != 0 { available := int(seg.sequenceNumber.Size(end)) @@ -418,8 +417,20 @@ func (s *sender) sendData() { available = limit } + // nextTooBig indicates that the next segment was too + // large to entirely fit in the current segment. It would + // be possible to split the next segment and merge the + // portion that fits, but unexpectedly splitting segments + // can have user visible side-effects which can break + // applications. For example, RFC 7766 section 8 says + // that the length and data of a DNS response should be + // sent in the same TCP segment to avoid triggering bugs + // in poorly written DNS implementations. + var nextTooBig bool + for next != nil && next.data.Size() != 0 { if seg.data.Size()+next.data.Size() > available { + nextTooBig = true break } @@ -429,7 +440,32 @@ func (s *sender) sendData() { s.writeList.Remove(next) next = next.Next() } + + if !nextTooBig && seg.data.Size() < available { + // Segment is not full. + if s.outstanding > 0 && atomic.LoadUint32(&s.ep.delay) != 0 { + // Nagle's algorithm. From Wikipedia: + // Nagle's algorithm works by combining a number of + // small outgoing messages and sending them all at + // once. Specifically, as long as there is a sent + // packet for which the sender has received no + // acknowledgment, the sender should keep buffering + // its output until it has a full packet's worth of + // output, thus allowing output to be sent all at + // once. + break + } + if atomic.LoadUint32(&s.ep.cork) != 0 { + // Hold back the segment until full. + break + } + } } + + // Assign flags. We don't do it above so that we can merge + // additional data if Nagle holds the segment. + seg.sequenceNumber = s.sndNxt + seg.flags = flagAck | flagPsh } var segEnd seqnum.Value |