github.com/metacubex/mihomo@v1.18.5/transport/tuic/congestion_v2/bbr_sender.go (about) 1 package congestion 2 3 // src from https://github.com/google/quiche/blob/e7872fc9e12bb1d46a118949c3d4da36de58aa44/quiche/quic/core/congestion_control/bbr_sender.cc 4 5 import ( 6 "fmt" 7 "net" 8 "time" 9 10 "github.com/metacubex/quic-go/congestion" 11 12 "github.com/zhangyunhao116/fastrand" 13 ) 14 15 // BbrSender implements BBR congestion control algorithm. BBR aims to estimate 16 // the current available Bottleneck Bandwidth and RTT (hence the name), and 17 // regulates the pacing rate and the size of the congestion window based on 18 // those signals. 19 // 20 // BBR relies on pacing in order to function properly. Do not use BBR when 21 // pacing is disabled. 22 // 23 24 const ( 25 minBps = 65536 // 64 kbps 26 27 invalidPacketNumber = -1 28 initialCongestionWindowPackets = 32 29 30 // Constants based on TCP defaults. 31 // The minimum CWND to ensure delayed acks don't reduce bandwidth measurements. 32 // Does not inflate the pacing rate. 33 defaultMinimumCongestionWindow = 4 * congestion.ByteCount(congestion.InitialPacketSizeIPv4) 34 35 // The gain used for the STARTUP, equal to 2/ln(2). 36 defaultHighGain = 2.885 37 // The newly derived gain for STARTUP, equal to 4 * ln(2) 38 derivedHighGain = 2.773 39 // The newly derived CWND gain for STARTUP, 2. 40 derivedHighCWNDGain = 2.0 41 ) 42 43 // The cycle of gains used during the PROBE_BW stage. 44 var pacingGain = [...]float64{1.25, 0.75, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0} 45 46 const ( 47 // The length of the gain cycle. 48 gainCycleLength = len(pacingGain) 49 // The size of the bandwidth filter window, in round-trips. 50 bandwidthWindowSize = gainCycleLength + 2 51 52 // The time after which the current min_rtt value expires. 53 minRttExpiry = 10 * time.Second 54 // The minimum time the connection can spend in PROBE_RTT mode. 55 probeRttTime = 200 * time.Millisecond 56 // If the bandwidth does not increase by the factor of |kStartupGrowthTarget| 57 // within |kRoundTripsWithoutGrowthBeforeExitingStartup| rounds, the connection 58 // will exit the STARTUP mode. 59 startupGrowthTarget = 1.25 60 roundTripsWithoutGrowthBeforeExitingStartup = int64(3) 61 62 // Flag. 63 defaultStartupFullLossCount = 8 64 quicBbr2DefaultLossThreshold = 0.02 65 maxBbrBurstPackets = 3 66 ) 67 68 type bbrMode int 69 70 const ( 71 // Startup phase of the connection. 72 bbrModeStartup = iota 73 // After achieving the highest possible bandwidth during the startup, lower 74 // the pacing rate in order to drain the queue. 75 bbrModeDrain 76 // Cruising mode. 77 bbrModeProbeBw 78 // Temporarily slow down sending in order to empty the buffer and measure 79 // the real minimum RTT. 80 bbrModeProbeRtt 81 ) 82 83 // Indicates how the congestion control limits the amount of bytes in flight. 84 type bbrRecoveryState int 85 86 const ( 87 // Do not limit. 88 bbrRecoveryStateNotInRecovery = iota 89 // Allow an extra outstanding byte for each byte acknowledged. 90 bbrRecoveryStateConservation 91 // Allow two extra outstanding bytes for each byte acknowledged (slow 92 // start). 93 bbrRecoveryStateGrowth 94 ) 95 96 type bbrSender struct { 97 rttStats congestion.RTTStatsProvider 98 clock Clock 99 pacer *Pacer 100 101 mode bbrMode 102 103 // Bandwidth sampler provides BBR with the bandwidth measurements at 104 // individual points. 105 sampler *bandwidthSampler 106 107 // The number of the round trips that have occurred during the connection. 108 roundTripCount roundTripCount 109 110 // The packet number of the most recently sent packet. 111 lastSentPacket congestion.PacketNumber 112 // Acknowledgement of any packet after |current_round_trip_end_| will cause 113 // the round trip counter to advance. 114 currentRoundTripEnd congestion.PacketNumber 115 116 // Number of congestion events with some losses, in the current round. 117 numLossEventsInRound uint64 118 119 // Number of total bytes lost in the current round. 120 bytesLostInRound congestion.ByteCount 121 122 // The filter that tracks the maximum bandwidth over the multiple recent 123 // round-trips. 124 maxBandwidth *WindowedFilter[Bandwidth, roundTripCount] 125 126 // Minimum RTT estimate. Automatically expires within 10 seconds (and 127 // triggers PROBE_RTT mode) if no new value is sampled during that period. 128 minRtt time.Duration 129 // The time at which the current value of |min_rtt_| was assigned. 130 minRttTimestamp time.Time 131 132 // The maximum allowed number of bytes in flight. 133 congestionWindow congestion.ByteCount 134 135 // The initial value of the |congestion_window_|. 136 initialCongestionWindow congestion.ByteCount 137 138 // The largest value the |congestion_window_| can achieve. 139 maxCongestionWindow congestion.ByteCount 140 141 // The smallest value the |congestion_window_| can achieve. 142 minCongestionWindow congestion.ByteCount 143 144 // The pacing gain applied during the STARTUP phase. 145 highGain float64 146 147 // The CWND gain applied during the STARTUP phase. 148 highCwndGain float64 149 150 // The pacing gain applied during the DRAIN phase. 151 drainGain float64 152 153 // The current pacing rate of the connection. 154 pacingRate Bandwidth 155 156 // The gain currently applied to the pacing rate. 157 pacingGain float64 158 // The gain currently applied to the congestion window. 159 congestionWindowGain float64 160 161 // The gain used for the congestion window during PROBE_BW. Latched from 162 // quic_bbr_cwnd_gain flag. 163 congestionWindowGainConstant float64 164 // The number of RTTs to stay in STARTUP mode. Defaults to 3. 165 numStartupRtts int64 166 167 // Number of round-trips in PROBE_BW mode, used for determining the current 168 // pacing gain cycle. 169 cycleCurrentOffset int 170 // The time at which the last pacing gain cycle was started. 171 lastCycleStart time.Time 172 173 // Indicates whether the connection has reached the full bandwidth mode. 174 isAtFullBandwidth bool 175 // Number of rounds during which there was no significant bandwidth increase. 176 roundsWithoutBandwidthGain int64 177 // The bandwidth compared to which the increase is measured. 178 bandwidthAtLastRound Bandwidth 179 180 // Set to true upon exiting quiescence. 181 exitingQuiescence bool 182 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 190 // Indicates whether the most recent bandwidth sample was marked as 191 // app-limited. 192 lastSampleIsAppLimited bool 193 // Indicates whether any non app-limited samples have been recorded. 194 hasNoAppLimitedSample bool 195 196 // Current state of recovery. 197 recoveryState bbrRecoveryState 198 // Receiving acknowledgement of a packet after |end_recovery_at_| will cause 199 // BBR to exit the recovery mode. A value above zero indicates at least one 200 // loss has been detected, so it must not be set back to zero. 201 endRecoveryAt congestion.PacketNumber 202 // A window used to limit the number of bytes in flight during loss recovery. 203 recoveryWindow congestion.ByteCount 204 // If true, consider all samples in recovery app-limited. 205 isAppLimitedRecovery bool // not used 206 207 // When true, pace at 1.5x and disable packet conservation in STARTUP. 208 slowerStartup bool // not used 209 // When true, disables packet conservation in STARTUP. 210 rateBasedStartup bool // not used 211 212 // When true, add the most recent ack aggregation measurement during STARTUP. 213 enableAckAggregationDuringStartup bool 214 // When true, expire the windowed ack aggregation values in STARTUP when 215 // bandwidth increases more than 25%. 216 expireAckAggregationInStartup bool 217 218 // If true, will not exit low gain mode until bytes_in_flight drops below BDP 219 // or it's time for high gain mode. 220 drainToTarget bool 221 222 // If true, slow down pacing rate in STARTUP when overshooting is detected. 223 detectOvershooting bool 224 // Bytes lost while detect_overshooting_ is true. 225 bytesLostWhileDetectingOvershooting congestion.ByteCount 226 // Slow down pacing rate if 227 // bytes_lost_while_detecting_overshooting_ * 228 // bytes_lost_multiplier_while_detecting_overshooting_ > IW. 229 bytesLostMultiplierWhileDetectingOvershooting uint8 230 // When overshooting is detected, do not drop pacing_rate_ below this value / 231 // min_rtt. 232 cwndToCalculateMinPacingRate congestion.ByteCount 233 234 // Max congestion window when adjusting network parameters. 235 maxCongestionWindowWithNetworkParametersAdjusted congestion.ByteCount // not used 236 237 // Params. 238 maxDatagramSize congestion.ByteCount 239 // Recorded on packet sent. equivalent |unacked_packets_->bytes_in_flight()| 240 bytesInFlight congestion.ByteCount 241 } 242 243 var _ congestion.CongestionControl = &bbrSender{} 244 245 func NewBbrSender( 246 clock Clock, 247 initialMaxDatagramSize congestion.ByteCount, 248 initialCongestionWindowPackets congestion.ByteCount, 249 ) *bbrSender { 250 return newBbrSender( 251 clock, 252 initialMaxDatagramSize, 253 initialCongestionWindowPackets*initialMaxDatagramSize, 254 congestion.MaxCongestionWindowPackets*initialMaxDatagramSize, 255 ) 256 } 257 258 func newBbrSender( 259 clock Clock, 260 initialMaxDatagramSize, 261 initialCongestionWindow, 262 initialMaxCongestionWindow congestion.ByteCount, 263 ) *bbrSender { 264 b := &bbrSender{ 265 clock: clock, 266 mode: bbrModeStartup, 267 sampler: newBandwidthSampler(roundTripCount(bandwidthWindowSize)), 268 lastSentPacket: invalidPacketNumber, 269 currentRoundTripEnd: invalidPacketNumber, 270 maxBandwidth: NewWindowedFilter(roundTripCount(bandwidthWindowSize), MaxFilter[Bandwidth]), 271 congestionWindow: initialCongestionWindow, 272 initialCongestionWindow: initialCongestionWindow, 273 maxCongestionWindow: initialMaxCongestionWindow, 274 minCongestionWindow: defaultMinimumCongestionWindow, 275 highGain: defaultHighGain, 276 highCwndGain: defaultHighGain, 277 drainGain: 1.0 / defaultHighGain, 278 pacingGain: 1.0, 279 congestionWindowGain: 1.0, 280 congestionWindowGainConstant: 2.0, 281 numStartupRtts: roundTripsWithoutGrowthBeforeExitingStartup, 282 recoveryState: bbrRecoveryStateNotInRecovery, 283 endRecoveryAt: invalidPacketNumber, 284 recoveryWindow: initialMaxCongestionWindow, 285 bytesLostMultiplierWhileDetectingOvershooting: 2, 286 cwndToCalculateMinPacingRate: initialCongestionWindow, 287 maxCongestionWindowWithNetworkParametersAdjusted: initialMaxCongestionWindow, 288 maxDatagramSize: initialMaxDatagramSize, 289 } 290 b.pacer = NewPacer(b.bandwidthForPacer) 291 292 /* 293 if b.tracer != nil { 294 b.lastState = logging.CongestionStateStartup 295 b.tracer.UpdatedCongestionState(logging.CongestionStateStartup) 296 } 297 */ 298 299 b.enterStartupMode(b.clock.Now()) 300 b.setHighCwndGain(derivedHighCWNDGain) 301 302 return b 303 } 304 305 func (b *bbrSender) SetRTTStatsProvider(provider congestion.RTTStatsProvider) { 306 b.rttStats = provider 307 } 308 309 // TimeUntilSend implements the SendAlgorithm interface. 310 func (b *bbrSender) TimeUntilSend(bytesInFlight congestion.ByteCount) time.Time { 311 return b.pacer.TimeUntilSend() 312 } 313 314 // HasPacingBudget implements the SendAlgorithm interface. 315 func (b *bbrSender) HasPacingBudget(now time.Time) bool { 316 return b.pacer.Budget(now) >= b.maxDatagramSize 317 } 318 319 // OnPacketSent implements the SendAlgorithm interface. 320 func (b *bbrSender) OnPacketSent( 321 sentTime time.Time, 322 bytesInFlight congestion.ByteCount, 323 packetNumber congestion.PacketNumber, 324 bytes congestion.ByteCount, 325 isRetransmittable bool, 326 ) { 327 b.pacer.SentPacket(sentTime, bytes) 328 329 b.lastSentPacket = packetNumber 330 b.bytesInFlight = bytesInFlight 331 332 if bytesInFlight == 0 { 333 b.exitingQuiescence = true 334 } 335 336 b.sampler.OnPacketSent(sentTime, packetNumber, bytes, bytesInFlight, isRetransmittable) 337 } 338 339 // CanSend implements the SendAlgorithm interface. 340 func (b *bbrSender) CanSend(bytesInFlight congestion.ByteCount) bool { 341 return bytesInFlight < b.GetCongestionWindow() 342 } 343 344 // MaybeExitSlowStart implements the SendAlgorithm interface. 345 func (b *bbrSender) MaybeExitSlowStart() { 346 // Do nothing 347 } 348 349 // OnPacketAcked implements the SendAlgorithm interface. 350 func (b *bbrSender) OnPacketAcked(number congestion.PacketNumber, ackedBytes, priorInFlight congestion.ByteCount, eventTime time.Time) { 351 // Do nothing. 352 } 353 354 // OnPacketLost implements the SendAlgorithm interface. 355 func (b *bbrSender) OnPacketLost(number congestion.PacketNumber, lostBytes, priorInFlight congestion.ByteCount) { 356 // Do nothing. 357 } 358 359 // OnRetransmissionTimeout implements the SendAlgorithm interface. 360 func (b *bbrSender) OnRetransmissionTimeout(packetsRetransmitted bool) { 361 // Do nothing. 362 } 363 364 // SetMaxDatagramSize implements the SendAlgorithm interface. 365 func (b *bbrSender) SetMaxDatagramSize(s congestion.ByteCount) { 366 if s < b.maxDatagramSize { 367 panic(fmt.Sprintf("congestion BUG: decreased max datagram size from %d to %d", b.maxDatagramSize, s)) 368 } 369 cwndIsMinCwnd := b.congestionWindow == b.minCongestionWindow 370 b.maxDatagramSize = s 371 if cwndIsMinCwnd { 372 b.congestionWindow = b.minCongestionWindow 373 } 374 b.pacer.SetMaxDatagramSize(s) 375 } 376 377 // InSlowStart implements the SendAlgorithmWithDebugInfos interface. 378 func (b *bbrSender) InSlowStart() bool { 379 return b.mode == bbrModeStartup 380 } 381 382 // InRecovery implements the SendAlgorithmWithDebugInfos interface. 383 func (b *bbrSender) InRecovery() bool { 384 return b.recoveryState != bbrRecoveryStateNotInRecovery 385 } 386 387 // GetCongestionWindow implements the SendAlgorithmWithDebugInfos interface. 388 func (b *bbrSender) GetCongestionWindow() congestion.ByteCount { 389 if b.mode == bbrModeProbeRtt { 390 return b.probeRttCongestionWindow() 391 } 392 393 if b.InRecovery() { 394 return Min(b.congestionWindow, b.recoveryWindow) 395 } 396 397 return b.congestionWindow 398 } 399 400 func (b *bbrSender) OnCongestionEvent(number congestion.PacketNumber, lostBytes, priorInFlight congestion.ByteCount) { 401 // Do nothing. 402 } 403 404 func (b *bbrSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime time.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) { 405 totalBytesAckedBefore := b.sampler.TotalBytesAcked() 406 totalBytesLostBefore := b.sampler.TotalBytesLost() 407 408 var isRoundStart, minRttExpired bool 409 var excessAcked, bytesLost congestion.ByteCount 410 411 // The send state of the largest packet in acked_packets, unless it is 412 // empty. If acked_packets is empty, it's the send state of the largest 413 // packet in lost_packets. 414 var lastPacketSendState sendTimeState 415 416 b.maybeApplimited(priorInFlight) 417 418 // Update bytesInFlight 419 b.bytesInFlight = priorInFlight 420 for _, p := range ackedPackets { 421 b.bytesInFlight -= p.BytesAcked 422 } 423 for _, p := range lostPackets { 424 b.bytesInFlight -= p.BytesLost 425 } 426 427 if len(ackedPackets) != 0 { 428 lastAckedPacket := ackedPackets[len(ackedPackets)-1].PacketNumber 429 isRoundStart = b.updateRoundTripCounter(lastAckedPacket) 430 b.updateRecoveryState(lastAckedPacket, len(lostPackets) != 0, isRoundStart) 431 } 432 433 sample := b.sampler.OnCongestionEvent(eventTime, 434 ackedPackets, lostPackets, b.maxBandwidth.GetBest(), infBandwidth, b.roundTripCount) 435 if sample.lastPacketSendState.isValid { 436 b.lastSampleIsAppLimited = sample.lastPacketSendState.isAppLimited 437 b.hasNoAppLimitedSample = b.hasNoAppLimitedSample || !b.lastSampleIsAppLimited 438 } 439 // Avoid updating |max_bandwidth_| if a) this is a loss-only event, or b) all 440 // packets in |acked_packets| did not generate valid samples. (e.g. ack of 441 // ack-only packets). In both cases, sampler_.total_bytes_acked() will not 442 // change. 443 if totalBytesAckedBefore != b.sampler.TotalBytesAcked() { 444 if !sample.sampleIsAppLimited || sample.sampleMaxBandwidth > b.maxBandwidth.GetBest() { 445 b.maxBandwidth.Update(sample.sampleMaxBandwidth, b.roundTripCount) 446 } 447 } 448 449 if sample.sampleRtt != infRTT { 450 minRttExpired = b.maybeUpdateMinRtt(eventTime, sample.sampleRtt) 451 } 452 bytesLost = b.sampler.TotalBytesLost() - totalBytesLostBefore 453 454 excessAcked = sample.extraAcked 455 lastPacketSendState = sample.lastPacketSendState 456 457 if len(lostPackets) != 0 { 458 b.numLossEventsInRound++ 459 b.bytesLostInRound += bytesLost 460 } 461 462 // Handle logic specific to PROBE_BW mode. 463 if b.mode == bbrModeProbeBw { 464 b.updateGainCyclePhase(eventTime, priorInFlight, len(lostPackets) != 0) 465 } 466 467 // Handle logic specific to STARTUP and DRAIN modes. 468 if isRoundStart && !b.isAtFullBandwidth { 469 b.checkIfFullBandwidthReached(&lastPacketSendState) 470 } 471 472 b.maybeExitStartupOrDrain(eventTime) 473 474 // Handle logic specific to PROBE_RTT. 475 b.maybeEnterOrExitProbeRtt(eventTime, isRoundStart, minRttExpired) 476 477 // Calculate number of packets acked and lost. 478 bytesAcked := b.sampler.TotalBytesAcked() - totalBytesAckedBefore 479 480 // After the model is updated, recalculate the pacing rate and congestion 481 // window. 482 b.calculatePacingRate(bytesLost) 483 b.calculateCongestionWindow(bytesAcked, excessAcked) 484 b.calculateRecoveryWindow(bytesAcked, bytesLost) 485 486 // Cleanup internal state. 487 // This is where we clean up obsolete (acked or lost) packets from the bandwidth sampler. 488 // The "least unacked" should actually be FirstOutstanding, but since we are not passing 489 // that through OnCongestionEventEx, we will only do an estimate using acked/lost packets 490 // for now. Because of fast retransmission, they should differ by no more than 2 packets. 491 // (this is controlled by packetThreshold in quic-go's sentPacketHandler) 492 var leastUnacked congestion.PacketNumber 493 if len(ackedPackets) != 0 { 494 leastUnacked = ackedPackets[len(ackedPackets)-1].PacketNumber - 2 495 } else { 496 leastUnacked = lostPackets[len(lostPackets)-1].PacketNumber + 1 497 } 498 b.sampler.RemoveObsoletePackets(leastUnacked) 499 500 if isRoundStart { 501 b.numLossEventsInRound = 0 502 b.bytesLostInRound = 0 503 } 504 } 505 506 func (b *bbrSender) PacingRate() Bandwidth { 507 if b.pacingRate == 0 { 508 return Bandwidth(b.highGain * float64( 509 BandwidthFromDelta(b.initialCongestionWindow, b.getMinRtt()))) 510 } 511 512 return b.pacingRate 513 } 514 515 func (b *bbrSender) hasGoodBandwidthEstimateForResumption() bool { 516 return b.hasNonAppLimitedSample() 517 } 518 519 func (b *bbrSender) hasNonAppLimitedSample() bool { 520 return b.hasNoAppLimitedSample 521 } 522 523 // Sets the pacing gain used in STARTUP. Must be greater than 1. 524 func (b *bbrSender) setHighGain(highGain float64) { 525 b.highGain = highGain 526 if b.mode == bbrModeStartup { 527 b.pacingGain = highGain 528 } 529 } 530 531 // Sets the CWND gain used in STARTUP. Must be greater than 1. 532 func (b *bbrSender) setHighCwndGain(highCwndGain float64) { 533 b.highCwndGain = highCwndGain 534 if b.mode == bbrModeStartup { 535 b.congestionWindowGain = highCwndGain 536 } 537 } 538 539 // Sets the gain used in DRAIN. Must be less than 1. 540 func (b *bbrSender) setDrainGain(drainGain float64) { 541 b.drainGain = drainGain 542 } 543 544 // What's the current estimated bandwidth in bytes per second. 545 func (b *bbrSender) bandwidthEstimate() Bandwidth { 546 return b.maxBandwidth.GetBest() 547 } 548 549 func (b *bbrSender) bandwidthForPacer() congestion.ByteCount { 550 bps := congestion.ByteCount(float64(b.bandwidthEstimate()) * b.congestionWindowGain / float64(BytesPerSecond)) 551 if bps < minBps { 552 // We need to make sure that the bandwidth value for pacer is never zero, 553 // otherwise it will go into an edge case where HasPacingBudget = false 554 // but TimeUntilSend is before, causing the quic-go send loop to go crazy and get stuck. 555 return minBps 556 } 557 return bps 558 } 559 560 // Returns the current estimate of the RTT of the connection. Outside of the 561 // edge cases, this is minimum RTT. 562 func (b *bbrSender) getMinRtt() time.Duration { 563 if b.minRtt != 0 { 564 return b.minRtt 565 } 566 // min_rtt could be available if the handshake packet gets neutered then 567 // gets acknowledged. This could only happen for QUIC crypto where we do not 568 // drop keys. 569 minRtt := b.rttStats.MinRTT() 570 if minRtt == 0 { 571 return 100 * time.Millisecond 572 } else { 573 return minRtt 574 } 575 } 576 577 // Computes the target congestion window using the specified gain. 578 func (b *bbrSender) getTargetCongestionWindow(gain float64) congestion.ByteCount { 579 bdp := bdpFromRttAndBandwidth(b.getMinRtt(), b.bandwidthEstimate()) 580 congestionWindow := congestion.ByteCount(gain * float64(bdp)) 581 582 // BDP estimate will be zero if no bandwidth samples are available yet. 583 if congestionWindow == 0 { 584 congestionWindow = congestion.ByteCount(gain * float64(b.initialCongestionWindow)) 585 } 586 587 return Max(congestionWindow, b.minCongestionWindow) 588 } 589 590 // The target congestion window during PROBE_RTT. 591 func (b *bbrSender) probeRttCongestionWindow() congestion.ByteCount { 592 return b.minCongestionWindow 593 } 594 595 func (b *bbrSender) maybeUpdateMinRtt(now time.Time, sampleMinRtt time.Duration) bool { 596 // Do not expire min_rtt if none was ever available. 597 minRttExpired := b.minRtt != 0 && now.After(b.minRttTimestamp.Add(minRttExpiry)) 598 if minRttExpired || sampleMinRtt < b.minRtt || b.minRtt == 0 { 599 b.minRtt = sampleMinRtt 600 b.minRttTimestamp = now 601 } 602 603 return minRttExpired 604 } 605 606 // Enters the STARTUP mode. 607 func (b *bbrSender) enterStartupMode(now time.Time) { 608 b.mode = bbrModeStartup 609 // b.maybeTraceStateChange(logging.CongestionStateStartup) 610 b.pacingGain = b.highGain 611 b.congestionWindowGain = b.highCwndGain 612 } 613 614 // Enters the PROBE_BW mode. 615 func (b *bbrSender) enterProbeBandwidthMode(now time.Time) { 616 b.mode = bbrModeProbeBw 617 // b.maybeTraceStateChange(logging.CongestionStateProbeBw) 618 b.congestionWindowGain = b.congestionWindowGainConstant 619 620 // Pick a random offset for the gain cycle out of {0, 2..7} range. 1 is 621 // excluded because in that case increased gain and decreased gain would not 622 // follow each other. 623 b.cycleCurrentOffset = int(fastrand.Int31n(congestion.PacketsPerConnectionID)) % (gainCycleLength - 1) 624 if b.cycleCurrentOffset >= 1 { 625 b.cycleCurrentOffset += 1 626 } 627 628 b.lastCycleStart = now 629 b.pacingGain = pacingGain[b.cycleCurrentOffset] 630 } 631 632 // Updates the round-trip counter if a round-trip has passed. Returns true if 633 // the counter has been advanced. 634 func (b *bbrSender) updateRoundTripCounter(lastAckedPacket congestion.PacketNumber) bool { 635 if b.currentRoundTripEnd == invalidPacketNumber || lastAckedPacket > b.currentRoundTripEnd { 636 b.roundTripCount++ 637 b.currentRoundTripEnd = b.lastSentPacket 638 return true 639 } 640 return false 641 } 642 643 // Updates the current gain used in PROBE_BW mode. 644 func (b *bbrSender) updateGainCyclePhase(now time.Time, priorInFlight congestion.ByteCount, hasLosses bool) { 645 // In most cases, the cycle is advanced after an RTT passes. 646 shouldAdvanceGainCycling := now.After(b.lastCycleStart.Add(b.getMinRtt())) 647 // If the pacing gain is above 1.0, the connection is trying to probe the 648 // bandwidth by increasing the number of bytes in flight to at least 649 // pacing_gain * BDP. Make sure that it actually reaches the target, as long 650 // as there are no losses suggesting that the buffers are not able to hold 651 // that much. 652 if b.pacingGain > 1.0 && !hasLosses && priorInFlight < b.getTargetCongestionWindow(b.pacingGain) { 653 shouldAdvanceGainCycling = false 654 } 655 656 // If pacing gain is below 1.0, the connection is trying to drain the extra 657 // queue which could have been incurred by probing prior to it. If the number 658 // of bytes in flight falls down to the estimated BDP value earlier, conclude 659 // that the queue has been successfully drained and exit this cycle early. 660 if b.pacingGain < 1.0 && b.bytesInFlight <= b.getTargetCongestionWindow(1) { 661 shouldAdvanceGainCycling = true 662 } 663 664 if shouldAdvanceGainCycling { 665 b.cycleCurrentOffset = (b.cycleCurrentOffset + 1) % gainCycleLength 666 b.lastCycleStart = now 667 // Stay in low gain mode until the target BDP is hit. 668 // Low gain mode will be exited immediately when the target BDP is achieved. 669 if b.drainToTarget && b.pacingGain < 1 && 670 pacingGain[b.cycleCurrentOffset] == 1 && 671 b.bytesInFlight > b.getTargetCongestionWindow(1) { 672 return 673 } 674 b.pacingGain = pacingGain[b.cycleCurrentOffset] 675 } 676 } 677 678 // Tracks for how many round-trips the bandwidth has not increased 679 // significantly. 680 func (b *bbrSender) checkIfFullBandwidthReached(lastPacketSendState *sendTimeState) { 681 if b.lastSampleIsAppLimited { 682 return 683 } 684 685 target := Bandwidth(float64(b.bandwidthAtLastRound) * startupGrowthTarget) 686 if b.bandwidthEstimate() >= target { 687 b.bandwidthAtLastRound = b.bandwidthEstimate() 688 b.roundsWithoutBandwidthGain = 0 689 if b.expireAckAggregationInStartup { 690 // Expire old excess delivery measurements now that bandwidth increased. 691 b.sampler.ResetMaxAckHeightTracker(0, b.roundTripCount) 692 } 693 return 694 } 695 696 b.roundsWithoutBandwidthGain++ 697 if b.roundsWithoutBandwidthGain >= b.numStartupRtts || 698 b.shouldExitStartupDueToLoss(lastPacketSendState) { 699 b.isAtFullBandwidth = true 700 } 701 } 702 703 func (b *bbrSender) maybeApplimited(bytesInFlight congestion.ByteCount) { 704 congestionWindow := b.GetCongestionWindow() 705 if bytesInFlight >= congestionWindow { 706 return 707 } 708 availableBytes := congestionWindow - bytesInFlight 709 drainLimited := b.mode == bbrModeDrain && bytesInFlight > congestionWindow/2 710 if !drainLimited || availableBytes > maxBbrBurstPackets*b.maxDatagramSize { 711 b.sampler.OnAppLimited() 712 } 713 } 714 715 // Transitions from STARTUP to DRAIN and from DRAIN to PROBE_BW if 716 // appropriate. 717 func (b *bbrSender) maybeExitStartupOrDrain(now time.Time) { 718 if b.mode == bbrModeStartup && b.isAtFullBandwidth { 719 b.mode = bbrModeDrain 720 // b.maybeTraceStateChange(logging.CongestionStateDrain) 721 b.pacingGain = b.drainGain 722 b.congestionWindowGain = b.highCwndGain 723 } 724 if b.mode == bbrModeDrain && b.bytesInFlight <= b.getTargetCongestionWindow(1) { 725 b.enterProbeBandwidthMode(now) 726 } 727 } 728 729 // Decides whether to enter or exit PROBE_RTT. 730 func (b *bbrSender) maybeEnterOrExitProbeRtt(now time.Time, isRoundStart, minRttExpired bool) { 731 if minRttExpired && !b.exitingQuiescence && b.mode != bbrModeProbeRtt { 732 b.mode = bbrModeProbeRtt 733 // b.maybeTraceStateChange(logging.CongestionStateProbRtt) 734 b.pacingGain = 1.0 735 // Do not decide on the time to exit PROBE_RTT until the |bytes_in_flight| 736 // is at the target small value. 737 b.exitProbeRttAt = time.Time{} 738 } 739 740 if b.mode == bbrModeProbeRtt { 741 b.sampler.OnAppLimited() 742 // b.maybeTraceStateChange(logging.CongestionStateApplicationLimited) 743 744 if b.exitProbeRttAt.IsZero() { 745 // If the window has reached the appropriate size, schedule exiting 746 // PROBE_RTT. The CWND during PROBE_RTT is kMinimumCongestionWindow, but 747 // we allow an extra packet since QUIC checks CWND before sending a 748 // packet. 749 if b.bytesInFlight < b.probeRttCongestionWindow()+congestion.MaxPacketBufferSize { 750 b.exitProbeRttAt = now.Add(probeRttTime) 751 b.probeRttRoundPassed = false 752 } 753 } else { 754 if isRoundStart { 755 b.probeRttRoundPassed = true 756 } 757 if now.Sub(b.exitProbeRttAt) >= 0 && b.probeRttRoundPassed { 758 b.minRttTimestamp = now 759 if !b.isAtFullBandwidth { 760 b.enterStartupMode(now) 761 } else { 762 b.enterProbeBandwidthMode(now) 763 } 764 } 765 } 766 } 767 768 b.exitingQuiescence = false 769 } 770 771 // Determines whether BBR needs to enter, exit or advance state of the 772 // recovery. 773 func (b *bbrSender) updateRecoveryState(lastAckedPacket congestion.PacketNumber, hasLosses, isRoundStart bool) { 774 // Disable recovery in startup, if loss-based exit is enabled. 775 if !b.isAtFullBandwidth { 776 return 777 } 778 779 // Exit recovery when there are no losses for a round. 780 if hasLosses { 781 b.endRecoveryAt = b.lastSentPacket 782 } 783 784 switch b.recoveryState { 785 case bbrRecoveryStateNotInRecovery: 786 if hasLosses { 787 b.recoveryState = bbrRecoveryStateConservation 788 // This will cause the |recovery_window_| to be set to the correct 789 // value in CalculateRecoveryWindow(). 790 b.recoveryWindow = 0 791 // Since the conservation phase is meant to be lasting for a whole 792 // round, extend the current round as if it were started right now. 793 b.currentRoundTripEnd = b.lastSentPacket 794 } 795 case bbrRecoveryStateConservation: 796 if isRoundStart { 797 b.recoveryState = bbrRecoveryStateGrowth 798 } 799 fallthrough 800 case bbrRecoveryStateGrowth: 801 // Exit recovery if appropriate. 802 if !hasLosses && lastAckedPacket > b.endRecoveryAt { 803 b.recoveryState = bbrRecoveryStateNotInRecovery 804 } 805 } 806 } 807 808 // Determines the appropriate pacing rate for the connection. 809 func (b *bbrSender) calculatePacingRate(bytesLost congestion.ByteCount) { 810 if b.bandwidthEstimate() == 0 { 811 return 812 } 813 814 targetRate := Bandwidth(b.pacingGain * float64(b.bandwidthEstimate())) 815 if b.isAtFullBandwidth { 816 b.pacingRate = targetRate 817 return 818 } 819 820 // Pace at the rate of initial_window / RTT as soon as RTT measurements are 821 // available. 822 if b.pacingRate == 0 && b.rttStats.MinRTT() != 0 { 823 b.pacingRate = BandwidthFromDelta(b.initialCongestionWindow, b.rttStats.MinRTT()) 824 return 825 } 826 827 if b.detectOvershooting { 828 b.bytesLostWhileDetectingOvershooting += bytesLost 829 // Check for overshooting with network parameters adjusted when pacing rate 830 // > target_rate and loss has been detected. 831 if b.pacingRate > targetRate && b.bytesLostWhileDetectingOvershooting > 0 { 832 if b.hasNoAppLimitedSample || 833 b.bytesLostWhileDetectingOvershooting*congestion.ByteCount(b.bytesLostMultiplierWhileDetectingOvershooting) > b.initialCongestionWindow { 834 // We are fairly sure overshoot happens if 1) there is at least one 835 // non app-limited bw sample or 2) half of IW gets lost. Slow pacing 836 // rate. 837 b.pacingRate = Max(targetRate, BandwidthFromDelta(b.cwndToCalculateMinPacingRate, b.rttStats.MinRTT())) 838 b.bytesLostWhileDetectingOvershooting = 0 839 b.detectOvershooting = false 840 } 841 } 842 } 843 844 // Do not decrease the pacing rate during startup. 845 b.pacingRate = Max(b.pacingRate, targetRate) 846 } 847 848 // Determines the appropriate congestion window for the connection. 849 func (b *bbrSender) calculateCongestionWindow(bytesAcked, excessAcked congestion.ByteCount) { 850 if b.mode == bbrModeProbeRtt { 851 return 852 } 853 854 targetWindow := b.getTargetCongestionWindow(b.congestionWindowGain) 855 if b.isAtFullBandwidth { 856 // Add the max recently measured ack aggregation to CWND. 857 targetWindow += b.sampler.MaxAckHeight() 858 } else if b.enableAckAggregationDuringStartup { 859 // Add the most recent excess acked. Because CWND never decreases in 860 // STARTUP, this will automatically create a very localized max filter. 861 targetWindow += excessAcked 862 } 863 864 // Instead of immediately setting the target CWND as the new one, BBR grows 865 // the CWND towards |target_window| by only increasing it |bytes_acked| at a 866 // time. 867 if b.isAtFullBandwidth { 868 b.congestionWindow = Min(targetWindow, b.congestionWindow+bytesAcked) 869 } else if b.congestionWindow < targetWindow || 870 b.sampler.TotalBytesAcked() < b.initialCongestionWindow { 871 // If the connection is not yet out of startup phase, do not decrease the 872 // window. 873 b.congestionWindow += bytesAcked 874 } 875 876 // Enforce the limits on the congestion window. 877 b.congestionWindow = Max(b.congestionWindow, b.minCongestionWindow) 878 b.congestionWindow = Min(b.congestionWindow, b.maxCongestionWindow) 879 } 880 881 // Determines the appropriate window that constrains the in-flight during recovery. 882 func (b *bbrSender) calculateRecoveryWindow(bytesAcked, bytesLost congestion.ByteCount) { 883 if b.recoveryState == bbrRecoveryStateNotInRecovery { 884 return 885 } 886 887 // Set up the initial recovery window. 888 if b.recoveryWindow == 0 { 889 b.recoveryWindow = b.bytesInFlight + bytesAcked 890 b.recoveryWindow = Max(b.minCongestionWindow, b.recoveryWindow) 891 return 892 } 893 894 // Remove losses from the recovery window, while accounting for a potential 895 // integer underflow. 896 if b.recoveryWindow >= bytesLost { 897 b.recoveryWindow = b.recoveryWindow - bytesLost 898 } else { 899 b.recoveryWindow = b.maxDatagramSize 900 } 901 902 // In CONSERVATION mode, just subtracting losses is sufficient. In GROWTH, 903 // release additional |bytes_acked| to achieve a slow-start-like behavior. 904 if b.recoveryState == bbrRecoveryStateGrowth { 905 b.recoveryWindow += bytesAcked 906 } 907 908 // Always allow sending at least |bytes_acked| in response. 909 b.recoveryWindow = Max(b.recoveryWindow, b.bytesInFlight+bytesAcked) 910 b.recoveryWindow = Max(b.minCongestionWindow, b.recoveryWindow) 911 } 912 913 // Return whether we should exit STARTUP due to excessive loss. 914 func (b *bbrSender) shouldExitStartupDueToLoss(lastPacketSendState *sendTimeState) bool { 915 if b.numLossEventsInRound < defaultStartupFullLossCount || !lastPacketSendState.isValid { 916 return false 917 } 918 919 inflightAtSend := lastPacketSendState.bytesInFlight 920 921 if inflightAtSend > 0 && b.bytesLostInRound > 0 { 922 if b.bytesLostInRound > congestion.ByteCount(float64(inflightAtSend)*quicBbr2DefaultLossThreshold) { 923 return true 924 } 925 return false 926 } 927 return false 928 } 929 930 func bdpFromRttAndBandwidth(rtt time.Duration, bandwidth Bandwidth) congestion.ByteCount { 931 return congestion.ByteCount(rtt) * congestion.ByteCount(bandwidth) / congestion.ByteCount(BytesPerSecond) / congestion.ByteCount(time.Second) 932 } 933 934 func GetInitialPacketSize(addr net.Addr) congestion.ByteCount { 935 // If this is not a UDP address, we don't know anything about the MTU. 936 // Use the minimum size of an Initial packet as the max packet size. 937 if udpAddr, ok := addr.(*net.UDPAddr); ok { 938 if udpAddr.IP.To4() != nil { 939 return congestion.InitialPacketSizeIPv4 940 } else { 941 return congestion.InitialPacketSizeIPv6 942 } 943 } else { 944 return congestion.MinInitialPacketSize 945 } 946 }