xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/core/congestion_control/bbr_sender.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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