blob: 7abba24aeec61720a3deb2ad398ae9daba5ccb9a [file] [log] [blame]
Bence Békybac04052022-04-07 15:44:29 -04001// Copyright 2016 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "quiche/quic/core/congestion_control/bbr_sender.h"
6
7#include <algorithm>
8#include <sstream>
9#include <string>
10
11#include "absl/base/attributes.h"
12#include "quiche/quic/core/congestion_control/rtt_stats.h"
13#include "quiche/quic/core/crypto/crypto_protocol.h"
14#include "quiche/quic/core/quic_time.h"
15#include "quiche/quic/core/quic_time_accumulator.h"
16#include "quiche/quic/platform/api/quic_bug_tracker.h"
17#include "quiche/quic/platform/api/quic_flag_utils.h"
18#include "quiche/quic/platform/api/quic_flags.h"
19#include "quiche/quic/platform/api/quic_logging.h"
20
21namespace quic {
22
23namespace {
24// Constants based on TCP defaults.
25// The minimum CWND to ensure delayed acks don't reduce bandwidth measurements.
26// Does not inflate the pacing rate.
27const QuicByteCount kDefaultMinimumCongestionWindow = 4 * kMaxSegmentSize;
28
29// The gain used for the STARTUP, equal to 2/ln(2).
30const float kDefaultHighGain = 2.885f;
31// The newly derived gain for STARTUP, equal to 4 * ln(2)
32const float kDerivedHighGain = 2.773f;
33// The newly derived CWND gain for STARTUP, 2.
34const float kDerivedHighCWNDGain = 2.0f;
35// The cycle of gains used during the PROBE_BW stage.
36const float kPacingGain[] = {1.25, 0.75, 1, 1, 1, 1, 1, 1};
37
38// The length of the gain cycle.
39const size_t kGainCycleLength = sizeof(kPacingGain) / sizeof(kPacingGain[0]);
40// The size of the bandwidth filter window, in round-trips.
41const QuicRoundTripCount kBandwidthWindowSize = kGainCycleLength + 2;
42
43// The time after which the current min_rtt value expires.
44const QuicTime::Delta kMinRttExpiry = QuicTime::Delta::FromSeconds(10);
45// The minimum time the connection can spend in PROBE_RTT mode.
46const QuicTime::Delta kProbeRttTime = QuicTime::Delta::FromMilliseconds(200);
47// If the bandwidth does not increase by the factor of |kStartupGrowthTarget|
48// within |kRoundTripsWithoutGrowthBeforeExitingStartup| rounds, the connection
49// will exit the STARTUP mode.
50const float kStartupGrowthTarget = 1.25;
51const QuicRoundTripCount kRoundTripsWithoutGrowthBeforeExitingStartup = 3;
52} // namespace
53
54BbrSender::DebugState::DebugState(const BbrSender& sender)
55 : mode(sender.mode_),
56 max_bandwidth(sender.max_bandwidth_.GetBest()),
57 round_trip_count(sender.round_trip_count_),
58 gain_cycle_index(sender.cycle_current_offset_),
59 congestion_window(sender.congestion_window_),
60 is_at_full_bandwidth(sender.is_at_full_bandwidth_),
61 bandwidth_at_last_round(sender.bandwidth_at_last_round_),
62 rounds_without_bandwidth_gain(sender.rounds_without_bandwidth_gain_),
63 min_rtt(sender.min_rtt_),
64 min_rtt_timestamp(sender.min_rtt_timestamp_),
65 recovery_state(sender.recovery_state_),
66 recovery_window(sender.recovery_window_),
67 last_sample_is_app_limited(sender.last_sample_is_app_limited_),
68 end_of_app_limited_phase(sender.sampler_.end_of_app_limited_phase()) {}
69
70BbrSender::DebugState::DebugState(const DebugState& state) = default;
71
bnc8fa40ee2022-04-12 13:30:20 -070072BbrSender::BbrSender(QuicTime now, const RttStats* rtt_stats,
Bence Békybac04052022-04-07 15:44:29 -040073 const QuicUnackedPacketMap* unacked_packets,
74 QuicPacketCount initial_tcp_congestion_window,
75 QuicPacketCount max_tcp_congestion_window,
bnc8fa40ee2022-04-12 13:30:20 -070076 QuicRandom* random, QuicConnectionStats* stats)
Bence Békybac04052022-04-07 15:44:29 -040077 : rtt_stats_(rtt_stats),
78 unacked_packets_(unacked_packets),
79 random_(random),
80 stats_(stats),
81 mode_(STARTUP),
82 sampler_(unacked_packets, kBandwidthWindowSize),
83 round_trip_count_(0),
84 num_loss_events_in_round_(0),
85 bytes_lost_in_round_(0),
86 max_bandwidth_(kBandwidthWindowSize, QuicBandwidth::Zero(), 0),
87 min_rtt_(QuicTime::Delta::Zero()),
88 min_rtt_timestamp_(QuicTime::Zero()),
89 congestion_window_(initial_tcp_congestion_window * kDefaultTCPMSS),
90 initial_congestion_window_(initial_tcp_congestion_window *
91 kDefaultTCPMSS),
92 max_congestion_window_(max_tcp_congestion_window * kDefaultTCPMSS),
93 min_congestion_window_(kDefaultMinimumCongestionWindow),
94 high_gain_(kDefaultHighGain),
95 high_cwnd_gain_(kDefaultHighGain),
96 drain_gain_(1.f / kDefaultHighGain),
97 pacing_rate_(QuicBandwidth::Zero()),
98 pacing_gain_(1),
99 congestion_window_gain_(1),
100 congestion_window_gain_constant_(
birenroyef686222022-09-12 11:34:34 -0700101 static_cast<float>(GetQuicFlag(quic_bbr_cwnd_gain))),
Bence Békybac04052022-04-07 15:44:29 -0400102 num_startup_rtts_(kRoundTripsWithoutGrowthBeforeExitingStartup),
103 cycle_current_offset_(0),
104 last_cycle_start_(QuicTime::Zero()),
105 is_at_full_bandwidth_(false),
106 rounds_without_bandwidth_gain_(0),
107 bandwidth_at_last_round_(QuicBandwidth::Zero()),
108 exiting_quiescence_(false),
109 exit_probe_rtt_at_(QuicTime::Zero()),
110 probe_rtt_round_passed_(false),
111 last_sample_is_app_limited_(false),
112 has_non_app_limited_sample_(false),
113 recovery_state_(NOT_IN_RECOVERY),
114 recovery_window_(max_congestion_window_),
115 slower_startup_(false),
116 rate_based_startup_(false),
117 enable_ack_aggregation_during_startup_(false),
118 expire_ack_aggregation_in_startup_(false),
119 drain_to_target_(false),
120 detect_overshooting_(false),
121 bytes_lost_while_detecting_overshooting_(0),
122 bytes_lost_multiplier_while_detecting_overshooting_(2),
123 cwnd_to_calculate_min_pacing_rate_(initial_congestion_window_),
124 max_congestion_window_with_network_parameters_adjusted_(
125 kMaxInitialCongestionWindow * kDefaultTCPMSS) {
126 if (stats_) {
127 // Clear some startup stats if |stats_| has been used by another sender,
128 // which happens e.g. when QuicConnection switch send algorithms.
129 stats_->slowstart_count = 0;
130 stats_->slowstart_duration = QuicTimeAccumulator();
131 }
132 EnterStartupMode(now);
133 set_high_cwnd_gain(kDerivedHighCWNDGain);
134}
135
136BbrSender::~BbrSender() {}
137
138void BbrSender::SetInitialCongestionWindowInPackets(
139 QuicPacketCount congestion_window) {
140 if (mode_ == STARTUP) {
141 initial_congestion_window_ = congestion_window * kDefaultTCPMSS;
142 congestion_window_ = congestion_window * kDefaultTCPMSS;
143 cwnd_to_calculate_min_pacing_rate_ = std::min(
144 initial_congestion_window_, cwnd_to_calculate_min_pacing_rate_);
145 }
146}
147
bnc8fa40ee2022-04-12 13:30:20 -0700148bool BbrSender::InSlowStart() const { return mode_ == STARTUP; }
Bence Békybac04052022-04-07 15:44:29 -0400149
bnc8fa40ee2022-04-12 13:30:20 -0700150void BbrSender::OnPacketSent(QuicTime sent_time, QuicByteCount bytes_in_flight,
Bence Békybac04052022-04-07 15:44:29 -0400151 QuicPacketNumber packet_number,
152 QuicByteCount bytes,
153 HasRetransmittableData is_retransmittable) {
154 if (stats_ && InSlowStart()) {
155 ++stats_->slowstart_packets_sent;
156 stats_->slowstart_bytes_sent += bytes;
157 }
158
159 last_sent_packet_ = packet_number;
160
161 if (bytes_in_flight == 0 && sampler_.is_app_limited()) {
162 exiting_quiescence_ = true;
163 }
164
165 sampler_.OnPacketSent(sent_time, packet_number, bytes, bytes_in_flight,
166 is_retransmittable);
167}
168
169void BbrSender::OnPacketNeutered(QuicPacketNumber packet_number) {
170 sampler_.OnPacketNeutered(packet_number);
171}
172
173bool BbrSender::CanSend(QuicByteCount bytes_in_flight) {
174 return bytes_in_flight < GetCongestionWindow();
175}
176
177QuicBandwidth BbrSender::PacingRate(QuicByteCount /*bytes_in_flight*/) const {
178 if (pacing_rate_.IsZero()) {
179 return high_gain_ * QuicBandwidth::FromBytesAndTimeDelta(
180 initial_congestion_window_, GetMinRtt());
181 }
182 return pacing_rate_;
183}
184
185QuicBandwidth BbrSender::BandwidthEstimate() const {
186 return max_bandwidth_.GetBest();
187}
188
189QuicByteCount BbrSender::GetCongestionWindow() const {
190 if (mode_ == PROBE_RTT) {
191 return ProbeRttCongestionWindow();
192 }
193
194 if (InRecovery()) {
195 return std::min(congestion_window_, recovery_window_);
196 }
197
198 return congestion_window_;
199}
200
bnc8fa40ee2022-04-12 13:30:20 -0700201QuicByteCount BbrSender::GetSlowStartThreshold() const { return 0; }
Bence Békybac04052022-04-07 15:44:29 -0400202
203bool BbrSender::InRecovery() const {
204 return recovery_state_ != NOT_IN_RECOVERY;
205}
206
Bence Békybac04052022-04-07 15:44:29 -0400207void BbrSender::SetFromConfig(const QuicConfig& config,
208 Perspective perspective) {
209 if (config.HasClientRequestedIndependentOption(k1RTT, perspective)) {
210 num_startup_rtts_ = 1;
211 }
212 if (config.HasClientRequestedIndependentOption(k2RTT, perspective)) {
213 num_startup_rtts_ = 2;
214 }
215 if (config.HasClientRequestedIndependentOption(kBBR3, perspective)) {
216 drain_to_target_ = true;
217 }
218 if (config.HasClientRequestedIndependentOption(kBWM3, perspective)) {
219 bytes_lost_multiplier_while_detecting_overshooting_ = 3;
220 }
221 if (config.HasClientRequestedIndependentOption(kBWM4, perspective)) {
222 bytes_lost_multiplier_while_detecting_overshooting_ = 4;
223 }
224 if (config.HasClientRequestedIndependentOption(kBBR4, perspective)) {
225 sampler_.SetMaxAckHeightTrackerWindowLength(2 * kBandwidthWindowSize);
226 }
227 if (config.HasClientRequestedIndependentOption(kBBR5, perspective)) {
228 sampler_.SetMaxAckHeightTrackerWindowLength(4 * kBandwidthWindowSize);
229 }
230 if (config.HasClientRequestedIndependentOption(kBBQ1, perspective)) {
231 set_high_gain(kDerivedHighGain);
232 set_high_cwnd_gain(kDerivedHighGain);
wub7e7968b2022-08-22 08:29:27 -0700233 set_drain_gain(1.0 / kDerivedHighCWNDGain);
Bence Békybac04052022-04-07 15:44:29 -0400234 }
235 if (config.HasClientRequestedIndependentOption(kBBQ3, perspective)) {
236 enable_ack_aggregation_during_startup_ = true;
237 }
238 if (config.HasClientRequestedIndependentOption(kBBQ5, perspective)) {
239 expire_ack_aggregation_in_startup_ = true;
240 }
241 if (config.HasClientRequestedIndependentOption(kMIN1, perspective)) {
242 min_congestion_window_ = kMaxSegmentSize;
243 }
244 if (config.HasClientRequestedIndependentOption(kICW1, perspective)) {
245 max_congestion_window_with_network_parameters_adjusted_ =
246 100 * kDefaultTCPMSS;
247 }
248 if (config.HasClientRequestedIndependentOption(kDTOS, perspective)) {
249 detect_overshooting_ = true;
250 // DTOS would allow pacing rate drop to IW 10 / min_rtt if overshooting is
251 // detected.
252 cwnd_to_calculate_min_pacing_rate_ =
253 std::min(initial_congestion_window_, 10 * kDefaultTCPMSS);
254 }
255
256 ApplyConnectionOptions(config.ClientRequestedIndependentOptions(perspective));
257}
258
259void BbrSender::ApplyConnectionOptions(
260 const QuicTagVector& connection_options) {
261 if (ContainsQuicTag(connection_options, kBSAO)) {
262 sampler_.EnableOverestimateAvoidance();
263 }
264 if (ContainsQuicTag(connection_options, kBBRA)) {
265 sampler_.SetStartNewAggregationEpochAfterFullRound(true);
266 }
267 if (ContainsQuicTag(connection_options, kBBRB)) {
268 sampler_.SetLimitMaxAckHeightTrackerBySendRate(true);
269 }
270}
271
272void BbrSender::AdjustNetworkParameters(const NetworkParams& params) {
273 const QuicBandwidth& bandwidth = params.bandwidth;
274 const QuicTime::Delta& rtt = params.rtt;
275
276 if (!rtt.IsZero() && (min_rtt_ > rtt || min_rtt_.IsZero())) {
277 min_rtt_ = rtt;
278 }
279
280 if (mode_ == STARTUP) {
281 if (bandwidth.IsZero()) {
282 // Ignore bad bandwidth samples.
283 return;
284 }
285
286 auto cwnd_bootstrapping_rtt = GetMinRtt();
287 if (params.max_initial_congestion_window > 0) {
288 max_congestion_window_with_network_parameters_adjusted_ =
289 params.max_initial_congestion_window * kDefaultTCPMSS;
290 }
291 const QuicByteCount new_cwnd = std::max(
292 kMinInitialCongestionWindow * kDefaultTCPMSS,
293 std::min(max_congestion_window_with_network_parameters_adjusted_,
294 bandwidth * cwnd_bootstrapping_rtt));
295
296 stats_->cwnd_bootstrapping_rtt_us = cwnd_bootstrapping_rtt.ToMicroseconds();
297 if (!rtt_stats_->smoothed_rtt().IsZero()) {
298 QUIC_CODE_COUNT(quic_smoothed_rtt_available);
299 } else if (rtt_stats_->initial_rtt() !=
300 QuicTime::Delta::FromMilliseconds(kInitialRttMs)) {
301 QUIC_CODE_COUNT(quic_client_initial_rtt_available);
302 } else {
303 QUIC_CODE_COUNT(quic_default_initial_rtt);
304 }
305 if (new_cwnd < congestion_window_ && !params.allow_cwnd_to_decrease) {
306 // Only decrease cwnd if allow_cwnd_to_decrease is true.
307 return;
308 }
309 if (GetQuicReloadableFlag(quic_conservative_cwnd_and_pacing_gains)) {
310 // Decreases cwnd gain and pacing gain. Please note, if pacing_rate_ has
311 // been calculated, it cannot decrease in STARTUP phase.
312 QUIC_RELOADABLE_FLAG_COUNT(quic_conservative_cwnd_and_pacing_gains);
313 set_high_gain(kDerivedHighCWNDGain);
314 set_high_cwnd_gain(kDerivedHighCWNDGain);
315 }
316 congestion_window_ = new_cwnd;
317
318 // Pace at the rate of new_cwnd / RTT.
319 QuicBandwidth new_pacing_rate =
320 QuicBandwidth::FromBytesAndTimeDelta(congestion_window_, GetMinRtt());
321 pacing_rate_ = std::max(pacing_rate_, new_pacing_rate);
322 detect_overshooting_ = true;
323 }
324}
325
326void BbrSender::OnCongestionEvent(bool /*rtt_updated*/,
327 QuicByteCount prior_in_flight,
328 QuicTime event_time,
329 const AckedPacketVector& acked_packets,
330 const LostPacketVector& lost_packets) {
331 const QuicByteCount total_bytes_acked_before = sampler_.total_bytes_acked();
332 const QuicByteCount total_bytes_lost_before = sampler_.total_bytes_lost();
333
334 bool is_round_start = false;
335 bool min_rtt_expired = false;
336 QuicByteCount excess_acked = 0;
337 QuicByteCount bytes_lost = 0;
338
339 // The send state of the largest packet in acked_packets, unless it is
340 // empty. If acked_packets is empty, it's the send state of the largest
341 // packet in lost_packets.
342 SendTimeState last_packet_send_state;
343
344 if (!acked_packets.empty()) {
345 QuicPacketNumber last_acked_packet = acked_packets.rbegin()->packet_number;
346 is_round_start = UpdateRoundTripCounter(last_acked_packet);
347 UpdateRecoveryState(last_acked_packet, !lost_packets.empty(),
348 is_round_start);
349 }
350
351 BandwidthSamplerInterface::CongestionEventSample sample =
352 sampler_.OnCongestionEvent(event_time, acked_packets, lost_packets,
353 max_bandwidth_.GetBest(),
354 QuicBandwidth::Infinite(), round_trip_count_);
355 if (sample.last_packet_send_state.is_valid) {
356 last_sample_is_app_limited_ = sample.last_packet_send_state.is_app_limited;
357 has_non_app_limited_sample_ |= !last_sample_is_app_limited_;
358 if (stats_) {
359 stats_->has_non_app_limited_sample = has_non_app_limited_sample_;
360 }
361 }
362 // Avoid updating |max_bandwidth_| if a) this is a loss-only event, or b) all
363 // packets in |acked_packets| did not generate valid samples. (e.g. ack of
364 // ack-only packets). In both cases, sampler_.total_bytes_acked() will not
365 // change.
366 if (total_bytes_acked_before != sampler_.total_bytes_acked()) {
367 QUIC_LOG_IF(WARNING, sample.sample_max_bandwidth.IsZero())
368 << sampler_.total_bytes_acked() - total_bytes_acked_before
369 << " bytes from " << acked_packets.size()
370 << " packets have been acked, but sample_max_bandwidth is zero.";
371 if (!sample.sample_is_app_limited ||
372 sample.sample_max_bandwidth > max_bandwidth_.GetBest()) {
373 max_bandwidth_.Update(sample.sample_max_bandwidth, round_trip_count_);
374 }
375 }
376
377 if (!sample.sample_rtt.IsInfinite()) {
378 min_rtt_expired = MaybeUpdateMinRtt(event_time, sample.sample_rtt);
379 }
380 bytes_lost = sampler_.total_bytes_lost() - total_bytes_lost_before;
381 if (mode_ == STARTUP) {
382 if (stats_) {
383 stats_->slowstart_packets_lost += lost_packets.size();
384 stats_->slowstart_bytes_lost += bytes_lost;
385 }
386 }
387 excess_acked = sample.extra_acked;
388 last_packet_send_state = sample.last_packet_send_state;
389
390 if (!lost_packets.empty()) {
391 ++num_loss_events_in_round_;
392 bytes_lost_in_round_ += bytes_lost;
393 }
394
395 // Handle logic specific to PROBE_BW mode.
396 if (mode_ == PROBE_BW) {
397 UpdateGainCyclePhase(event_time, prior_in_flight, !lost_packets.empty());
398 }
399
400 // Handle logic specific to STARTUP and DRAIN modes.
401 if (is_round_start && !is_at_full_bandwidth_) {
402 CheckIfFullBandwidthReached(last_packet_send_state);
403 }
404 MaybeExitStartupOrDrain(event_time);
405
406 // Handle logic specific to PROBE_RTT.
407 MaybeEnterOrExitProbeRtt(event_time, is_round_start, min_rtt_expired);
408
409 // Calculate number of packets acked and lost.
410 QuicByteCount bytes_acked =
411 sampler_.total_bytes_acked() - total_bytes_acked_before;
412
413 // After the model is updated, recalculate the pacing rate and congestion
414 // window.
415 CalculatePacingRate(bytes_lost);
416 CalculateCongestionWindow(bytes_acked, excess_acked);
417 CalculateRecoveryWindow(bytes_acked, bytes_lost);
418
419 // Cleanup internal state.
420 sampler_.RemoveObsoletePackets(unacked_packets_->GetLeastUnacked());
421 if (is_round_start) {
422 num_loss_events_in_round_ = 0;
423 bytes_lost_in_round_ = 0;
424 }
425}
426
427CongestionControlType BbrSender::GetCongestionControlType() const {
428 return kBBR;
429}
430
431QuicTime::Delta BbrSender::GetMinRtt() const {
432 if (!min_rtt_.IsZero()) {
433 return min_rtt_;
434 }
435 // min_rtt could be available if the handshake packet gets neutered then
436 // gets acknowledged. This could only happen for QUIC crypto where we do not
437 // drop keys.
438 return rtt_stats_->MinOrInitialRtt();
439}
440
441QuicByteCount BbrSender::GetTargetCongestionWindow(float gain) const {
442 QuicByteCount bdp = GetMinRtt() * BandwidthEstimate();
443 QuicByteCount congestion_window = gain * bdp;
444
445 // BDP estimate will be zero if no bandwidth samples are available yet.
446 if (congestion_window == 0) {
447 congestion_window = gain * initial_congestion_window_;
448 }
449
450 return std::max(congestion_window, min_congestion_window_);
451}
452
453QuicByteCount BbrSender::ProbeRttCongestionWindow() const {
454 return min_congestion_window_;
455}
456
457void BbrSender::EnterStartupMode(QuicTime now) {
458 if (stats_) {
459 ++stats_->slowstart_count;
460 stats_->slowstart_duration.Start(now);
461 }
462 mode_ = STARTUP;
463 pacing_gain_ = high_gain_;
464 congestion_window_gain_ = high_cwnd_gain_;
465}
466
467void BbrSender::EnterProbeBandwidthMode(QuicTime now) {
468 mode_ = PROBE_BW;
469 congestion_window_gain_ = congestion_window_gain_constant_;
470
471 // Pick a random offset for the gain cycle out of {0, 2..7} range. 1 is
472 // excluded because in that case increased gain and decreased gain would not
473 // follow each other.
474 cycle_current_offset_ = random_->RandUint64() % (kGainCycleLength - 1);
475 if (cycle_current_offset_ >= 1) {
476 cycle_current_offset_ += 1;
477 }
478
479 last_cycle_start_ = now;
480 pacing_gain_ = kPacingGain[cycle_current_offset_];
481}
482
483bool BbrSender::UpdateRoundTripCounter(QuicPacketNumber last_acked_packet) {
484 if (!current_round_trip_end_.IsInitialized() ||
485 last_acked_packet > current_round_trip_end_) {
486 round_trip_count_++;
487 current_round_trip_end_ = last_sent_packet_;
488 if (stats_ && InSlowStart()) {
489 ++stats_->slowstart_num_rtts;
490 }
491 return true;
492 }
493
494 return false;
495}
496
497bool BbrSender::MaybeUpdateMinRtt(QuicTime now,
498 QuicTime::Delta sample_min_rtt) {
499 // Do not expire min_rtt if none was ever available.
500 bool min_rtt_expired =
501 !min_rtt_.IsZero() && (now > (min_rtt_timestamp_ + kMinRttExpiry));
502
503 if (min_rtt_expired || sample_min_rtt < min_rtt_ || min_rtt_.IsZero()) {
504 QUIC_DVLOG(2) << "Min RTT updated, old value: " << min_rtt_
505 << ", new value: " << sample_min_rtt
506 << ", current time: " << now.ToDebuggingValue();
507
508 min_rtt_ = sample_min_rtt;
509 min_rtt_timestamp_ = now;
510 }
511 QUICHE_DCHECK(!min_rtt_.IsZero());
512
513 return min_rtt_expired;
514}
515
516void BbrSender::UpdateGainCyclePhase(QuicTime now,
517 QuicByteCount prior_in_flight,
518 bool has_losses) {
519 const QuicByteCount bytes_in_flight = unacked_packets_->bytes_in_flight();
520 // In most cases, the cycle is advanced after an RTT passes.
521 bool should_advance_gain_cycling = now - last_cycle_start_ > GetMinRtt();
522
523 // If the pacing gain is above 1.0, the connection is trying to probe the
524 // bandwidth by increasing the number of bytes in flight to at least
525 // pacing_gain * BDP. Make sure that it actually reaches the target, as long
526 // as there are no losses suggesting that the buffers are not able to hold
527 // that much.
528 if (pacing_gain_ > 1.0 && !has_losses &&
529 prior_in_flight < GetTargetCongestionWindow(pacing_gain_)) {
530 should_advance_gain_cycling = false;
531 }
532
533 // If pacing gain is below 1.0, the connection is trying to drain the extra
534 // queue which could have been incurred by probing prior to it. If the number
535 // of bytes in flight falls down to the estimated BDP value earlier, conclude
536 // that the queue has been successfully drained and exit this cycle early.
537 if (pacing_gain_ < 1.0 && bytes_in_flight <= GetTargetCongestionWindow(1)) {
538 should_advance_gain_cycling = true;
539 }
540
541 if (should_advance_gain_cycling) {
542 cycle_current_offset_ = (cycle_current_offset_ + 1) % kGainCycleLength;
543 if (cycle_current_offset_ == 0) {
544 ++stats_->bbr_num_cycles;
545 }
546 last_cycle_start_ = now;
547 // Stay in low gain mode until the target BDP is hit.
548 // Low gain mode will be exited immediately when the target BDP is achieved.
549 if (drain_to_target_ && pacing_gain_ < 1 &&
550 kPacingGain[cycle_current_offset_] == 1 &&
551 bytes_in_flight > GetTargetCongestionWindow(1)) {
552 return;
553 }
554 pacing_gain_ = kPacingGain[cycle_current_offset_];
555 }
556}
557
558void BbrSender::CheckIfFullBandwidthReached(
559 const SendTimeState& last_packet_send_state) {
560 if (last_sample_is_app_limited_) {
561 return;
562 }
563
564 QuicBandwidth target = bandwidth_at_last_round_ * kStartupGrowthTarget;
565 if (BandwidthEstimate() >= target) {
566 bandwidth_at_last_round_ = BandwidthEstimate();
567 rounds_without_bandwidth_gain_ = 0;
568 if (expire_ack_aggregation_in_startup_) {
569 // Expire old excess delivery measurements now that bandwidth increased.
570 sampler_.ResetMaxAckHeightTracker(0, round_trip_count_);
571 }
572 return;
573 }
574
575 rounds_without_bandwidth_gain_++;
576 if ((rounds_without_bandwidth_gain_ >= num_startup_rtts_) ||
577 ShouldExitStartupDueToLoss(last_packet_send_state)) {
578 QUICHE_DCHECK(has_non_app_limited_sample_);
579 is_at_full_bandwidth_ = true;
580 }
581}
582
583void BbrSender::MaybeExitStartupOrDrain(QuicTime now) {
584 if (mode_ == STARTUP && is_at_full_bandwidth_) {
585 OnExitStartup(now);
586 mode_ = DRAIN;
587 pacing_gain_ = drain_gain_;
588 congestion_window_gain_ = high_cwnd_gain_;
589 }
590 if (mode_ == DRAIN &&
591 unacked_packets_->bytes_in_flight() <= GetTargetCongestionWindow(1)) {
592 EnterProbeBandwidthMode(now);
593 }
594}
595
596void BbrSender::OnExitStartup(QuicTime now) {
597 QUICHE_DCHECK_EQ(mode_, STARTUP);
598 if (stats_) {
599 stats_->slowstart_duration.Stop(now);
600 }
601}
602
603bool BbrSender::ShouldExitStartupDueToLoss(
604 const SendTimeState& last_packet_send_state) const {
605 if (num_loss_events_in_round_ <
birenroyef686222022-09-12 11:34:34 -0700606 GetQuicFlag(quic_bbr2_default_startup_full_loss_count) ||
Bence Békybac04052022-04-07 15:44:29 -0400607 !last_packet_send_state.is_valid) {
608 return false;
609 }
610
611 const QuicByteCount inflight_at_send = last_packet_send_state.bytes_in_flight;
612
613 if (inflight_at_send > 0 && bytes_lost_in_round_ > 0) {
614 if (bytes_lost_in_round_ >
birenroyef686222022-09-12 11:34:34 -0700615 inflight_at_send * GetQuicFlag(quic_bbr2_default_loss_threshold)) {
Bence Békybac04052022-04-07 15:44:29 -0400616 stats_->bbr_exit_startup_due_to_loss = true;
617 return true;
618 }
619 return false;
620 }
621
622 return false;
623}
624
bnc8fa40ee2022-04-12 13:30:20 -0700625void BbrSender::MaybeEnterOrExitProbeRtt(QuicTime now, bool is_round_start,
Bence Békybac04052022-04-07 15:44:29 -0400626 bool min_rtt_expired) {
627 if (min_rtt_expired && !exiting_quiescence_ && mode_ != PROBE_RTT) {
628 if (InSlowStart()) {
629 OnExitStartup(now);
630 }
631 mode_ = PROBE_RTT;
632 pacing_gain_ = 1;
633 // Do not decide on the time to exit PROBE_RTT until the |bytes_in_flight|
634 // is at the target small value.
635 exit_probe_rtt_at_ = QuicTime::Zero();
636 }
637
638 if (mode_ == PROBE_RTT) {
639 sampler_.OnAppLimited();
640
641 if (exit_probe_rtt_at_ == QuicTime::Zero()) {
642 // If the window has reached the appropriate size, schedule exiting
643 // PROBE_RTT. The CWND during PROBE_RTT is kMinimumCongestionWindow, but
644 // we allow an extra packet since QUIC checks CWND before sending a
645 // packet.
646 if (unacked_packets_->bytes_in_flight() <
647 ProbeRttCongestionWindow() + kMaxOutgoingPacketSize) {
648 exit_probe_rtt_at_ = now + kProbeRttTime;
649 probe_rtt_round_passed_ = false;
650 }
651 } else {
652 if (is_round_start) {
653 probe_rtt_round_passed_ = true;
654 }
655 if (now >= exit_probe_rtt_at_ && probe_rtt_round_passed_) {
656 min_rtt_timestamp_ = now;
657 if (!is_at_full_bandwidth_) {
658 EnterStartupMode(now);
659 } else {
660 EnterProbeBandwidthMode(now);
661 }
662 }
663 }
664 }
665
666 exiting_quiescence_ = false;
667}
668
669void BbrSender::UpdateRecoveryState(QuicPacketNumber last_acked_packet,
bnc8fa40ee2022-04-12 13:30:20 -0700670 bool has_losses, bool is_round_start) {
Bence Békybac04052022-04-07 15:44:29 -0400671 // Disable recovery in startup, if loss-based exit is enabled.
672 if (!is_at_full_bandwidth_) {
673 return;
674 }
675
676 // Exit recovery when there are no losses for a round.
677 if (has_losses) {
678 end_recovery_at_ = last_sent_packet_;
679 }
680
681 switch (recovery_state_) {
682 case NOT_IN_RECOVERY:
683 // Enter conservation on the first loss.
684 if (has_losses) {
685 recovery_state_ = CONSERVATION;
686 // This will cause the |recovery_window_| to be set to the correct
687 // value in CalculateRecoveryWindow().
688 recovery_window_ = 0;
689 // Since the conservation phase is meant to be lasting for a whole
690 // round, extend the current round as if it were started right now.
691 current_round_trip_end_ = last_sent_packet_;
692 }
693 break;
694
695 case CONSERVATION:
696 if (is_round_start) {
697 recovery_state_ = GROWTH;
698 }
699 ABSL_FALLTHROUGH_INTENDED;
700
701 case GROWTH:
702 // Exit recovery if appropriate.
703 if (!has_losses && last_acked_packet > end_recovery_at_) {
704 recovery_state_ = NOT_IN_RECOVERY;
705 }
706
707 break;
708 }
709}
710
711void BbrSender::CalculatePacingRate(QuicByteCount bytes_lost) {
712 if (BandwidthEstimate().IsZero()) {
713 return;
714 }
715
716 QuicBandwidth target_rate = pacing_gain_ * BandwidthEstimate();
717 if (is_at_full_bandwidth_) {
718 pacing_rate_ = target_rate;
719 return;
720 }
721
722 // Pace at the rate of initial_window / RTT as soon as RTT measurements are
723 // available.
724 if (pacing_rate_.IsZero() && !rtt_stats_->min_rtt().IsZero()) {
725 pacing_rate_ = QuicBandwidth::FromBytesAndTimeDelta(
726 initial_congestion_window_, rtt_stats_->min_rtt());
727 return;
728 }
729
730 if (detect_overshooting_) {
731 bytes_lost_while_detecting_overshooting_ += bytes_lost;
732 // Check for overshooting with network parameters adjusted when pacing rate
733 // > target_rate and loss has been detected.
734 if (pacing_rate_ > target_rate &&
735 bytes_lost_while_detecting_overshooting_ > 0) {
736 if (has_non_app_limited_sample_ ||
737 bytes_lost_while_detecting_overshooting_ *
738 bytes_lost_multiplier_while_detecting_overshooting_ >
739 initial_congestion_window_) {
740 // We are fairly sure overshoot happens if 1) there is at least one
741 // non app-limited bw sample or 2) half of IW gets lost. Slow pacing
742 // rate.
743 pacing_rate_ = std::max(
744 target_rate, QuicBandwidth::FromBytesAndTimeDelta(
745 cwnd_to_calculate_min_pacing_rate_, GetMinRtt()));
746 if (stats_) {
747 stats_->overshooting_detected_with_network_parameters_adjusted = true;
748 }
749 bytes_lost_while_detecting_overshooting_ = 0;
750 detect_overshooting_ = false;
751 }
752 }
753 }
754
755 // Do not decrease the pacing rate during startup.
756 pacing_rate_ = std::max(pacing_rate_, target_rate);
757}
758
759void BbrSender::CalculateCongestionWindow(QuicByteCount bytes_acked,
760 QuicByteCount excess_acked) {
761 if (mode_ == PROBE_RTT) {
762 return;
763 }
764
765 QuicByteCount target_window =
766 GetTargetCongestionWindow(congestion_window_gain_);
767 if (is_at_full_bandwidth_) {
768 // Add the max recently measured ack aggregation to CWND.
769 target_window += sampler_.max_ack_height();
770 } else if (enable_ack_aggregation_during_startup_) {
771 // Add the most recent excess acked. Because CWND never decreases in
772 // STARTUP, this will automatically create a very localized max filter.
773 target_window += excess_acked;
774 }
775
776 // Instead of immediately setting the target CWND as the new one, BBR grows
777 // the CWND towards |target_window| by only increasing it |bytes_acked| at a
778 // time.
779 if (is_at_full_bandwidth_) {
780 congestion_window_ =
781 std::min(target_window, congestion_window_ + bytes_acked);
782 } else if (congestion_window_ < target_window ||
783 sampler_.total_bytes_acked() < initial_congestion_window_) {
784 // If the connection is not yet out of startup phase, do not decrease the
785 // window.
786 congestion_window_ = congestion_window_ + bytes_acked;
787 }
788
789 // Enforce the limits on the congestion window.
790 congestion_window_ = std::max(congestion_window_, min_congestion_window_);
791 congestion_window_ = std::min(congestion_window_, max_congestion_window_);
792}
793
794void BbrSender::CalculateRecoveryWindow(QuicByteCount bytes_acked,
795 QuicByteCount bytes_lost) {
796 if (recovery_state_ == NOT_IN_RECOVERY) {
797 return;
798 }
799
800 // Set up the initial recovery window.
801 if (recovery_window_ == 0) {
802 recovery_window_ = unacked_packets_->bytes_in_flight() + bytes_acked;
803 recovery_window_ = std::max(min_congestion_window_, recovery_window_);
804 return;
805 }
806
807 // Remove losses from the recovery window, while accounting for a potential
808 // integer underflow.
809 recovery_window_ = recovery_window_ >= bytes_lost
810 ? recovery_window_ - bytes_lost
811 : kMaxSegmentSize;
812
813 // In CONSERVATION mode, just subtracting losses is sufficient. In GROWTH,
814 // release additional |bytes_acked| to achieve a slow-start-like behavior.
815 if (recovery_state_ == GROWTH) {
816 recovery_window_ += bytes_acked;
817 }
818
819 // Always allow sending at least |bytes_acked| in response.
820 recovery_window_ = std::max(
821 recovery_window_, unacked_packets_->bytes_in_flight() + bytes_acked);
822 recovery_window_ = std::max(min_congestion_window_, recovery_window_);
823}
824
825std::string BbrSender::GetDebugState() const {
826 std::ostringstream stream;
827 stream << ExportDebugState();
828 return stream.str();
829}
830
831void BbrSender::OnApplicationLimited(QuicByteCount bytes_in_flight) {
832 if (bytes_in_flight >= GetCongestionWindow()) {
833 return;
834 }
835
836 sampler_.OnAppLimited();
837 QUIC_DVLOG(2) << "Becoming application limited. Last sent packet: "
838 << last_sent_packet_ << ", CWND: " << GetCongestionWindow();
839}
840
841void BbrSender::PopulateConnectionStats(QuicConnectionStats* stats) const {
842 stats->num_ack_aggregation_epochs = sampler_.num_ack_aggregation_epochs();
843}
844
845BbrSender::DebugState BbrSender::ExportDebugState() const {
846 return DebugState(*this);
847}
848
849static std::string ModeToString(BbrSender::Mode mode) {
850 switch (mode) {
851 case BbrSender::STARTUP:
852 return "STARTUP";
853 case BbrSender::DRAIN:
854 return "DRAIN";
855 case BbrSender::PROBE_BW:
856 return "PROBE_BW";
857 case BbrSender::PROBE_RTT:
858 return "PROBE_RTT";
859 }
860 return "???";
861}
862
863std::ostream& operator<<(std::ostream& os, const BbrSender::Mode& mode) {
864 os << ModeToString(mode);
865 return os;
866}
867
868std::ostream& operator<<(std::ostream& os, const BbrSender::DebugState& state) {
869 os << "Mode: " << ModeToString(state.mode) << std::endl;
870 os << "Maximum bandwidth: " << state.max_bandwidth << std::endl;
871 os << "Round trip counter: " << state.round_trip_count << std::endl;
872 os << "Gain cycle index: " << static_cast<int>(state.gain_cycle_index)
873 << std::endl;
874 os << "Congestion window: " << state.congestion_window << " bytes"
875 << std::endl;
876
877 if (state.mode == BbrSender::STARTUP) {
878 os << "(startup) Bandwidth at last round: " << state.bandwidth_at_last_round
879 << std::endl;
880 os << "(startup) Rounds without gain: "
881 << state.rounds_without_bandwidth_gain << std::endl;
882 }
883
884 os << "Minimum RTT: " << state.min_rtt << std::endl;
885 os << "Minimum RTT timestamp: " << state.min_rtt_timestamp.ToDebuggingValue()
886 << std::endl;
887
888 os << "Last sample is app-limited: "
889 << (state.last_sample_is_app_limited ? "yes" : "no");
890
891 return os;
892}
893
894} // namespace quic