github.com/metacubex/mihomo@v1.18.5/transport/tuic/congestion/bbr_sender.go (about) 1 package congestion 2 3 // src from https://quiche.googlesource.com/quiche.git/+/66dea072431f94095dfc3dd2743cb94ef365f7ef/quic/core/congestion_control/bbr_sender.cc 4 5 import ( 6 "fmt" 7 "math" 8 "net" 9 "time" 10 11 "github.com/metacubex/quic-go/congestion" 12 "github.com/zhangyunhao116/fastrand" 13 ) 14 15 const ( 16 // InitialMaxDatagramSize is the default maximum packet size used in QUIC for congestion window computations in bytes. 17 InitialMaxDatagramSize = 1252 18 InitialPacketSizeIPv4 = 1252 19 InitialPacketSizeIPv6 = 1232 20 InitialCongestionWindow = 32 21 DefaultBBRMaxCongestionWindow = 10000 22 ) 23 24 func GetInitialPacketSize(addr net.Addr) congestion.ByteCount { 25 maxSize := congestion.ByteCount(1200) 26 // If this is not a UDP address, we don't know anything about the MTU. 27 // Use the minimum size of an Initial packet as the max packet size. 28 if udpAddr, ok := addr.(*net.UDPAddr); ok { 29 if udpAddr.IP.To4() != nil { 30 maxSize = InitialPacketSizeIPv4 31 } else { 32 maxSize = InitialPacketSizeIPv6 33 } 34 } 35 return congestion.ByteCount(maxSize) 36 } 37 38 var ( 39 40 // Default initial rtt used before any samples are received. 41 InitialRtt = 100 * time.Millisecond 42 43 // The gain used for the STARTUP, equal to 4*ln(2). 44 DefaultHighGain = 2.77 45 46 // The gain used in STARTUP after loss has been detected. 47 // 1.5 is enough to allow for 25% exogenous loss and still observe a 25% growth 48 // in measured bandwidth. 49 StartupAfterLossGain = 1.5 50 51 // The cycle of gains used during the PROBE_BW stage. 52 PacingGain = []float64{1.25, 0.75, 1, 1, 1, 1, 1, 1} 53 54 // The length of the gain cycle. 55 GainCycleLength = len(PacingGain) 56 57 // The size of the bandwidth filter window, in round-trips. 58 BandwidthWindowSize = GainCycleLength + 2 59 60 // The time after which the current min_rtt value expires. 61 MinRttExpiry = 10 * time.Second 62 63 // The minimum time the connection can spend in PROBE_RTT mode. 64 ProbeRttTime = time.Millisecond * 200 65 66 // If the bandwidth does not increase by the factor of |kStartupGrowthTarget| 67 // within |kRoundTripsWithoutGrowthBeforeExitingStartup| rounds, the connection 68 // will exit the STARTUP mode. 69 StartupGrowthTarget = 1.25 70 RoundTripsWithoutGrowthBeforeExitingStartup = int64(3) 71 72 // Coefficient of target congestion window to use when basing PROBE_RTT on BDP. 73 ModerateProbeRttMultiplier = 0.75 74 75 // Coefficient to determine if a new RTT is sufficiently similar to min_rtt that 76 // we don't need to enter PROBE_RTT. 77 SimilarMinRttThreshold = 1.125 78 79 // Congestion window gain for QUIC BBR during PROBE_BW phase. 80 DefaultCongestionWindowGainConst = 2.0 81 ) 82 83 type bbrMode int 84 85 const ( 86 // Startup phase of the connection. 87 STARTUP = iota 88 // After achieving the highest possible bandwidth during the startup, lower 89 // the pacing rate in order to drain the queue. 90 DRAIN 91 // Cruising mode. 92 PROBE_BW 93 // Temporarily slow down sending in order to empty the buffer and measure 94 // the real minimum RTT. 95 PROBE_RTT 96 ) 97 98 type bbrRecoveryState int 99 100 const ( 101 // Do not limit. 102 NOT_IN_RECOVERY = iota 103 104 // Allow an extra outstanding byte for each byte acknowledged. 105 CONSERVATION 106 107 // Allow two extra outstanding bytes for each byte acknowledged (slow 108 // start). 109 GROWTH 110 ) 111 112 type bbrSender struct { 113 mode bbrMode 114 clock Clock 115 rttStats congestion.RTTStatsProvider 116 bytesInFlight congestion.ByteCount 117 // return total bytes of unacked packets. 118 //GetBytesInFlight func() congestion.ByteCount 119 // Bandwidth sampler provides BBR with the bandwidth measurements at 120 // individual points. 121 sampler *BandwidthSampler 122 // The number of the round trips that have occurred during the connection. 123 roundTripCount int64 124 // The packet number of the most recently sent packet. 125 lastSendPacket congestion.PacketNumber 126 // Acknowledgement of any packet after |current_round_trip_end_| will cause 127 // the round trip counter to advance. 128 currentRoundTripEnd congestion.PacketNumber 129 // The filter that tracks the maximum bandwidth over the multiple recent 130 // round-trips. 131 maxBandwidth *WindowedFilter 132 // Tracks the maximum number of bytes acked faster than the sending rate. 133 maxAckHeight *WindowedFilter 134 // The time this aggregation started and the number of bytes acked during it. 135 aggregationEpochStartTime time.Time 136 aggregationEpochBytes congestion.ByteCount 137 // Minimum RTT estimate. Automatically expires within 10 seconds (and 138 // triggers PROBE_RTT mode) if no new value is sampled during that period. 139 minRtt time.Duration 140 // The time at which the current value of |min_rtt_| was assigned. 141 minRttTimestamp time.Time 142 // The maximum allowed number of bytes in flight. 143 congestionWindow congestion.ByteCount 144 // The initial value of the |congestion_window_|. 145 initialCongestionWindow congestion.ByteCount 146 // The largest value the |congestion_window_| can achieve. 147 initialMaxCongestionWindow congestion.ByteCount 148 // The smallest value the |congestion_window_| can achieve. 149 //minCongestionWindow congestion.ByteCount 150 // The pacing gain applied during the STARTUP phase. 151 highGain float64 152 // The CWND gain applied during the STARTUP phase. 153 highCwndGain float64 154 // The pacing gain applied during the DRAIN phase. 155 drainGain float64 156 // The current pacing rate of the connection. 157 pacingRate Bandwidth 158 // The gain currently applied to the pacing rate. 159 pacingGain float64 160 // The gain currently applied to the congestion window. 161 congestionWindowGain float64 162 // The gain used for the congestion window during PROBE_BW. Latched from 163 // quic_bbr_cwnd_gain flag. 164 congestionWindowGainConst float64 165 // The number of RTTs to stay in STARTUP mode. Defaults to 3. 166 numStartupRtts int64 167 // If true, exit startup if 1RTT has passed with no bandwidth increase and 168 // the connection is in recovery. 169 exitStartupOnLoss bool 170 // Number of round-trips in PROBE_BW mode, used for determining the current 171 // pacing gain cycle. 172 cycleCurrentOffset int 173 // The time at which the last pacing gain cycle was started. 174 lastCycleStart time.Time 175 // Indicates whether the connection has reached the full bandwidth mode. 176 isAtFullBandwidth bool 177 // Number of rounds during which there was no significant bandwidth increase. 178 roundsWithoutBandwidthGain int64 179 // The bandwidth compared to which the increase is measured. 180 bandwidthAtLastRound Bandwidth 181 // Set to true upon exiting quiescence. 182 exitingQuiescence bool 183 // Time at which PROBE_RTT has to be exited. Setting it to zero indicates 184 // that the time is yet unknown as the number of packets in flight has not 185 // reached the required value. 186 exitProbeRttAt time.Time 187 // Indicates whether a round-trip has passed since PROBE_RTT became active. 188 probeRttRoundPassed bool 189 // Indicates whether the most recent bandwidth sample was marked as 190 // app-limited. 191 lastSampleIsAppLimited bool 192 // Indicates whether any non app-limited samples have been recorded. 193 hasNoAppLimitedSample bool 194 // Indicates app-limited calls should be ignored as long as there's 195 // enough data inflight to see more bandwidth when necessary. 196 flexibleAppLimited bool 197 // Current state of recovery. 198 recoveryState bbrRecoveryState 199 // Receiving acknowledgement of a packet after |end_recovery_at_| will cause 200 // BBR to exit the recovery mode. A value above zero indicates at least one 201 // loss has been detected, so it must not be set back to zero. 202 endRecoveryAt congestion.PacketNumber 203 // A window used to limit the number of bytes in flight during loss recovery. 204 recoveryWindow congestion.ByteCount 205 // If true, consider all samples in recovery app-limited. 206 isAppLimitedRecovery bool 207 // When true, pace at 1.5x and disable packet conservation in STARTUP. 208 slowerStartup bool 209 // When true, disables packet conservation in STARTUP. 210 rateBasedStartup bool 211 // When non-zero, decreases the rate in STARTUP by the total number of bytes 212 // lost in STARTUP divided by CWND. 213 startupRateReductionMultiplier int64 214 // Sum of bytes lost in STARTUP. 215 startupBytesLost congestion.ByteCount 216 // When true, add the most recent ack aggregation measurement during STARTUP. 217 enableAckAggregationDuringStartup bool 218 // When true, expire the windowed ack aggregation values in STARTUP when 219 // bandwidth increases more than 25%. 220 expireAckAggregationInStartup bool 221 // If true, will not exit low gain mode until bytes_in_flight drops below BDP 222 // or it's time for high gain mode. 223 drainToTarget bool 224 // If true, use a CWND of 0.75*BDP during probe_rtt instead of 4 packets. 225 probeRttBasedOnBdp bool 226 // If true, skip probe_rtt and update the timestamp of the existing min_rtt to 227 // now if min_rtt over the last cycle is within 12.5% of the current min_rtt. 228 // Even if the min_rtt is 12.5% too low, the 25% gain cycling and 2x CWND gain 229 // should overcome an overly small min_rtt. 230 probeRttSkippedIfSimilarRtt bool 231 // If true, disable PROBE_RTT entirely as long as the connection was recently 232 // app limited. 233 probeRttDisabledIfAppLimited bool 234 appLimitedSinceLastProbeRtt bool 235 minRttSinceLastProbeRtt time.Duration 236 // Latched value of --quic_always_get_bw_sample_when_acked. 237 alwaysGetBwSampleWhenAcked bool 238 239 pacer *pacer 240 241 maxDatagramSize congestion.ByteCount 242 } 243 244 func NewBBRSender( 245 clock Clock, 246 initialMaxDatagramSize, 247 initialCongestionWindow, 248 initialMaxCongestionWindow congestion.ByteCount, 249 ) *bbrSender { 250 b := &bbrSender{ 251 mode: STARTUP, 252 clock: clock, 253 sampler: NewBandwidthSampler(), 254 maxBandwidth: NewWindowedFilter(int64(BandwidthWindowSize), MaxFilter), 255 maxAckHeight: NewWindowedFilter(int64(BandwidthWindowSize), MaxFilter), 256 congestionWindow: initialCongestionWindow, 257 initialCongestionWindow: initialCongestionWindow, 258 highGain: DefaultHighGain, 259 highCwndGain: DefaultHighGain, 260 drainGain: 1.0 / DefaultHighGain, 261 pacingGain: 1.0, 262 congestionWindowGain: 1.0, 263 congestionWindowGainConst: DefaultCongestionWindowGainConst, 264 numStartupRtts: RoundTripsWithoutGrowthBeforeExitingStartup, 265 recoveryState: NOT_IN_RECOVERY, 266 recoveryWindow: initialMaxCongestionWindow, 267 minRttSinceLastProbeRtt: InfiniteRTT, 268 maxDatagramSize: initialMaxDatagramSize, 269 } 270 b.pacer = newPacer(b.BandwidthEstimate) 271 return b 272 } 273 274 func (b *bbrSender) maxCongestionWindow() congestion.ByteCount { 275 return b.maxDatagramSize * DefaultBBRMaxCongestionWindow 276 } 277 278 func (b *bbrSender) minCongestionWindow() congestion.ByteCount { 279 return b.maxDatagramSize * b.initialCongestionWindow 280 } 281 282 func (b *bbrSender) SetRTTStatsProvider(provider congestion.RTTStatsProvider) { 283 b.rttStats = provider 284 } 285 286 func (b *bbrSender) GetBytesInFlight() congestion.ByteCount { 287 return b.bytesInFlight 288 } 289 290 // TimeUntilSend returns when the next packet should be sent. 291 func (b *bbrSender) TimeUntilSend(bytesInFlight congestion.ByteCount) time.Time { 292 b.bytesInFlight = bytesInFlight 293 return b.pacer.TimeUntilSend() 294 } 295 296 func (b *bbrSender) HasPacingBudget(now time.Time) bool { 297 return b.pacer.Budget(now) >= b.maxDatagramSize 298 } 299 300 func (b *bbrSender) SetMaxDatagramSize(s congestion.ByteCount) { 301 if s < b.maxDatagramSize { 302 panic(fmt.Sprintf("congestion BUG: decreased max datagram size from %d to %d", b.maxDatagramSize, s)) 303 } 304 cwndIsMinCwnd := b.congestionWindow == b.minCongestionWindow() 305 b.maxDatagramSize = s 306 if cwndIsMinCwnd { 307 b.congestionWindow = b.minCongestionWindow() 308 } 309 b.pacer.SetMaxDatagramSize(s) 310 } 311 312 func (b *bbrSender) OnPacketSent(sentTime time.Time, bytesInFlight congestion.ByteCount, packetNumber congestion.PacketNumber, bytes congestion.ByteCount, isRetransmittable bool) { 313 b.pacer.SentPacket(sentTime, bytes) 314 b.lastSendPacket = packetNumber 315 316 b.bytesInFlight = bytesInFlight 317 if bytesInFlight == 0 && b.sampler.isAppLimited { 318 b.exitingQuiescence = true 319 } 320 321 if b.aggregationEpochStartTime.IsZero() { 322 b.aggregationEpochStartTime = sentTime 323 } 324 325 b.sampler.OnPacketSent(sentTime, packetNumber, bytes, bytesInFlight, isRetransmittable) 326 } 327 328 func (b *bbrSender) CanSend(bytesInFlight congestion.ByteCount) bool { 329 b.bytesInFlight = bytesInFlight 330 return bytesInFlight < b.GetCongestionWindow() 331 } 332 333 func (b *bbrSender) GetCongestionWindow() congestion.ByteCount { 334 if b.mode == PROBE_RTT { 335 return b.ProbeRttCongestionWindow() 336 } 337 338 if b.InRecovery() && !(b.rateBasedStartup && b.mode == STARTUP) { 339 return minByteCount(b.congestionWindow, b.recoveryWindow) 340 } 341 342 return b.congestionWindow 343 } 344 345 func (b *bbrSender) MaybeExitSlowStart() { 346 347 } 348 349 func (b *bbrSender) OnPacketAcked(number congestion.PacketNumber, ackedBytes congestion.ByteCount, priorInFlight congestion.ByteCount, eventTime time.Time) { 350 // Stub 351 } 352 353 func (b *bbrSender) OnCongestionEvent(number congestion.PacketNumber, lostBytes congestion.ByteCount, priorInFlight congestion.ByteCount) { 354 // Stub 355 } 356 357 func (b *bbrSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime time.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) { 358 totalBytesAckedBefore := b.sampler.totalBytesAcked 359 isRoundStart, minRttExpired := false, false 360 361 if lostPackets != nil { 362 b.DiscardLostPackets(lostPackets) 363 } 364 365 // Input the new data into the BBR model of the connection. 366 var excessAcked congestion.ByteCount 367 if len(ackedPackets) > 0 { 368 lastAckedPacket := ackedPackets[len(ackedPackets)-1].PacketNumber 369 isRoundStart = b.UpdateRoundTripCounter(lastAckedPacket) 370 minRttExpired = b.UpdateBandwidthAndMinRtt(eventTime, ackedPackets) 371 b.UpdateRecoveryState(len(lostPackets) > 0, isRoundStart) 372 bytesAcked := b.sampler.totalBytesAcked - totalBytesAckedBefore 373 excessAcked = b.UpdateAckAggregationBytes(eventTime, bytesAcked) 374 } 375 376 // Handle logic specific to PROBE_BW mode. 377 if b.mode == PROBE_BW { 378 b.UpdateGainCyclePhase(eventTime, priorInFlight, len(lostPackets) > 0) 379 } 380 381 // Handle logic specific to STARTUP and DRAIN modes. 382 if isRoundStart && !b.isAtFullBandwidth { 383 b.CheckIfFullBandwidthReached() 384 } 385 b.MaybeExitStartupOrDrain(eventTime) 386 387 // Handle logic specific to PROBE_RTT. 388 b.MaybeEnterOrExitProbeRtt(eventTime, isRoundStart, minRttExpired) 389 390 // Calculate number of packets acked and lost. 391 bytesAcked := b.sampler.totalBytesAcked - totalBytesAckedBefore 392 bytesLost := congestion.ByteCount(0) 393 for _, packet := range lostPackets { 394 bytesLost += packet.BytesLost 395 } 396 397 // After the model is updated, recalculate the pacing rate and congestion 398 // window. 399 b.CalculatePacingRate() 400 b.CalculateCongestionWindow(bytesAcked, excessAcked) 401 b.CalculateRecoveryWindow(bytesAcked, bytesLost) 402 } 403 404 //func (b *bbrSender) SetNumEmulatedConnections(n int) { 405 // 406 //} 407 408 func (b *bbrSender) OnRetransmissionTimeout(packetsRetransmitted bool) { 409 410 } 411 412 //func (b *bbrSender) OnConnectionMigration() { 413 // 414 //} 415 416 //// Experiments 417 //func (b *bbrSender) SetSlowStartLargeReduction(enabled bool) { 418 // 419 //} 420 421 //func (b *bbrSender) BandwidthEstimate() Bandwidth { 422 // return Bandwidth(b.maxBandwidth.GetBest()) 423 //} 424 425 // BandwidthEstimate returns the current bandwidth estimate 426 func (b *bbrSender) BandwidthEstimate() Bandwidth { 427 if b.rttStats == nil { 428 return infBandwidth 429 } 430 srtt := b.rttStats.SmoothedRTT() 431 if srtt == 0 { 432 // If we haven't measured an rtt, the bandwidth estimate is unknown. 433 return infBandwidth 434 } 435 return BandwidthFromDelta(b.GetCongestionWindow(), srtt) 436 } 437 438 //func (b *bbrSender) HybridSlowStart() *HybridSlowStart { 439 // return nil 440 //} 441 442 //func (b *bbrSender) SlowstartThreshold() congestion.ByteCount { 443 // return 0 444 //} 445 446 //func (b *bbrSender) RenoBeta() float32 { 447 // return 0.0 448 //} 449 450 func (b *bbrSender) InRecovery() bool { 451 return b.recoveryState != NOT_IN_RECOVERY 452 } 453 454 func (b *bbrSender) InSlowStart() bool { 455 return b.mode == STARTUP 456 } 457 458 //func (b *bbrSender) ShouldSendProbingPacket() bool { 459 // if b.pacingGain <= 1 { 460 // return false 461 // } 462 // // TODO(b/77975811): If the pipe is highly under-utilized, consider not 463 // // sending a probing transmission, because the extra bandwidth is not needed. 464 // // If flexible_app_limited is enabled, check if the pipe is sufficiently full. 465 // if b.flexibleAppLimited { 466 // return !b.IsPipeSufficientlyFull() 467 // } else { 468 // return true 469 // } 470 //} 471 472 //func (b *bbrSender) IsPipeSufficientlyFull() bool { 473 // // See if we need more bytes in flight to see more bandwidth. 474 // if b.mode == STARTUP { 475 // // STARTUP exits if it doesn't observe a 25% bandwidth increase, so the CWND 476 // // must be more than 25% above the target. 477 // return b.GetBytesInFlight() >= b.GetTargetCongestionWindow(1.5) 478 // } 479 // if b.pacingGain > 1 { 480 // // Super-unity PROBE_BW doesn't exit until 1.25 * BDP is achieved. 481 // return b.GetBytesInFlight() >= b.GetTargetCongestionWindow(b.pacingGain) 482 // } 483 // // If bytes_in_flight are above the target congestion window, it should be 484 // // possible to observe the same or more bandwidth if it's available. 485 // return b.GetBytesInFlight() >= b.GetTargetCongestionWindow(1.1) 486 //} 487 488 //func (b *bbrSender) SetFromConfig() { 489 // // TODO: not impl. 490 //} 491 492 func (b *bbrSender) UpdateRoundTripCounter(lastAckedPacket congestion.PacketNumber) bool { 493 if b.currentRoundTripEnd == 0 || lastAckedPacket > b.currentRoundTripEnd { 494 b.currentRoundTripEnd = lastAckedPacket 495 b.roundTripCount++ 496 // if b.rttStats != nil && b.InSlowStart() { 497 // TODO: ++stats_->slowstart_num_rtts; 498 // } 499 return true 500 } 501 return false 502 } 503 504 func (b *bbrSender) UpdateBandwidthAndMinRtt(now time.Time, ackedPackets []congestion.AckedPacketInfo) bool { 505 sampleMinRtt := InfiniteRTT 506 507 for _, packet := range ackedPackets { 508 if !b.alwaysGetBwSampleWhenAcked && packet.BytesAcked == 0 { 509 // Skip acked packets with 0 in flight bytes when updating bandwidth. 510 return false 511 } 512 bandwidthSample := b.sampler.OnPacketAcked(now, packet.PacketNumber) 513 if b.alwaysGetBwSampleWhenAcked && !bandwidthSample.stateAtSend.isValid { 514 // From the sampler's perspective, the packet has never been sent, or the 515 // packet has been acked or marked as lost previously. 516 return false 517 } 518 b.lastSampleIsAppLimited = bandwidthSample.stateAtSend.isAppLimited 519 // has_non_app_limited_sample_ |= 520 // !bandwidth_sample.state_at_send.is_app_limited; 521 if !bandwidthSample.stateAtSend.isAppLimited { 522 b.hasNoAppLimitedSample = true 523 } 524 if bandwidthSample.rtt > 0 { 525 sampleMinRtt = minRtt(sampleMinRtt, bandwidthSample.rtt) 526 } 527 if !bandwidthSample.stateAtSend.isAppLimited || bandwidthSample.bandwidth > b.BandwidthEstimate() { 528 b.maxBandwidth.Update(int64(bandwidthSample.bandwidth), b.roundTripCount) 529 } 530 } 531 532 // If none of the RTT samples are valid, return immediately. 533 if sampleMinRtt == InfiniteRTT { 534 return false 535 } 536 537 b.minRttSinceLastProbeRtt = minRtt(b.minRttSinceLastProbeRtt, sampleMinRtt) 538 // Do not expire min_rtt if none was ever available. 539 minRttExpired := b.minRtt > 0 && (now.After(b.minRttTimestamp.Add(MinRttExpiry))) 540 if minRttExpired || sampleMinRtt < b.minRtt || b.minRtt == 0 { 541 if minRttExpired && b.ShouldExtendMinRttExpiry() { 542 minRttExpired = false 543 } else { 544 b.minRtt = sampleMinRtt 545 } 546 b.minRttTimestamp = now 547 // Reset since_last_probe_rtt fields. 548 b.minRttSinceLastProbeRtt = InfiniteRTT 549 b.appLimitedSinceLastProbeRtt = false 550 } 551 552 return minRttExpired 553 } 554 555 func (b *bbrSender) ShouldExtendMinRttExpiry() bool { 556 if b.probeRttDisabledIfAppLimited && b.appLimitedSinceLastProbeRtt { 557 // Extend the current min_rtt if we've been app limited recently. 558 return true 559 } 560 561 minRttIncreasedSinceLastProbe := b.minRttSinceLastProbeRtt > time.Duration(float64(b.minRtt)*SimilarMinRttThreshold) 562 if b.probeRttSkippedIfSimilarRtt && b.appLimitedSinceLastProbeRtt && !minRttIncreasedSinceLastProbe { 563 // Extend the current min_rtt if we've been app limited recently and an rtt 564 // has been measured in that time that's less than 12.5% more than the 565 // current min_rtt. 566 return true 567 } 568 569 return false 570 } 571 572 func (b *bbrSender) DiscardLostPackets(lostPackets []congestion.LostPacketInfo) { 573 for _, packet := range lostPackets { 574 b.sampler.OnCongestionEvent(packet.PacketNumber) 575 if b.mode == STARTUP { 576 // if b.rttStats != nil { 577 // TODO: slow start. 578 // } 579 if b.startupRateReductionMultiplier != 0 { 580 b.startupBytesLost += packet.BytesLost 581 } 582 } 583 } 584 } 585 586 func (b *bbrSender) UpdateRecoveryState(hasLosses, isRoundStart bool) { 587 // Exit recovery when there are no losses for a round. 588 if !hasLosses { 589 b.endRecoveryAt = b.lastSendPacket 590 } 591 switch b.recoveryState { 592 case NOT_IN_RECOVERY: 593 // Enter conservation on the first loss. 594 if hasLosses { 595 b.recoveryState = CONSERVATION 596 // This will cause the |recovery_window_| to be set to the correct 597 // value in CalculateRecoveryWindow(). 598 b.recoveryWindow = 0 599 // Since the conservation phase is meant to be lasting for a whole 600 // round, extend the current round as if it were started right now. 601 b.currentRoundTripEnd = b.lastSendPacket 602 if false && b.lastSampleIsAppLimited { 603 b.isAppLimitedRecovery = true 604 } 605 } 606 case CONSERVATION: 607 if isRoundStart { 608 b.recoveryState = GROWTH 609 } 610 fallthrough 611 case GROWTH: 612 // Exit recovery if appropriate. 613 if !hasLosses && b.lastSendPacket > b.endRecoveryAt { 614 b.recoveryState = NOT_IN_RECOVERY 615 b.isAppLimitedRecovery = false 616 } 617 } 618 619 if b.recoveryState != NOT_IN_RECOVERY && b.isAppLimitedRecovery { 620 b.sampler.OnAppLimited() 621 } 622 } 623 624 func (b *bbrSender) UpdateAckAggregationBytes(ackTime time.Time, ackedBytes congestion.ByteCount) congestion.ByteCount { 625 // Compute how many bytes are expected to be delivered, assuming max bandwidth 626 // is correct. 627 expectedAckedBytes := congestion.ByteCount(b.maxBandwidth.GetBest()) * 628 congestion.ByteCount((ackTime.Sub(b.aggregationEpochStartTime))) 629 // Reset the current aggregation epoch as soon as the ack arrival rate is less 630 // than or equal to the max bandwidth. 631 if b.aggregationEpochBytes <= expectedAckedBytes { 632 // Reset to start measuring a new aggregation epoch. 633 b.aggregationEpochBytes = ackedBytes 634 b.aggregationEpochStartTime = ackTime 635 return 0 636 } 637 // Compute how many extra bytes were delivered vs max bandwidth. 638 // Include the bytes most recently acknowledged to account for stretch acks. 639 b.aggregationEpochBytes += ackedBytes 640 b.maxAckHeight.Update(int64(b.aggregationEpochBytes-expectedAckedBytes), b.roundTripCount) 641 return b.aggregationEpochBytes - expectedAckedBytes 642 } 643 644 func (b *bbrSender) UpdateGainCyclePhase(now time.Time, priorInFlight congestion.ByteCount, hasLosses bool) { 645 bytesInFlight := b.GetBytesInFlight() 646 // In most cases, the cycle is advanced after an RTT passes. 647 shouldAdvanceGainCycling := now.Sub(b.lastCycleStart) > b.GetMinRtt() 648 649 // If the pacing gain is above 1.0, the connection is trying to probe the 650 // bandwidth by increasing the number of bytes in flight to at least 651 // pacing_gain * BDP. Make sure that it actually reaches the target, as long 652 // as there are no losses suggesting that the buffers are not able to hold 653 // that much. 654 if b.pacingGain > 1.0 && !hasLosses && priorInFlight < b.GetTargetCongestionWindow(b.pacingGain) { 655 shouldAdvanceGainCycling = false 656 } 657 // If pacing gain is below 1.0, the connection is trying to drain the extra 658 // queue which could have been incurred by probing prior to it. If the number 659 // of bytes in flight falls down to the estimated BDP value earlier, conclude 660 // that the queue has been successfully drained and exit this cycle early. 661 if b.pacingGain < 1.0 && bytesInFlight <= b.GetTargetCongestionWindow(1.0) { 662 shouldAdvanceGainCycling = true 663 } 664 665 if shouldAdvanceGainCycling { 666 b.cycleCurrentOffset = (b.cycleCurrentOffset + 1) % GainCycleLength 667 b.lastCycleStart = now 668 // Stay in low gain mode until the target BDP is hit. 669 // Low gain mode will be exited immediately when the target BDP is achieved. 670 if b.drainToTarget && b.pacingGain < 1.0 && PacingGain[b.cycleCurrentOffset] == 1.0 && 671 bytesInFlight > b.GetTargetCongestionWindow(1.0) { 672 return 673 } 674 b.pacingGain = PacingGain[b.cycleCurrentOffset] 675 } 676 } 677 678 func (b *bbrSender) GetTargetCongestionWindow(gain float64) congestion.ByteCount { 679 bdp := congestion.ByteCount(b.GetMinRtt()) * congestion.ByteCount(b.BandwidthEstimate()) 680 congestionWindow := congestion.ByteCount(gain * float64(bdp)) 681 682 // BDP estimate will be zero if no bandwidth samples are available yet. 683 if congestionWindow == 0 { 684 congestionWindow = congestion.ByteCount(gain * float64(b.initialCongestionWindow)) 685 } 686 687 return maxByteCount(congestionWindow, b.minCongestionWindow()) 688 } 689 690 func (b *bbrSender) CheckIfFullBandwidthReached() { 691 if b.lastSampleIsAppLimited { 692 return 693 } 694 695 target := Bandwidth(float64(b.bandwidthAtLastRound) * StartupGrowthTarget) 696 if b.BandwidthEstimate() >= target { 697 b.bandwidthAtLastRound = b.BandwidthEstimate() 698 b.roundsWithoutBandwidthGain = 0 699 if b.expireAckAggregationInStartup { 700 // Expire old excess delivery measurements now that bandwidth increased. 701 b.maxAckHeight.Reset(0, b.roundTripCount) 702 } 703 return 704 } 705 b.roundsWithoutBandwidthGain++ 706 if b.roundsWithoutBandwidthGain >= b.numStartupRtts || (b.exitStartupOnLoss && b.InRecovery()) { 707 b.isAtFullBandwidth = true 708 } 709 } 710 711 func (b *bbrSender) MaybeExitStartupOrDrain(now time.Time) { 712 if b.mode == STARTUP && b.isAtFullBandwidth { 713 b.OnExitStartup(now) 714 b.mode = DRAIN 715 b.pacingGain = b.drainGain 716 b.congestionWindowGain = b.highCwndGain 717 } 718 if b.mode == DRAIN && b.GetBytesInFlight() <= b.GetTargetCongestionWindow(1) { 719 b.EnterProbeBandwidthMode(now) 720 } 721 } 722 723 func (b *bbrSender) EnterProbeBandwidthMode(now time.Time) { 724 b.mode = PROBE_BW 725 b.congestionWindowGain = b.congestionWindowGainConst 726 727 // Pick a random offset for the gain cycle out of {0, 2..7} range. 1 is 728 // excluded because in that case increased gain and decreased gain would not 729 // follow each other. 730 b.cycleCurrentOffset = fastrand.Int() % (GainCycleLength - 1) 731 if b.cycleCurrentOffset >= 1 { 732 b.cycleCurrentOffset += 1 733 } 734 735 b.lastCycleStart = now 736 b.pacingGain = PacingGain[b.cycleCurrentOffset] 737 } 738 739 func (b *bbrSender) MaybeEnterOrExitProbeRtt(now time.Time, isRoundStart, minRttExpired bool) { 740 if minRttExpired && !b.exitingQuiescence && b.mode != PROBE_RTT { 741 if b.InSlowStart() { 742 b.OnExitStartup(now) 743 } 744 b.mode = PROBE_RTT 745 b.pacingGain = 1.0 746 // Do not decide on the time to exit PROBE_RTT until the |bytes_in_flight| 747 // is at the target small value. 748 b.exitProbeRttAt = time.Time{} 749 } 750 751 if b.mode == PROBE_RTT { 752 b.sampler.OnAppLimited() 753 if b.exitProbeRttAt.IsZero() { 754 // If the window has reached the appropriate size, schedule exiting 755 // PROBE_RTT. The CWND during PROBE_RTT is kMinimumCongestionWindow, but 756 // we allow an extra packet since QUIC checks CWND before sending a 757 // packet. 758 if b.GetBytesInFlight() < b.ProbeRttCongestionWindow()+b.maxDatagramSize { 759 b.exitProbeRttAt = now.Add(ProbeRttTime) 760 b.probeRttRoundPassed = false 761 } 762 } else { 763 if isRoundStart { 764 b.probeRttRoundPassed = true 765 } 766 if !now.Before(b.exitProbeRttAt) && b.probeRttRoundPassed { 767 b.minRttTimestamp = now 768 if !b.isAtFullBandwidth { 769 b.EnterStartupMode(now) 770 } else { 771 b.EnterProbeBandwidthMode(now) 772 } 773 } 774 } 775 } 776 b.exitingQuiescence = false 777 } 778 779 func (b *bbrSender) ProbeRttCongestionWindow() congestion.ByteCount { 780 if b.probeRttBasedOnBdp { 781 return b.GetTargetCongestionWindow(ModerateProbeRttMultiplier) 782 } else { 783 return b.minCongestionWindow() 784 } 785 } 786 787 func (b *bbrSender) EnterStartupMode(now time.Time) { 788 // if b.rttStats != nil { 789 // TODO: slow start. 790 // } 791 b.mode = STARTUP 792 b.pacingGain = b.highGain 793 b.congestionWindowGain = b.highCwndGain 794 } 795 796 func (b *bbrSender) OnExitStartup(now time.Time) { 797 if b.rttStats == nil { 798 return 799 } 800 // TODO: slow start. 801 } 802 803 func (b *bbrSender) CalculatePacingRate() { 804 if b.BandwidthEstimate() == 0 { 805 return 806 } 807 808 targetRate := Bandwidth(b.pacingGain * float64(b.BandwidthEstimate())) 809 if b.isAtFullBandwidth { 810 b.pacingRate = targetRate 811 return 812 } 813 814 // Pace at the rate of initial_window / RTT as soon as RTT measurements are 815 // available. 816 if b.pacingRate == 0 && b.rttStats.MinRTT() > 0 { 817 b.pacingRate = BandwidthFromDelta(b.initialCongestionWindow, b.rttStats.MinRTT()) 818 return 819 } 820 // Slow the pacing rate in STARTUP once loss has ever been detected. 821 hasEverDetectedLoss := b.endRecoveryAt > 0 822 if b.slowerStartup && hasEverDetectedLoss && b.hasNoAppLimitedSample { 823 b.pacingRate = Bandwidth(StartupAfterLossGain * float64(b.BandwidthEstimate())) 824 return 825 } 826 827 // Slow the pacing rate in STARTUP by the bytes_lost / CWND. 828 if b.startupRateReductionMultiplier != 0 && hasEverDetectedLoss && b.hasNoAppLimitedSample { 829 b.pacingRate = Bandwidth((1.0 - (float64(b.startupBytesLost) * float64(b.startupRateReductionMultiplier) / float64(b.congestionWindow))) * float64(targetRate)) 830 // Ensure the pacing rate doesn't drop below the startup growth target times 831 // the bandwidth estimate. 832 b.pacingRate = maxBandwidth(b.pacingRate, Bandwidth(StartupGrowthTarget*float64(b.BandwidthEstimate()))) 833 return 834 } 835 836 // Do not decrease the pacing rate during startup. 837 b.pacingRate = maxBandwidth(b.pacingRate, targetRate) 838 } 839 840 func (b *bbrSender) CalculateCongestionWindow(ackedBytes, excessAcked congestion.ByteCount) { 841 if b.mode == PROBE_RTT { 842 return 843 } 844 845 targetWindow := b.GetTargetCongestionWindow(b.congestionWindowGain) 846 if b.isAtFullBandwidth { 847 // Add the max recently measured ack aggregation to CWND. 848 targetWindow += congestion.ByteCount(b.maxAckHeight.GetBest()) 849 } else if b.enableAckAggregationDuringStartup { 850 // Add the most recent excess acked. Because CWND never decreases in 851 // STARTUP, this will automatically create a very localized max filter. 852 targetWindow += excessAcked 853 } 854 855 // Instead of immediately setting the target CWND as the new one, BBR grows 856 // the CWND towards |target_window| by only increasing it |bytes_acked| at a 857 // time. 858 addBytesAcked := true || !b.InRecovery() 859 if b.isAtFullBandwidth { 860 b.congestionWindow = minByteCount(targetWindow, b.congestionWindow+ackedBytes) 861 } else if addBytesAcked && (b.congestionWindow < targetWindow || b.sampler.totalBytesAcked < b.initialCongestionWindow) { 862 // If the connection is not yet out of startup phase, do not decrease the 863 // window. 864 b.congestionWindow += ackedBytes 865 } 866 867 // Enforce the limits on the congestion window. 868 b.congestionWindow = maxByteCount(b.congestionWindow, b.minCongestionWindow()) 869 b.congestionWindow = minByteCount(b.congestionWindow, b.maxCongestionWindow()) 870 } 871 872 func (b *bbrSender) CalculateRecoveryWindow(ackedBytes, lostBytes congestion.ByteCount) { 873 if b.rateBasedStartup && b.mode == STARTUP { 874 return 875 } 876 877 if b.recoveryState == NOT_IN_RECOVERY { 878 return 879 } 880 881 // Set up the initial recovery window. 882 if b.recoveryWindow == 0 { 883 b.recoveryWindow = maxByteCount(b.GetBytesInFlight()+ackedBytes, b.minCongestionWindow()) 884 return 885 } 886 887 // Remove losses from the recovery window, while accounting for a potential 888 // integer underflow. 889 if b.recoveryWindow >= lostBytes { 890 b.recoveryWindow -= lostBytes 891 } else { 892 b.recoveryWindow = congestion.ByteCount(b.maxDatagramSize) 893 } 894 // In CONSERVATION mode, just subtracting losses is sufficient. In GROWTH, 895 // release additional |bytes_acked| to achieve a slow-start-like behavior. 896 if b.recoveryState == GROWTH { 897 b.recoveryWindow += ackedBytes 898 } 899 // Sanity checks. Ensure that we always allow to send at least an MSS or 900 // |bytes_acked| in response, whichever is larger. 901 b.recoveryWindow = maxByteCount(b.recoveryWindow, b.GetBytesInFlight()+ackedBytes) 902 b.recoveryWindow = maxByteCount(b.recoveryWindow, b.minCongestionWindow()) 903 } 904 905 var _ congestion.CongestionControl = (*bbrSender)(nil) 906 907 func (b *bbrSender) GetMinRtt() time.Duration { 908 if b.minRtt > 0 { 909 return b.minRtt 910 } else { 911 return InitialRtt 912 } 913 } 914 915 func minRtt(a, b time.Duration) time.Duration { 916 if a < b { 917 return a 918 } else { 919 return b 920 } 921 } 922 923 func minBandwidth(a, b Bandwidth) Bandwidth { 924 if a < b { 925 return a 926 } else { 927 return b 928 } 929 } 930 931 func maxBandwidth(a, b Bandwidth) Bandwidth { 932 if a > b { 933 return a 934 } else { 935 return b 936 } 937 } 938 939 func maxByteCount(a, b congestion.ByteCount) congestion.ByteCount { 940 if a > b { 941 return a 942 } else { 943 return b 944 } 945 } 946 947 func minByteCount(a, b congestion.ByteCount) congestion.ByteCount { 948 if a < b { 949 return a 950 } else { 951 return b 952 } 953 } 954 955 var ( 956 InfiniteRTT = time.Duration(math.MaxInt64) 957 )