diff options
Diffstat (limited to 'pkg/tcpip')
-rw-r--r-- | pkg/tcpip/tcpip.go | 22 | ||||
-rw-r--r-- | pkg/tcpip/transport/tcp/segment.go | 3 | ||||
-rw-r--r-- | pkg/tcpip/transport/tcp/segment_state.go | 10 | ||||
-rw-r--r-- | pkg/tcpip/transport/tcp/snd.go | 15 | ||||
-rw-r--r-- | pkg/tcpip/transport/tcp/tcp_test.go | 32 |
5 files changed, 81 insertions, 1 deletions
diff --git a/pkg/tcpip/tcpip.go b/pkg/tcpip/tcpip.go index 49cc8705a..7010d1b68 100644 --- a/pkg/tcpip/tcpip.go +++ b/pkg/tcpip/tcpip.go @@ -628,6 +628,28 @@ type TCPStats struct { // ResetsReceived is the number of TCP resets received. ResetsReceived *StatCounter + + // Retransmits is the number of TCP segments retransmitted. + Retransmits *StatCounter + + // FastRecovery is the number of times Fast Recovery was used to + // recover from packet loss. + FastRecovery *StatCounter + + // SACKRecovery is the number of times SACK Recovery was used to + // recover from packet loss. + SACKRecovery *StatCounter + + // SlowStartRetransmits is the number of segments retransmitted in slow + // start. + SlowStartRetransmits *StatCounter + + // FastRetransmit is the number of segments retransmitted in fast + // recovery. + FastRetransmit *StatCounter + + // Timeouts is the number of times the RTO expired. + Timeouts *StatCounter } // UDPStats collects UDP-specific stats. diff --git a/pkg/tcpip/transport/tcp/segment.go b/pkg/tcpip/transport/tcp/segment.go index bd8017f64..a4c4a115c 100644 --- a/pkg/tcpip/transport/tcp/segment.go +++ b/pkg/tcpip/transport/tcp/segment.go @@ -61,6 +61,9 @@ type segment struct { options []byte `state:".([]byte)"` hasNewSACKInfo bool rcvdTime time.Time `state:".(unixTime)"` + // xmitTime is the last transmit time of this segment. A zero value + // indicates that the segment has yet to be transmitted. + xmitTime time.Time `state:".(unixTime)"` } func newSegment(r *stack.Route, id stack.TransportEndpointID, vv buffer.VectorisedView) *segment { diff --git a/pkg/tcpip/transport/tcp/segment_state.go b/pkg/tcpip/transport/tcp/segment_state.go index 7b98a3ec8..68b049f06 100644 --- a/pkg/tcpip/transport/tcp/segment_state.go +++ b/pkg/tcpip/transport/tcp/segment_state.go @@ -70,3 +70,13 @@ func (s *segment) saveRcvdTime() unixTime { func (s *segment) loadRcvdTime(unix unixTime) { s.rcvdTime = time.Unix(unix.second, unix.nano) } + +// saveXmitTime is invoked by stateify. +func (s *segment) saveXmitTime() unixTime { + return unixTime{s.rcvdTime.Unix(), s.rcvdTime.UnixNano()} +} + +// loadXmitTime is invoked by stateify. +func (s *segment) loadXmitTime(unix unixTime) { + s.rcvdTime = time.Unix(unix.second, unix.nano) +} diff --git a/pkg/tcpip/transport/tcp/snd.go b/pkg/tcpip/transport/tcp/snd.go index 8312ae077..2cf12f4a6 100644 --- a/pkg/tcpip/transport/tcp/snd.go +++ b/pkg/tcpip/transport/tcp/snd.go @@ -338,6 +338,8 @@ func (s *sender) resendSegment() { // Resend the segment. if seg := s.writeList.Front(); seg != nil { s.sendSegment(seg.data, seg.flags, seg.sequenceNumber) + s.ep.stack.Stats().TCP.FastRetransmit.Increment() + s.ep.stack.Stats().TCP.Retransmits.Increment() } } @@ -352,6 +354,8 @@ func (s *sender) retransmitTimerExpired() bool { return true } + s.ep.stack.Stats().TCP.Timeouts.Increment() + // Give up if we've waited more than a minute since the last resend. if s.rto >= 60*time.Second { return false @@ -422,7 +426,6 @@ func (s *sender) sendData() { end := s.sndUna.Add(s.sndWnd) var dataSent bool for ; seg != nil && s.outstanding < s.sndCwnd; seg = seg.Next() { - // We abuse the flags field to determine if we have already // assigned a sequence number to this segment. if seg.flags == 0 { @@ -524,6 +527,15 @@ func (s *sender) sendData() { // ensure that no keepalives are sent while there is pending data. s.ep.disableKeepaliveTimer() } + + if !seg.xmitTime.IsZero() { + s.ep.stack.Stats().TCP.Retransmits.Increment() + if s.sndCwnd < s.sndSsthresh { + s.ep.stack.Stats().TCP.SlowStartRetransmits.Increment() + } + } + + seg.xmitTime = time.Now() s.sendSegment(seg.data, seg.flags, seg.sequenceNumber) // Update sndNxt if we actually sent new data (as opposed to @@ -556,6 +568,7 @@ func (s *sender) enterFastRecovery() { s.fr.first = s.sndUna s.fr.last = s.sndNxt - 1 s.fr.maxCwnd = s.sndCwnd + s.outstanding + s.ep.stack.Stats().TCP.FastRecovery.Increment() } func (s *sender) leaveFastRecovery() { diff --git a/pkg/tcpip/transport/tcp/tcp_test.go b/pkg/tcpip/transport/tcp/tcp_test.go index 2011189b7..7f2615ca9 100644 --- a/pkg/tcpip/transport/tcp/tcp_test.go +++ b/pkg/tcpip/transport/tcp/tcp_test.go @@ -2669,6 +2669,18 @@ func TestFastRecovery(t *testing.T) { // Receive the retransmitted packet. c.ReceiveAndCheckPacket(data, rtxOffset, maxPayload) + if got, want := c.Stack().Stats().TCP.FastRetransmit.Value(), uint64(1); got != want { + t.Errorf("got stats.TCP.FastRetransmit.Value = %v, want = %v", got, want) + } + + if got, want := c.Stack().Stats().TCP.Retransmits.Value(), uint64(1); got != want { + t.Errorf("got stats.TCP.Retransmit.Value = %v, want = %v", got, want) + } + + if got, want := c.Stack().Stats().TCP.FastRecovery.Value(), uint64(1); got != want { + t.Errorf("got stats.TCP.FastRecovery.Value = %v, want = %v", got, want) + } + // Now send 7 mode duplicate acks. Each of these should cause a window // inflation by 1 and cause the sender to send an extra packet. for i := 0; i < 7; i++ { @@ -2688,6 +2700,14 @@ func TestFastRecovery(t *testing.T) { // Receive the retransmit due to partial ack. c.ReceiveAndCheckPacket(data, rtxOffset, maxPayload) + if got, want := c.Stack().Stats().TCP.FastRetransmit.Value(), uint64(2); got != want { + t.Errorf("got stats.TCP.FastRetransmit.Value = %v, want = %v", got, want) + } + + if got, want := c.Stack().Stats().TCP.Retransmits.Value(), uint64(2); got != want { + t.Errorf("got stats.TCP.Retransmit.Value = %v, want = %v", got, want) + } + // Receive the 10 extra packets that should have been released due to // the congestion window inflation in recovery. for i := 0; i < 10; i++ { @@ -2799,6 +2819,18 @@ func TestRetransmit(t *testing.T) { rtxOffset := bytesRead - maxPayload*expected c.ReceiveAndCheckPacket(data, rtxOffset, maxPayload) + if got, want := c.Stack().Stats().TCP.Timeouts.Value(), uint64(1); got != want { + t.Errorf("got stats.TCP.Timeouts.Value = %v, want = %v", got, want) + } + + if got, want := c.Stack().Stats().TCP.Retransmits.Value(), uint64(1); got != want { + t.Errorf("got stats.TCP.Retransmit.Value = %v, want = %v", got, want) + } + + if got, want := c.Stack().Stats().TCP.SlowStartRetransmits.Value(), uint64(1); got != want { + t.Errorf("got stats.TCP.SlowStartRetransmits.Value = %v, want = %v", got, want) + } + // Acknowledge half of the pending data. rtxOffset = bytesRead - expected*maxPayload/2 c.SendAck(790, rtxOffset) |