summaryrefslogtreecommitdiffhomepage
path: root/pkg/tcpip/transport/tcp/snd.go
diff options
context:
space:
mode:
authorIan Gudger <igudger@google.com>2018-11-13 18:01:26 -0800
committerShentubot <shentubot@google.com>2018-11-13 18:02:43 -0800
commit7f60294a7367ee62cc5e0bd21648a68184c4ca5e (patch)
tree4de6614555660418c24b7c1b88c35e6fe7dcc173 /pkg/tcpip/transport/tcp/snd.go
parent40f843fc7802271654314e1c339c372e72900845 (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.go40
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