summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--pkg/sentry/socket/netstack/netstack.go1
-rw-r--r--pkg/tcpip/tcpip.go4
-rw-r--r--pkg/tcpip/transport/tcp/rack.go9
-rw-r--r--pkg/tcpip/transport/tcp/sack_recovery.go2
-rw-r--r--pkg/tcpip/transport/tcp/snd.go38
5 files changed, 40 insertions, 14 deletions
diff --git a/pkg/sentry/socket/netstack/netstack.go b/pkg/sentry/socket/netstack/netstack.go
index cee8120ab..a632b8bcd 100644
--- a/pkg/sentry/socket/netstack/netstack.go
+++ b/pkg/sentry/socket/netstack/netstack.go
@@ -222,6 +222,7 @@ var Metrics = tcpip.Stats{
Retransmits: mustCreateMetric("/netstack/tcp/retransmits", "Number of TCP segments retransmitted."),
FastRecovery: mustCreateMetric("/netstack/tcp/fast_recovery", "Number of times fast recovery was used to recover from packet loss."),
SACKRecovery: mustCreateMetric("/netstack/tcp/sack_recovery", "Number of times SACK recovery was used to recover from packet loss."),
+ TLPRecovery: mustCreateMetric("/netstack/tcp/tlp_recovery", "Number of times tail loss probe triggers recovery from tail loss."),
SlowStartRetransmits: mustCreateMetric("/netstack/tcp/slow_start_retransmits", "Number of segments retransmitted in slow start mode."),
FastRetransmit: mustCreateMetric("/netstack/tcp/fast_retransmit", "Number of TCP segments which were fast retransmitted."),
Timeouts: mustCreateMetric("/netstack/tcp/timeouts", "Number of times RTO expired."),
diff --git a/pkg/tcpip/tcpip.go b/pkg/tcpip/tcpip.go
index d5c2b0487..ba063dc26 100644
--- a/pkg/tcpip/tcpip.go
+++ b/pkg/tcpip/tcpip.go
@@ -1731,6 +1731,10 @@ type TCPStats struct {
// recover from packet loss.
SACKRecovery *StatCounter
+ // TLPRecovery is the number of times recovery was accomplished by the tail
+ // loss probe.
+ TLPRecovery *StatCounter
+
// SlowStartRetransmits is the number of segments retransmitted in slow
// start.
SlowStartRetransmits *StatCounter
diff --git a/pkg/tcpip/transport/tcp/rack.go b/pkg/tcpip/transport/tcp/rack.go
index 9959b60b8..ecabeceb4 100644
--- a/pkg/tcpip/transport/tcp/rack.go
+++ b/pkg/tcpip/transport/tcp/rack.go
@@ -263,7 +263,10 @@ func (s *sender) probeTimerExpired() tcpip.Error {
}
}
- s.postXmit(dataSent)
+ // Whether or not the probe was sent, the sender must arm the resend timer,
+ // not the probe timer. This ensures that the sender does not send repeated,
+ // back-to-back tail loss probes.
+ s.postXmit(dataSent, false /* shouldScheduleProbe */)
return nil
}
@@ -477,7 +480,5 @@ func (rc *rackControl) DoRecovery(_ *segment, fastRetransmit bool) {
snd.sendSegment(seg)
}
- // Rearm the RTO.
- snd.resendTimer.enable(snd.rto)
- snd.postXmit(dataSent)
+ snd.postXmit(dataSent, true /* shouldScheduleProbe */)
}
diff --git a/pkg/tcpip/transport/tcp/sack_recovery.go b/pkg/tcpip/transport/tcp/sack_recovery.go
index 7e813fa96..9d406b0bc 100644
--- a/pkg/tcpip/transport/tcp/sack_recovery.go
+++ b/pkg/tcpip/transport/tcp/sack_recovery.go
@@ -116,5 +116,5 @@ func (sr *sackRecovery) DoRecovery(rcvdSeg *segment, fastRetransmit bool) {
// RFC 6675 recovery algorithm step C 1-5.
end := snd.sndUna.Add(snd.sndWnd)
dataSent := sr.handleSACKRecovery(snd.maxPayloadSize, end)
- snd.postXmit(dataSent)
+ snd.postXmit(dataSent, true /* shouldScheduleProbe */)
}
diff --git a/pkg/tcpip/transport/tcp/snd.go b/pkg/tcpip/transport/tcp/snd.go
index 7911e6b85..83c8deb0e 100644
--- a/pkg/tcpip/transport/tcp/snd.go
+++ b/pkg/tcpip/transport/tcp/snd.go
@@ -966,7 +966,7 @@ func (s *sender) disableZeroWindowProbing() {
s.resendTimer.disable()
}
-func (s *sender) postXmit(dataSent bool) {
+func (s *sender) postXmit(dataSent bool, shouldScheduleProbe bool) {
if dataSent {
// We sent data, so we should stop the keepalive timer to ensure
// that no keepalives are sent while there is pending data.
@@ -980,13 +980,22 @@ func (s *sender) postXmit(dataSent bool) {
s.enableZeroWindowProbing()
}
- // Enable the timer if we have pending data and it's not enabled yet.
- if !s.resendTimer.enabled() && s.sndUna != s.sndNxt {
- s.resendTimer.enable(s.rto)
- }
// If we have no more pending data, start the keepalive timer.
if s.sndUna == s.sndNxt {
s.ep.resetKeepaliveTimer(false)
+ } else {
+ // Enable timers if we have pending data.
+ if shouldScheduleProbe && s.shouldSchedulePTO() {
+ // Schedule PTO after transmitting new data that wasn't itself a TLP probe.
+ s.schedulePTO()
+ } else if !s.resendTimer.enabled() {
+ s.probeTimer.disable()
+ if s.outstanding > 0 {
+ // Enable the resend timer if it's not enabled yet and there is
+ // outstanding data.
+ s.resendTimer.enable(s.rto)
+ }
+ }
}
}
@@ -1029,7 +1038,7 @@ func (s *sender) sendData() {
s.writeNext = seg.Next()
}
- s.postXmit(dataSent)
+ s.postXmit(dataSent, true /* shouldScheduleProbe */)
}
func (s *sender) enterRecovery() {
@@ -1052,6 +1061,10 @@ func (s *sender) enterRecovery() {
s.ep.stack.Stats().TCP.SACKRecovery.Increment()
// Set TLPRxtOut to false according to
// https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.6.1.
+ if s.rc.tlpRxtOut {
+ // The tail loss probe triggered recovery.
+ s.ep.stack.Stats().TCP.TLPRecovery.Increment()
+ }
s.rc.tlpRxtOut = false
return
}
@@ -1415,9 +1428,16 @@ func (s *sender) handleRcvdSegment(rcvdSeg *segment) {
s.updateRTO(elapsed)
}
- // When an ack is received we must rearm the timer.
- // RFC 6298 5.3
- s.resendTimer.enable(s.rto)
+ if s.shouldSchedulePTO() {
+ // Schedule PTO upon receiving an ACK that cumulatively acknowledges data.
+ // See https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.5.1.
+ s.schedulePTO()
+ } else {
+ // When an ack is received we must rearm the timer.
+ // RFC 6298 5.3
+ s.probeTimer.disable()
+ s.resendTimer.enable(s.rto)
+ }
// Remove all acknowledged data from the write list.
acked := s.sndUna.Size(ack)