1 // 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
21 namespace quic {
22
23 namespace {
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.
27 const QuicByteCount kDefaultMinimumCongestionWindow = 4 * kMaxSegmentSize;
28
29 // The gain used for the STARTUP, equal to 2/ln(2).
30 const float kDefaultHighGain = 2.885f;
31 // The newly derived gain for STARTUP, equal to 4 * ln(2)
32 const float kDerivedHighGain = 2.773f;
33 // The newly derived CWND gain for STARTUP, 2.
34 const float kDerivedHighCWNDGain = 2.0f;
35 // The cycle of gains used during the PROBE_BW stage.
36 const float kPacingGain[] = {1.25, 0.75, 1, 1, 1, 1, 1, 1};
37
38 // The length of the gain cycle.
39 const size_t kGainCycleLength = sizeof(kPacingGain) / sizeof(kPacingGain[0]);
40 // The size of the bandwidth filter window, in round-trips.
41 const QuicRoundTripCount kBandwidthWindowSize = kGainCycleLength + 2;
42
43 // The time after which the current min_rtt value expires.
44 const QuicTime::Delta kMinRttExpiry = QuicTime::Delta::FromSeconds(10);
45 // The minimum time the connection can spend in PROBE_RTT mode.
46 const 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.
50 const float kStartupGrowthTarget = 1.25;
51 const QuicRoundTripCount kRoundTripsWithoutGrowthBeforeExitingStartup = 3;
52 } // namespace
53
DebugState(const BbrSender & sender)54 BbrSender::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
70 BbrSender::DebugState::DebugState(const DebugState& state) = default;
71
BbrSender(QuicTime now,const RttStats * rtt_stats,const QuicUnackedPacketMap * unacked_packets,QuicPacketCount initial_tcp_congestion_window,QuicPacketCount max_tcp_congestion_window,QuicRandom * random,QuicConnectionStats * stats)72 BbrSender::BbrSender(QuicTime now, const RttStats* rtt_stats,
73 const QuicUnackedPacketMap* unacked_packets,
74 QuicPacketCount initial_tcp_congestion_window,
75 QuicPacketCount max_tcp_congestion_window,
76 QuicRandom* random, QuicConnectionStats* stats)
77 : 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_(
101 static_cast<float>(GetQuicFlag(quic_bbr_cwnd_gain))),
102 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
~BbrSender()136 BbrSender::~BbrSender() {}
137
SetInitialCongestionWindowInPackets(QuicPacketCount congestion_window)138 void 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
InSlowStart() const148 bool BbrSender::InSlowStart() const { return mode_ == STARTUP; }
149
OnPacketSent(QuicTime sent_time,QuicByteCount bytes_in_flight,QuicPacketNumber packet_number,QuicByteCount bytes,HasRetransmittableData is_retransmittable)150 void BbrSender::OnPacketSent(QuicTime sent_time, QuicByteCount bytes_in_flight,
151 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
OnPacketNeutered(QuicPacketNumber packet_number)169 void BbrSender::OnPacketNeutered(QuicPacketNumber packet_number) {
170 sampler_.OnPacketNeutered(packet_number);
171 }
172
CanSend(QuicByteCount bytes_in_flight)173 bool BbrSender::CanSend(QuicByteCount bytes_in_flight) {
174 return bytes_in_flight < GetCongestionWindow();
175 }
176
PacingRate(QuicByteCount) const177 QuicBandwidth 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
BandwidthEstimate() const185 QuicBandwidth BbrSender::BandwidthEstimate() const {
186 return max_bandwidth_.GetBest();
187 }
188
GetCongestionWindow() const189 QuicByteCount 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
GetSlowStartThreshold() const201 QuicByteCount BbrSender::GetSlowStartThreshold() const { return 0; }
202
InRecovery() const203 bool BbrSender::InRecovery() const {
204 return recovery_state_ != NOT_IN_RECOVERY;
205 }
206
SetFromConfig(const QuicConfig & config,Perspective perspective)207 void 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);
233 set_drain_gain(1.0 / kDerivedHighCWNDGain);
234 }
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
ApplyConnectionOptions(const QuicTagVector & connection_options)259 void 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
AdjustNetworkParameters(const NetworkParams & params)272 void 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
OnCongestionEvent(bool,QuicByteCount prior_in_flight,QuicTime event_time,const AckedPacketVector & acked_packets,const LostPacketVector & lost_packets,QuicPacketCount,QuicPacketCount)326 void 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 QuicPacketCount /*num_ect*/,
332 QuicPacketCount /*num_ce*/) {
333 const QuicByteCount total_bytes_acked_before = sampler_.total_bytes_acked();
334 const QuicByteCount total_bytes_lost_before = sampler_.total_bytes_lost();
335
336 bool is_round_start = false;
337 bool min_rtt_expired = false;
338 QuicByteCount excess_acked = 0;
339 QuicByteCount bytes_lost = 0;
340
341 // The send state of the largest packet in acked_packets, unless it is
342 // empty. If acked_packets is empty, it's the send state of the largest
343 // packet in lost_packets.
344 SendTimeState last_packet_send_state;
345
346 if (!acked_packets.empty()) {
347 QuicPacketNumber last_acked_packet = acked_packets.rbegin()->packet_number;
348 is_round_start = UpdateRoundTripCounter(last_acked_packet);
349 UpdateRecoveryState(last_acked_packet, !lost_packets.empty(),
350 is_round_start);
351 }
352
353 BandwidthSamplerInterface::CongestionEventSample sample =
354 sampler_.OnCongestionEvent(event_time, acked_packets, lost_packets,
355 max_bandwidth_.GetBest(),
356 QuicBandwidth::Infinite(), round_trip_count_);
357 if (sample.last_packet_send_state.is_valid) {
358 last_sample_is_app_limited_ = sample.last_packet_send_state.is_app_limited;
359 has_non_app_limited_sample_ |= !last_sample_is_app_limited_;
360 if (stats_) {
361 stats_->has_non_app_limited_sample = has_non_app_limited_sample_;
362 }
363 }
364 // Avoid updating |max_bandwidth_| if a) this is a loss-only event, or b) all
365 // packets in |acked_packets| did not generate valid samples. (e.g. ack of
366 // ack-only packets). In both cases, sampler_.total_bytes_acked() will not
367 // change.
368 if (total_bytes_acked_before != sampler_.total_bytes_acked()) {
369 QUIC_LOG_IF(WARNING, sample.sample_max_bandwidth.IsZero())
370 << sampler_.total_bytes_acked() - total_bytes_acked_before
371 << " bytes from " << acked_packets.size()
372 << " packets have been acked, but sample_max_bandwidth is zero.";
373 if (!sample.sample_is_app_limited ||
374 sample.sample_max_bandwidth > max_bandwidth_.GetBest()) {
375 max_bandwidth_.Update(sample.sample_max_bandwidth, round_trip_count_);
376 }
377 }
378
379 if (!sample.sample_rtt.IsInfinite()) {
380 min_rtt_expired = MaybeUpdateMinRtt(event_time, sample.sample_rtt);
381 }
382 bytes_lost = sampler_.total_bytes_lost() - total_bytes_lost_before;
383 if (mode_ == STARTUP) {
384 if (stats_) {
385 stats_->slowstart_packets_lost += lost_packets.size();
386 stats_->slowstart_bytes_lost += bytes_lost;
387 }
388 }
389 excess_acked = sample.extra_acked;
390 last_packet_send_state = sample.last_packet_send_state;
391
392 if (!lost_packets.empty()) {
393 ++num_loss_events_in_round_;
394 bytes_lost_in_round_ += bytes_lost;
395 }
396
397 // Handle logic specific to PROBE_BW mode.
398 if (mode_ == PROBE_BW) {
399 UpdateGainCyclePhase(event_time, prior_in_flight, !lost_packets.empty());
400 }
401
402 // Handle logic specific to STARTUP and DRAIN modes.
403 if (is_round_start && !is_at_full_bandwidth_) {
404 CheckIfFullBandwidthReached(last_packet_send_state);
405 }
406 MaybeExitStartupOrDrain(event_time);
407
408 // Handle logic specific to PROBE_RTT.
409 MaybeEnterOrExitProbeRtt(event_time, is_round_start, min_rtt_expired);
410
411 // Calculate number of packets acked and lost.
412 QuicByteCount bytes_acked =
413 sampler_.total_bytes_acked() - total_bytes_acked_before;
414
415 // After the model is updated, recalculate the pacing rate and congestion
416 // window.
417 CalculatePacingRate(bytes_lost);
418 CalculateCongestionWindow(bytes_acked, excess_acked);
419 CalculateRecoveryWindow(bytes_acked, bytes_lost);
420
421 // Cleanup internal state.
422 sampler_.RemoveObsoletePackets(unacked_packets_->GetLeastUnacked());
423 if (is_round_start) {
424 num_loss_events_in_round_ = 0;
425 bytes_lost_in_round_ = 0;
426 }
427 }
428
GetCongestionControlType() const429 CongestionControlType BbrSender::GetCongestionControlType() const {
430 return kBBR;
431 }
432
GetMinRtt() const433 QuicTime::Delta BbrSender::GetMinRtt() const {
434 if (!min_rtt_.IsZero()) {
435 return min_rtt_;
436 }
437 // min_rtt could be available if the handshake packet gets neutered then
438 // gets acknowledged. This could only happen for QUIC crypto where we do not
439 // drop keys.
440 return rtt_stats_->MinOrInitialRtt();
441 }
442
GetTargetCongestionWindow(float gain) const443 QuicByteCount BbrSender::GetTargetCongestionWindow(float gain) const {
444 QuicByteCount bdp = GetMinRtt() * BandwidthEstimate();
445 QuicByteCount congestion_window = gain * bdp;
446
447 // BDP estimate will be zero if no bandwidth samples are available yet.
448 if (congestion_window == 0) {
449 congestion_window = gain * initial_congestion_window_;
450 }
451
452 return std::max(congestion_window, min_congestion_window_);
453 }
454
ProbeRttCongestionWindow() const455 QuicByteCount BbrSender::ProbeRttCongestionWindow() const {
456 return min_congestion_window_;
457 }
458
EnterStartupMode(QuicTime now)459 void BbrSender::EnterStartupMode(QuicTime now) {
460 if (stats_) {
461 ++stats_->slowstart_count;
462 stats_->slowstart_duration.Start(now);
463 }
464 mode_ = STARTUP;
465 pacing_gain_ = high_gain_;
466 congestion_window_gain_ = high_cwnd_gain_;
467 }
468
EnterProbeBandwidthMode(QuicTime now)469 void BbrSender::EnterProbeBandwidthMode(QuicTime now) {
470 mode_ = PROBE_BW;
471 congestion_window_gain_ = congestion_window_gain_constant_;
472
473 // Pick a random offset for the gain cycle out of {0, 2..7} range. 1 is
474 // excluded because in that case increased gain and decreased gain would not
475 // follow each other.
476 cycle_current_offset_ = random_->RandUint64() % (kGainCycleLength - 1);
477 if (cycle_current_offset_ >= 1) {
478 cycle_current_offset_ += 1;
479 }
480
481 last_cycle_start_ = now;
482 pacing_gain_ = kPacingGain[cycle_current_offset_];
483 }
484
UpdateRoundTripCounter(QuicPacketNumber last_acked_packet)485 bool BbrSender::UpdateRoundTripCounter(QuicPacketNumber last_acked_packet) {
486 if (!current_round_trip_end_.IsInitialized() ||
487 last_acked_packet > current_round_trip_end_) {
488 round_trip_count_++;
489 current_round_trip_end_ = last_sent_packet_;
490 if (stats_ && InSlowStart()) {
491 ++stats_->slowstart_num_rtts;
492 }
493 return true;
494 }
495
496 return false;
497 }
498
MaybeUpdateMinRtt(QuicTime now,QuicTime::Delta sample_min_rtt)499 bool BbrSender::MaybeUpdateMinRtt(QuicTime now,
500 QuicTime::Delta sample_min_rtt) {
501 // Do not expire min_rtt if none was ever available.
502 bool min_rtt_expired =
503 !min_rtt_.IsZero() && (now > (min_rtt_timestamp_ + kMinRttExpiry));
504
505 if (min_rtt_expired || sample_min_rtt < min_rtt_ || min_rtt_.IsZero()) {
506 QUIC_DVLOG(2) << "Min RTT updated, old value: " << min_rtt_
507 << ", new value: " << sample_min_rtt
508 << ", current time: " << now.ToDebuggingValue();
509
510 min_rtt_ = sample_min_rtt;
511 min_rtt_timestamp_ = now;
512 }
513 QUICHE_DCHECK(!min_rtt_.IsZero());
514
515 return min_rtt_expired;
516 }
517
UpdateGainCyclePhase(QuicTime now,QuicByteCount prior_in_flight,bool has_losses)518 void BbrSender::UpdateGainCyclePhase(QuicTime now,
519 QuicByteCount prior_in_flight,
520 bool has_losses) {
521 const QuicByteCount bytes_in_flight = unacked_packets_->bytes_in_flight();
522 // In most cases, the cycle is advanced after an RTT passes.
523 bool should_advance_gain_cycling = now - last_cycle_start_ > GetMinRtt();
524
525 // If the pacing gain is above 1.0, the connection is trying to probe the
526 // bandwidth by increasing the number of bytes in flight to at least
527 // pacing_gain * BDP. Make sure that it actually reaches the target, as long
528 // as there are no losses suggesting that the buffers are not able to hold
529 // that much.
530 if (pacing_gain_ > 1.0 && !has_losses &&
531 prior_in_flight < GetTargetCongestionWindow(pacing_gain_)) {
532 should_advance_gain_cycling = false;
533 }
534
535 // If pacing gain is below 1.0, the connection is trying to drain the extra
536 // queue which could have been incurred by probing prior to it. If the number
537 // of bytes in flight falls down to the estimated BDP value earlier, conclude
538 // that the queue has been successfully drained and exit this cycle early.
539 if (pacing_gain_ < 1.0 && bytes_in_flight <= GetTargetCongestionWindow(1)) {
540 should_advance_gain_cycling = true;
541 }
542
543 if (should_advance_gain_cycling) {
544 cycle_current_offset_ = (cycle_current_offset_ + 1) % kGainCycleLength;
545 if (cycle_current_offset_ == 0) {
546 ++stats_->bbr_num_cycles;
547 }
548 last_cycle_start_ = now;
549 // Stay in low gain mode until the target BDP is hit.
550 // Low gain mode will be exited immediately when the target BDP is achieved.
551 if (drain_to_target_ && pacing_gain_ < 1 &&
552 kPacingGain[cycle_current_offset_] == 1 &&
553 bytes_in_flight > GetTargetCongestionWindow(1)) {
554 return;
555 }
556 pacing_gain_ = kPacingGain[cycle_current_offset_];
557 }
558 }
559
CheckIfFullBandwidthReached(const SendTimeState & last_packet_send_state)560 void BbrSender::CheckIfFullBandwidthReached(
561 const SendTimeState& last_packet_send_state) {
562 if (last_sample_is_app_limited_) {
563 return;
564 }
565
566 QuicBandwidth target = bandwidth_at_last_round_ * kStartupGrowthTarget;
567 if (BandwidthEstimate() >= target) {
568 bandwidth_at_last_round_ = BandwidthEstimate();
569 rounds_without_bandwidth_gain_ = 0;
570 if (expire_ack_aggregation_in_startup_) {
571 // Expire old excess delivery measurements now that bandwidth increased.
572 sampler_.ResetMaxAckHeightTracker(0, round_trip_count_);
573 }
574 return;
575 }
576
577 rounds_without_bandwidth_gain_++;
578 if ((rounds_without_bandwidth_gain_ >= num_startup_rtts_) ||
579 ShouldExitStartupDueToLoss(last_packet_send_state)) {
580 QUICHE_DCHECK(has_non_app_limited_sample_);
581 is_at_full_bandwidth_ = true;
582 }
583 }
584
MaybeExitStartupOrDrain(QuicTime now)585 void BbrSender::MaybeExitStartupOrDrain(QuicTime now) {
586 if (mode_ == STARTUP && is_at_full_bandwidth_) {
587 OnExitStartup(now);
588 mode_ = DRAIN;
589 pacing_gain_ = drain_gain_;
590 congestion_window_gain_ = high_cwnd_gain_;
591 }
592 if (mode_ == DRAIN &&
593 unacked_packets_->bytes_in_flight() <= GetTargetCongestionWindow(1)) {
594 EnterProbeBandwidthMode(now);
595 }
596 }
597
OnExitStartup(QuicTime now)598 void BbrSender::OnExitStartup(QuicTime now) {
599 QUICHE_DCHECK_EQ(mode_, STARTUP);
600 if (stats_) {
601 stats_->slowstart_duration.Stop(now);
602 }
603 }
604
ShouldExitStartupDueToLoss(const SendTimeState & last_packet_send_state) const605 bool BbrSender::ShouldExitStartupDueToLoss(
606 const SendTimeState& last_packet_send_state) const {
607 if (num_loss_events_in_round_ <
608 GetQuicFlag(quic_bbr2_default_startup_full_loss_count) ||
609 !last_packet_send_state.is_valid) {
610 return false;
611 }
612
613 const QuicByteCount inflight_at_send = last_packet_send_state.bytes_in_flight;
614
615 if (inflight_at_send > 0 && bytes_lost_in_round_ > 0) {
616 if (bytes_lost_in_round_ >
617 inflight_at_send * GetQuicFlag(quic_bbr2_default_loss_threshold)) {
618 stats_->bbr_exit_startup_due_to_loss = true;
619 return true;
620 }
621 return false;
622 }
623
624 return false;
625 }
626
MaybeEnterOrExitProbeRtt(QuicTime now,bool is_round_start,bool min_rtt_expired)627 void BbrSender::MaybeEnterOrExitProbeRtt(QuicTime now, bool is_round_start,
628 bool min_rtt_expired) {
629 if (min_rtt_expired && !exiting_quiescence_ && mode_ != PROBE_RTT) {
630 if (InSlowStart()) {
631 OnExitStartup(now);
632 }
633 mode_ = PROBE_RTT;
634 pacing_gain_ = 1;
635 // Do not decide on the time to exit PROBE_RTT until the |bytes_in_flight|
636 // is at the target small value.
637 exit_probe_rtt_at_ = QuicTime::Zero();
638 }
639
640 if (mode_ == PROBE_RTT) {
641 sampler_.OnAppLimited();
642
643 if (exit_probe_rtt_at_ == QuicTime::Zero()) {
644 // If the window has reached the appropriate size, schedule exiting
645 // PROBE_RTT. The CWND during PROBE_RTT is kMinimumCongestionWindow, but
646 // we allow an extra packet since QUIC checks CWND before sending a
647 // packet.
648 if (unacked_packets_->bytes_in_flight() <
649 ProbeRttCongestionWindow() + kMaxOutgoingPacketSize) {
650 exit_probe_rtt_at_ = now + kProbeRttTime;
651 probe_rtt_round_passed_ = false;
652 }
653 } else {
654 if (is_round_start) {
655 probe_rtt_round_passed_ = true;
656 }
657 if (now >= exit_probe_rtt_at_ && probe_rtt_round_passed_) {
658 min_rtt_timestamp_ = now;
659 if (!is_at_full_bandwidth_) {
660 EnterStartupMode(now);
661 } else {
662 EnterProbeBandwidthMode(now);
663 }
664 }
665 }
666 }
667
668 exiting_quiescence_ = false;
669 }
670
UpdateRecoveryState(QuicPacketNumber last_acked_packet,bool has_losses,bool is_round_start)671 void BbrSender::UpdateRecoveryState(QuicPacketNumber last_acked_packet,
672 bool has_losses, bool is_round_start) {
673 // Disable recovery in startup, if loss-based exit is enabled.
674 if (!is_at_full_bandwidth_) {
675 return;
676 }
677
678 // Exit recovery when there are no losses for a round.
679 if (has_losses) {
680 end_recovery_at_ = last_sent_packet_;
681 }
682
683 switch (recovery_state_) {
684 case NOT_IN_RECOVERY:
685 // Enter conservation on the first loss.
686 if (has_losses) {
687 recovery_state_ = CONSERVATION;
688 // This will cause the |recovery_window_| to be set to the correct
689 // value in CalculateRecoveryWindow().
690 recovery_window_ = 0;
691 // Since the conservation phase is meant to be lasting for a whole
692 // round, extend the current round as if it were started right now.
693 current_round_trip_end_ = last_sent_packet_;
694 }
695 break;
696
697 case CONSERVATION:
698 if (is_round_start) {
699 recovery_state_ = GROWTH;
700 }
701 ABSL_FALLTHROUGH_INTENDED;
702
703 case GROWTH:
704 // Exit recovery if appropriate.
705 if (!has_losses && last_acked_packet > end_recovery_at_) {
706 recovery_state_ = NOT_IN_RECOVERY;
707 }
708
709 break;
710 }
711 }
712
CalculatePacingRate(QuicByteCount bytes_lost)713 void BbrSender::CalculatePacingRate(QuicByteCount bytes_lost) {
714 if (BandwidthEstimate().IsZero()) {
715 return;
716 }
717
718 QuicBandwidth target_rate = pacing_gain_ * BandwidthEstimate();
719 if (is_at_full_bandwidth_) {
720 pacing_rate_ = target_rate;
721 return;
722 }
723
724 // Pace at the rate of initial_window / RTT as soon as RTT measurements are
725 // available.
726 if (pacing_rate_.IsZero() && !rtt_stats_->min_rtt().IsZero()) {
727 pacing_rate_ = QuicBandwidth::FromBytesAndTimeDelta(
728 initial_congestion_window_, rtt_stats_->min_rtt());
729 return;
730 }
731
732 if (detect_overshooting_) {
733 bytes_lost_while_detecting_overshooting_ += bytes_lost;
734 // Check for overshooting with network parameters adjusted when pacing rate
735 // > target_rate and loss has been detected.
736 if (pacing_rate_ > target_rate &&
737 bytes_lost_while_detecting_overshooting_ > 0) {
738 if (has_non_app_limited_sample_ ||
739 bytes_lost_while_detecting_overshooting_ *
740 bytes_lost_multiplier_while_detecting_overshooting_ >
741 initial_congestion_window_) {
742 // We are fairly sure overshoot happens if 1) there is at least one
743 // non app-limited bw sample or 2) half of IW gets lost. Slow pacing
744 // rate.
745 pacing_rate_ = std::max(
746 target_rate, QuicBandwidth::FromBytesAndTimeDelta(
747 cwnd_to_calculate_min_pacing_rate_, GetMinRtt()));
748 if (stats_) {
749 stats_->overshooting_detected_with_network_parameters_adjusted = true;
750 }
751 bytes_lost_while_detecting_overshooting_ = 0;
752 detect_overshooting_ = false;
753 }
754 }
755 }
756
757 // Do not decrease the pacing rate during startup.
758 pacing_rate_ = std::max(pacing_rate_, target_rate);
759 }
760
CalculateCongestionWindow(QuicByteCount bytes_acked,QuicByteCount excess_acked)761 void BbrSender::CalculateCongestionWindow(QuicByteCount bytes_acked,
762 QuicByteCount excess_acked) {
763 if (mode_ == PROBE_RTT) {
764 return;
765 }
766
767 QuicByteCount target_window =
768 GetTargetCongestionWindow(congestion_window_gain_);
769 if (is_at_full_bandwidth_) {
770 // Add the max recently measured ack aggregation to CWND.
771 target_window += sampler_.max_ack_height();
772 } else if (enable_ack_aggregation_during_startup_) {
773 // Add the most recent excess acked. Because CWND never decreases in
774 // STARTUP, this will automatically create a very localized max filter.
775 target_window += excess_acked;
776 }
777
778 // Instead of immediately setting the target CWND as the new one, BBR grows
779 // the CWND towards |target_window| by only increasing it |bytes_acked| at a
780 // time.
781 if (is_at_full_bandwidth_) {
782 congestion_window_ =
783 std::min(target_window, congestion_window_ + bytes_acked);
784 } else if (congestion_window_ < target_window ||
785 sampler_.total_bytes_acked() < initial_congestion_window_) {
786 // If the connection is not yet out of startup phase, do not decrease the
787 // window.
788 congestion_window_ = congestion_window_ + bytes_acked;
789 }
790
791 // Enforce the limits on the congestion window.
792 congestion_window_ = std::max(congestion_window_, min_congestion_window_);
793 congestion_window_ = std::min(congestion_window_, max_congestion_window_);
794 }
795
CalculateRecoveryWindow(QuicByteCount bytes_acked,QuicByteCount bytes_lost)796 void BbrSender::CalculateRecoveryWindow(QuicByteCount bytes_acked,
797 QuicByteCount bytes_lost) {
798 if (recovery_state_ == NOT_IN_RECOVERY) {
799 return;
800 }
801
802 // Set up the initial recovery window.
803 if (recovery_window_ == 0) {
804 recovery_window_ = unacked_packets_->bytes_in_flight() + bytes_acked;
805 recovery_window_ = std::max(min_congestion_window_, recovery_window_);
806 return;
807 }
808
809 // Remove losses from the recovery window, while accounting for a potential
810 // integer underflow.
811 recovery_window_ = recovery_window_ >= bytes_lost
812 ? recovery_window_ - bytes_lost
813 : kMaxSegmentSize;
814
815 // In CONSERVATION mode, just subtracting losses is sufficient. In GROWTH,
816 // release additional |bytes_acked| to achieve a slow-start-like behavior.
817 if (recovery_state_ == GROWTH) {
818 recovery_window_ += bytes_acked;
819 }
820
821 // Always allow sending at least |bytes_acked| in response.
822 recovery_window_ = std::max(
823 recovery_window_, unacked_packets_->bytes_in_flight() + bytes_acked);
824 recovery_window_ = std::max(min_congestion_window_, recovery_window_);
825 }
826
GetDebugState() const827 std::string BbrSender::GetDebugState() const {
828 std::ostringstream stream;
829 stream << ExportDebugState();
830 return stream.str();
831 }
832
OnApplicationLimited(QuicByteCount bytes_in_flight)833 void BbrSender::OnApplicationLimited(QuicByteCount bytes_in_flight) {
834 if (bytes_in_flight >= GetCongestionWindow()) {
835 return;
836 }
837
838 sampler_.OnAppLimited();
839 QUIC_DVLOG(2) << "Becoming application limited. Last sent packet: "
840 << last_sent_packet_ << ", CWND: " << GetCongestionWindow();
841 }
842
PopulateConnectionStats(QuicConnectionStats * stats) const843 void BbrSender::PopulateConnectionStats(QuicConnectionStats* stats) const {
844 stats->num_ack_aggregation_epochs = sampler_.num_ack_aggregation_epochs();
845 }
846
ExportDebugState() const847 BbrSender::DebugState BbrSender::ExportDebugState() const {
848 return DebugState(*this);
849 }
850
ModeToString(BbrSender::Mode mode)851 static std::string ModeToString(BbrSender::Mode mode) {
852 switch (mode) {
853 case BbrSender::STARTUP:
854 return "STARTUP";
855 case BbrSender::DRAIN:
856 return "DRAIN";
857 case BbrSender::PROBE_BW:
858 return "PROBE_BW";
859 case BbrSender::PROBE_RTT:
860 return "PROBE_RTT";
861 }
862 return "???";
863 }
864
operator <<(std::ostream & os,const BbrSender::Mode & mode)865 std::ostream& operator<<(std::ostream& os, const BbrSender::Mode& mode) {
866 os << ModeToString(mode);
867 return os;
868 }
869
operator <<(std::ostream & os,const BbrSender::DebugState & state)870 std::ostream& operator<<(std::ostream& os, const BbrSender::DebugState& state) {
871 os << "Mode: " << ModeToString(state.mode) << std::endl;
872 os << "Maximum bandwidth: " << state.max_bandwidth << std::endl;
873 os << "Round trip counter: " << state.round_trip_count << std::endl;
874 os << "Gain cycle index: " << static_cast<int>(state.gain_cycle_index)
875 << std::endl;
876 os << "Congestion window: " << state.congestion_window << " bytes"
877 << std::endl;
878
879 if (state.mode == BbrSender::STARTUP) {
880 os << "(startup) Bandwidth at last round: " << state.bandwidth_at_last_round
881 << std::endl;
882 os << "(startup) Rounds without gain: "
883 << state.rounds_without_bandwidth_gain << std::endl;
884 }
885
886 os << "Minimum RTT: " << state.min_rtt << std::endl;
887 os << "Minimum RTT timestamp: " << state.min_rtt_timestamp.ToDebuggingValue()
888 << std::endl;
889
890 os << "Last sample is app-limited: "
891 << (state.last_sample_is_app_limited ? "yes" : "no");
892
893 return os;
894 }
895
896 } // namespace quic
897