Add new retransmissions and recovery related metrics.

PiperOrigin-RevId: 236945145
Change-Id: I051760d95154ea5574c8bb6aea526f488af5e07b
This commit is contained in:
Bhasker Hariharan
2019-03-05 16:41:44 -08:00
committed by Shentubot
parent 23e66ee96d
commit 1718fdd1a8
6 changed files with 87 additions and 1 deletions
+6
View File
@@ -82,6 +82,12 @@ var Metrics = tcpip.Stats{
SegmentsSent: mustCreateMetric("/netstack/tcp/segments_sent", "Number of TCP segments sent."),
ResetsSent: mustCreateMetric("/netstack/tcp/resets_sent", "Number of TCP resets sent."),
ResetsReceived: mustCreateMetric("/netstack/tcp/resets_received", "Number of TCP resets received."),
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."),
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."),
},
UDP: tcpip.UDPStats{
PacketsReceived: mustCreateMetric("/netstack/udp/packets_received", "Number of UDP datagrams received via HandlePacket."),
+22
View File
@@ -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.
+3
View File
@@ -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 {
+10
View File
@@ -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)
}
+14 -1
View File
@@ -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() {
+32
View File
@@ -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)