diff options
-rw-r--r-- | pkg/sentry/socket/netstack/netstack.go | 1 | ||||
-rw-r--r-- | pkg/tcpip/tcpip.go | 4 | ||||
-rw-r--r-- | pkg/tcpip/transport/tcp/rack.go | 9 | ||||
-rw-r--r-- | pkg/tcpip/transport/tcp/sack_recovery.go | 2 | ||||
-rw-r--r-- | pkg/tcpip/transport/tcp/snd.go | 38 |
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) |