mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add new retransmissions and recovery related metrics.
PiperOrigin-RevId: 236945145 Change-Id: I051760d95154ea5574c8bb6aea526f488af5e07b
This commit is contained in:
committed by
Shentubot
parent
23e66ee96d
commit
1718fdd1a8
@@ -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."),
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user