gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/tcpip/transport/tcp/reno.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package tcp 16 17 import ( 18 "time" 19 ) 20 21 // renoState stores the variables related to TCP New Reno congestion 22 // control algorithm. 23 // 24 // +stateify savable 25 type renoState struct { 26 s *sender 27 } 28 29 // newRenoCC initializes the state for the NewReno congestion control algorithm. 30 func newRenoCC(s *sender) *renoState { 31 return &renoState{s: s} 32 } 33 34 // updateSlowStart will update the congestion window as per the slow-start 35 // algorithm used by NewReno. If after adjusting the congestion window 36 // we cross the SSthreshold then it will return the number of packets that 37 // must be consumed in congestion avoidance mode. 38 func (r *renoState) updateSlowStart(packetsAcked int) int { 39 // Don't let the congestion window cross into the congestion 40 // avoidance range. 41 newcwnd := r.s.SndCwnd + packetsAcked 42 if newcwnd >= r.s.Ssthresh { 43 newcwnd = r.s.Ssthresh 44 r.s.SndCAAckCount = 0 45 } 46 47 packetsAcked -= newcwnd - r.s.SndCwnd 48 r.s.SndCwnd = newcwnd 49 return packetsAcked 50 } 51 52 // updateCongestionAvoidance will update congestion window in congestion 53 // avoidance mode as described in RFC5681 section 3.1 54 func (r *renoState) updateCongestionAvoidance(packetsAcked int) { 55 // Consume the packets in congestion avoidance mode. 56 r.s.SndCAAckCount += packetsAcked 57 if r.s.SndCAAckCount >= r.s.SndCwnd { 58 r.s.SndCwnd += r.s.SndCAAckCount / r.s.SndCwnd 59 r.s.SndCAAckCount = r.s.SndCAAckCount % r.s.SndCwnd 60 } 61 } 62 63 // reduceSlowStartThreshold reduces the slow-start threshold per RFC 5681, 64 // page 6, eq. 4. It is called when we detect congestion in the network. 65 func (r *renoState) reduceSlowStartThreshold() { 66 r.s.Ssthresh = r.s.Outstanding / 2 67 if r.s.Ssthresh < 2 { 68 r.s.Ssthresh = 2 69 } 70 71 } 72 73 // Update updates the congestion state based on the number of packets that 74 // were acknowledged. 75 // Update implements congestionControl.Update. 76 func (r *renoState) Update(packetsAcked int, _ time.Duration) { 77 if r.s.SndCwnd < r.s.Ssthresh { 78 packetsAcked = r.updateSlowStart(packetsAcked) 79 if packetsAcked == 0 { 80 return 81 } 82 } 83 r.updateCongestionAvoidance(packetsAcked) 84 } 85 86 // HandleLossDetected implements congestionControl.HandleLossDetected. 87 func (r *renoState) HandleLossDetected() { 88 // A retransmit was triggered due to nDupAckThreshold or when RACK 89 // detected loss. Reduce our slow start threshold. 90 r.reduceSlowStartThreshold() 91 } 92 93 // HandleRTOExpired implements congestionControl.HandleRTOExpired. 94 func (r *renoState) HandleRTOExpired() { 95 // We lost a packet, so reduce ssthresh. 96 r.reduceSlowStartThreshold() 97 98 // Reduce the congestion window to 1, i.e., enter slow-start. Per 99 // RFC 5681, page 7, we must use 1 regardless of the value of the 100 // initial congestion window. 101 r.s.SndCwnd = 1 102 } 103 104 // PostRecovery implements congestionControl.PostRecovery. 105 func (r *renoState) PostRecovery() { 106 // noop. 107 }