blob: e59aa8d69a23a62a1fd6988455792f01e20e92f9 [file] [log] [blame]
QUICHE teama6ef0a62019-03-07 20:34:33 -05001// 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 "net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.h"
6
7#include <algorithm>
8#include <sstream>
vasilvv872e7a32019-03-12 16:42:44 -07009#include <string>
QUICHE teama6ef0a62019-03-07 20:34:33 -050010
11#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h"
12#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
wub967ba572019-04-01 09:27:52 -070013#include "net/third_party/quiche/src/quic/core/quic_time.h"
QUICHE teama6ef0a62019-03-07 20:34:33 -050014#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
15#include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h"
16#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
17#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
18#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
QUICHE teama6ef0a62019-03-07 20:34:33 -050019
20namespace quic {
21
22namespace {
23// Constants based on TCP defaults.
24// The minimum CWND to ensure delayed acks don't reduce bandwidth measurements.
25// Does not inflate the pacing rate.
26const QuicByteCount kDefaultMinimumCongestionWindow = 4 * kMaxSegmentSize;
27
28// The gain used for the STARTUP, equal to 2/ln(2).
29const float kDefaultHighGain = 2.885f;
30// The newly derived gain for STARTUP, equal to 4 * ln(2)
31const float kDerivedHighGain = 2.773f;
32// The newly derived CWND gain for STARTUP, 2.
33const float kDerivedHighCWNDGain = 2.773f;
34// The gain used in STARTUP after loss has been detected.
35// 1.5 is enough to allow for 25% exogenous loss and still observe a 25% growth
36// in measured bandwidth.
37const float kStartupAfterLossGain = 1.5f;
38// The cycle of gains used during the PROBE_BW stage.
39const float kPacingGain[] = {1.25, 0.75, 1, 1, 1, 1, 1, 1};
40
41// The length of the gain cycle.
42const size_t kGainCycleLength = sizeof(kPacingGain) / sizeof(kPacingGain[0]);
43// The size of the bandwidth filter window, in round-trips.
44const QuicRoundTripCount kBandwidthWindowSize = kGainCycleLength + 2;
45
46// The time after which the current min_rtt value expires.
47const QuicTime::Delta kMinRttExpiry = QuicTime::Delta::FromSeconds(10);
48// The minimum time the connection can spend in PROBE_RTT mode.
49const QuicTime::Delta kProbeRttTime = QuicTime::Delta::FromMilliseconds(200);
50// If the bandwidth does not increase by the factor of |kStartupGrowthTarget|
51// within |kRoundTripsWithoutGrowthBeforeExitingStartup| rounds, the connection
52// will exit the STARTUP mode.
53const float kStartupGrowthTarget = 1.25;
54const QuicRoundTripCount kRoundTripsWithoutGrowthBeforeExitingStartup = 3;
55// Coefficient of target congestion window to use when basing PROBE_RTT on BDP.
56const float kModerateProbeRttMultiplier = 0.75;
57// Coefficient to determine if a new RTT is sufficiently similar to min_rtt that
58// we don't need to enter PROBE_RTT.
59const float kSimilarMinRttThreshold = 1.125;
60
61} // namespace
62
63BbrSender::DebugState::DebugState(const BbrSender& sender)
64 : mode(sender.mode_),
65 max_bandwidth(sender.max_bandwidth_.GetBest()),
66 round_trip_count(sender.round_trip_count_),
67 gain_cycle_index(sender.cycle_current_offset_),
68 congestion_window(sender.congestion_window_),
69 is_at_full_bandwidth(sender.is_at_full_bandwidth_),
70 bandwidth_at_last_round(sender.bandwidth_at_last_round_),
71 rounds_without_bandwidth_gain(sender.rounds_without_bandwidth_gain_),
72 min_rtt(sender.min_rtt_),
73 min_rtt_timestamp(sender.min_rtt_timestamp_),
74 recovery_state(sender.recovery_state_),
75 recovery_window(sender.recovery_window_),
76 last_sample_is_app_limited(sender.last_sample_is_app_limited_),
77 end_of_app_limited_phase(sender.sampler_.end_of_app_limited_phase()) {}
78
79BbrSender::DebugState::DebugState(const DebugState& state) = default;
80
wub967ba572019-04-01 09:27:52 -070081BbrSender::BbrSender(QuicTime now,
82 const RttStats* rtt_stats,
QUICHE teama6ef0a62019-03-07 20:34:33 -050083 const QuicUnackedPacketMap* unacked_packets,
84 QuicPacketCount initial_tcp_congestion_window,
85 QuicPacketCount max_tcp_congestion_window,
wub967ba572019-04-01 09:27:52 -070086 QuicRandom* random,
87 QuicConnectionStats* stats)
QUICHE teama6ef0a62019-03-07 20:34:33 -050088 : rtt_stats_(rtt_stats),
89 unacked_packets_(unacked_packets),
90 random_(random),
wub967ba572019-04-01 09:27:52 -070091 stats_(stats),
QUICHE teama6ef0a62019-03-07 20:34:33 -050092 mode_(STARTUP),
93 round_trip_count_(0),
94 max_bandwidth_(kBandwidthWindowSize, QuicBandwidth::Zero(), 0),
95 max_ack_height_(kBandwidthWindowSize, 0, 0),
96 aggregation_epoch_start_time_(QuicTime::Zero()),
97 aggregation_epoch_bytes_(0),
98 min_rtt_(QuicTime::Delta::Zero()),
99 min_rtt_timestamp_(QuicTime::Zero()),
100 congestion_window_(initial_tcp_congestion_window * kDefaultTCPMSS),
101 initial_congestion_window_(initial_tcp_congestion_window *
102 kDefaultTCPMSS),
103 max_congestion_window_(max_tcp_congestion_window * kDefaultTCPMSS),
104 min_congestion_window_(kDefaultMinimumCongestionWindow),
105 high_gain_(kDefaultHighGain),
106 high_cwnd_gain_(kDefaultHighGain),
107 drain_gain_(1.f / kDefaultHighGain),
108 pacing_rate_(QuicBandwidth::Zero()),
109 pacing_gain_(1),
110 congestion_window_gain_(1),
111 congestion_window_gain_constant_(
112 static_cast<float>(FLAGS_quic_bbr_cwnd_gain)),
113 num_startup_rtts_(kRoundTripsWithoutGrowthBeforeExitingStartup),
114 exit_startup_on_loss_(false),
115 cycle_current_offset_(0),
116 last_cycle_start_(QuicTime::Zero()),
117 is_at_full_bandwidth_(false),
118 rounds_without_bandwidth_gain_(0),
119 bandwidth_at_last_round_(QuicBandwidth::Zero()),
120 exiting_quiescence_(false),
121 exit_probe_rtt_at_(QuicTime::Zero()),
122 probe_rtt_round_passed_(false),
123 last_sample_is_app_limited_(false),
124 has_non_app_limited_sample_(false),
125 flexible_app_limited_(false),
126 recovery_state_(NOT_IN_RECOVERY),
127 recovery_window_(max_congestion_window_),
128 is_app_limited_recovery_(false),
129 slower_startup_(false),
130 rate_based_startup_(false),
131 startup_rate_reduction_multiplier_(0),
132 startup_bytes_lost_(0),
133 enable_ack_aggregation_during_startup_(false),
134 expire_ack_aggregation_in_startup_(false),
135 drain_to_target_(false),
136 probe_rtt_based_on_bdp_(false),
137 probe_rtt_skipped_if_similar_rtt_(false),
138 probe_rtt_disabled_if_app_limited_(false),
139 app_limited_since_last_probe_rtt_(false),
140 min_rtt_since_last_probe_rtt_(QuicTime::Delta::Infinite()) {
wub967ba572019-04-01 09:27:52 -0700141 if (stats_) {
142 stats_->slowstart_count = 0;
143 stats_->slowstart_start_time = QuicTime::Zero();
144 }
145 EnterStartupMode(now);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500146}
147
148BbrSender::~BbrSender() {}
149
150void BbrSender::SetInitialCongestionWindowInPackets(
151 QuicPacketCount congestion_window) {
152 if (mode_ == STARTUP) {
153 initial_congestion_window_ = congestion_window * kDefaultTCPMSS;
154 congestion_window_ = congestion_window * kDefaultTCPMSS;
155 }
156}
157
158bool BbrSender::InSlowStart() const {
159 return mode_ == STARTUP;
160}
161
162void BbrSender::OnPacketSent(QuicTime sent_time,
163 QuicByteCount bytes_in_flight,
164 QuicPacketNumber packet_number,
165 QuicByteCount bytes,
166 HasRetransmittableData is_retransmittable) {
wub967ba572019-04-01 09:27:52 -0700167 if (stats_ && InSlowStart()) {
168 ++stats_->slowstart_packets_sent;
169 stats_->slowstart_bytes_sent += bytes;
170 }
171
QUICHE teama6ef0a62019-03-07 20:34:33 -0500172 last_sent_packet_ = packet_number;
173
174 if (bytes_in_flight == 0 && sampler_.is_app_limited()) {
175 exiting_quiescence_ = true;
176 }
177
178 if (!aggregation_epoch_start_time_.IsInitialized()) {
179 aggregation_epoch_start_time_ = sent_time;
180 }
181
182 sampler_.OnPacketSent(sent_time, packet_number, bytes, bytes_in_flight,
183 is_retransmittable);
184}
185
186bool BbrSender::CanSend(QuicByteCount bytes_in_flight) {
187 return bytes_in_flight < GetCongestionWindow();
188}
189
190QuicBandwidth BbrSender::PacingRate(QuicByteCount bytes_in_flight) const {
191 if (pacing_rate_.IsZero()) {
192 return high_gain_ * QuicBandwidth::FromBytesAndTimeDelta(
193 initial_congestion_window_, GetMinRtt());
194 }
195 return pacing_rate_;
196}
197
198QuicBandwidth BbrSender::BandwidthEstimate() const {
199 return max_bandwidth_.GetBest();
200}
201
202QuicByteCount BbrSender::GetCongestionWindow() const {
203 if (mode_ == PROBE_RTT) {
204 return ProbeRttCongestionWindow();
205 }
206
207 if (InRecovery() && !(rate_based_startup_ && mode_ == STARTUP)) {
208 return std::min(congestion_window_, recovery_window_);
209 }
210
211 return congestion_window_;
212}
213
214QuicByteCount BbrSender::GetSlowStartThreshold() const {
215 return 0;
216}
217
218bool BbrSender::InRecovery() const {
219 return recovery_state_ != NOT_IN_RECOVERY;
220}
221
222bool BbrSender::ShouldSendProbingPacket() const {
223 if (pacing_gain_ <= 1) {
224 return false;
225 }
226
227 // TODO(b/77975811): If the pipe is highly under-utilized, consider not
228 // sending a probing transmission, because the extra bandwidth is not needed.
229 // If flexible_app_limited is enabled, check if the pipe is sufficiently full.
230 if (flexible_app_limited_) {
231 return !IsPipeSufficientlyFull();
232 } else {
233 return true;
234 }
235}
236
237bool BbrSender::IsPipeSufficientlyFull() const {
238 // See if we need more bytes in flight to see more bandwidth.
239 if (mode_ == STARTUP) {
240 // STARTUP exits if it doesn't observe a 25% bandwidth increase, so the CWND
241 // must be more than 25% above the target.
242 return unacked_packets_->bytes_in_flight() >=
243 GetTargetCongestionWindow(1.5);
244 }
245 if (pacing_gain_ > 1) {
246 // Super-unity PROBE_BW doesn't exit until 1.25 * BDP is achieved.
247 return unacked_packets_->bytes_in_flight() >=
248 GetTargetCongestionWindow(pacing_gain_);
249 }
250 // If bytes_in_flight are above the target congestion window, it should be
251 // possible to observe the same or more bandwidth if it's available.
252 return unacked_packets_->bytes_in_flight() >= GetTargetCongestionWindow(1.1);
253}
254
255void BbrSender::SetFromConfig(const QuicConfig& config,
256 Perspective perspective) {
257 if (config.HasClientRequestedIndependentOption(kLRTT, perspective)) {
258 exit_startup_on_loss_ = true;
259 }
260 if (config.HasClientRequestedIndependentOption(k1RTT, perspective)) {
261 num_startup_rtts_ = 1;
262 }
263 if (config.HasClientRequestedIndependentOption(k2RTT, perspective)) {
264 num_startup_rtts_ = 2;
265 }
266 if (config.HasClientRequestedIndependentOption(kBBRS, perspective)) {
267 slower_startup_ = true;
268 }
269 if (config.HasClientRequestedIndependentOption(kBBR3, perspective)) {
270 drain_to_target_ = true;
271 }
272 if (config.HasClientRequestedIndependentOption(kBBS1, perspective)) {
273 rate_based_startup_ = true;
274 }
275 if (GetQuicReloadableFlag(quic_bbr_startup_rate_reduction) &&
276 config.HasClientRequestedIndependentOption(kBBS4, perspective)) {
277 rate_based_startup_ = true;
278 // Hits 1.25x pacing multiplier when ~2/3 CWND is lost.
279 startup_rate_reduction_multiplier_ = 1;
280 }
281 if (GetQuicReloadableFlag(quic_bbr_startup_rate_reduction) &&
282 config.HasClientRequestedIndependentOption(kBBS5, perspective)) {
283 rate_based_startup_ = true;
284 // Hits 1.25x pacing multiplier when ~1/3 CWND is lost.
285 startup_rate_reduction_multiplier_ = 2;
286 }
287 if (config.HasClientRequestedIndependentOption(kBBR4, perspective)) {
288 max_ack_height_.SetWindowLength(2 * kBandwidthWindowSize);
289 }
290 if (config.HasClientRequestedIndependentOption(kBBR5, perspective)) {
291 max_ack_height_.SetWindowLength(4 * kBandwidthWindowSize);
292 }
293 if (GetQuicReloadableFlag(quic_bbr_less_probe_rtt) &&
294 config.HasClientRequestedIndependentOption(kBBR6, perspective)) {
295 QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_less_probe_rtt, 1, 3);
296 probe_rtt_based_on_bdp_ = true;
297 }
298 if (GetQuicReloadableFlag(quic_bbr_less_probe_rtt) &&
299 config.HasClientRequestedIndependentOption(kBBR7, perspective)) {
300 QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_less_probe_rtt, 2, 3);
301 probe_rtt_skipped_if_similar_rtt_ = true;
302 }
303 if (GetQuicReloadableFlag(quic_bbr_less_probe_rtt) &&
304 config.HasClientRequestedIndependentOption(kBBR8, perspective)) {
305 QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_less_probe_rtt, 3, 3);
306 probe_rtt_disabled_if_app_limited_ = true;
307 }
308 if (GetQuicReloadableFlag(quic_bbr_flexible_app_limited) &&
309 config.HasClientRequestedIndependentOption(kBBR9, perspective)) {
310 QUIC_RELOADABLE_FLAG_COUNT(quic_bbr_flexible_app_limited);
311 flexible_app_limited_ = true;
312 }
313 if (GetQuicReloadableFlag(quic_bbr_slower_startup3) &&
314 config.HasClientRequestedIndependentOption(kBBQ1, perspective)) {
315 QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_slower_startup3, 1, 4);
316 set_high_gain(kDerivedHighGain);
317 set_high_cwnd_gain(kDerivedHighGain);
318 set_drain_gain(1.f / kDerivedHighGain);
319 }
320 if (GetQuicReloadableFlag(quic_bbr_slower_startup3) &&
321 config.HasClientRequestedIndependentOption(kBBQ2, perspective)) {
322 QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_slower_startup3, 2, 4);
323 set_high_cwnd_gain(kDerivedHighCWNDGain);
324 }
325 if (GetQuicReloadableFlag(quic_bbr_slower_startup3) &&
326 config.HasClientRequestedIndependentOption(kBBQ3, perspective)) {
327 QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_slower_startup3, 3, 4);
328 enable_ack_aggregation_during_startup_ = true;
329 }
330 if (GetQuicReloadableFlag(quic_bbr_slower_startup3) &&
331 config.HasClientRequestedIndependentOption(kBBQ4, perspective)) {
332 QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_slower_startup3, 4, 4);
333 set_drain_gain(kModerateProbeRttMultiplier);
334 }
335 if (GetQuicReloadableFlag(quic_bbr_slower_startup4) &&
336 config.HasClientRequestedIndependentOption(kBBQ5, perspective)) {
337 QUIC_RELOADABLE_FLAG_COUNT(quic_bbr_slower_startup4);
338 expire_ack_aggregation_in_startup_ = true;
339 }
340 if (config.HasClientRequestedIndependentOption(kMIN1, perspective)) {
341 min_congestion_window_ = kMaxSegmentSize;
342 }
343}
344
345void BbrSender::AdjustNetworkParameters(QuicBandwidth bandwidth,
346 QuicTime::Delta rtt) {
347 if (!bandwidth.IsZero()) {
348 max_bandwidth_.Update(bandwidth, round_trip_count_);
349 }
350 if (!rtt.IsZero() && (min_rtt_ > rtt || min_rtt_.IsZero())) {
351 min_rtt_ = rtt;
352 }
353}
354
355void BbrSender::OnCongestionEvent(bool /*rtt_updated*/,
356 QuicByteCount prior_in_flight,
357 QuicTime event_time,
358 const AckedPacketVector& acked_packets,
359 const LostPacketVector& lost_packets) {
360 const QuicByteCount total_bytes_acked_before = sampler_.total_bytes_acked();
361
362 bool is_round_start = false;
363 bool min_rtt_expired = false;
364
365 DiscardLostPackets(lost_packets);
366
367 // Input the new data into the BBR model of the connection.
368 QuicByteCount excess_acked = 0;
369 if (!acked_packets.empty()) {
370 QuicPacketNumber last_acked_packet = acked_packets.rbegin()->packet_number;
371 is_round_start = UpdateRoundTripCounter(last_acked_packet);
372 min_rtt_expired = UpdateBandwidthAndMinRtt(event_time, acked_packets);
373 UpdateRecoveryState(last_acked_packet, !lost_packets.empty(),
374 is_round_start);
375
376 const QuicByteCount bytes_acked =
377 sampler_.total_bytes_acked() - total_bytes_acked_before;
378
379 excess_acked = UpdateAckAggregationBytes(event_time, bytes_acked);
380 }
381
382 // Handle logic specific to PROBE_BW mode.
383 if (mode_ == PROBE_BW) {
384 UpdateGainCyclePhase(event_time, prior_in_flight, !lost_packets.empty());
385 }
386
387 // Handle logic specific to STARTUP and DRAIN modes.
388 if (is_round_start && !is_at_full_bandwidth_) {
389 CheckIfFullBandwidthReached();
390 }
391 MaybeExitStartupOrDrain(event_time);
392
393 // Handle logic specific to PROBE_RTT.
394 MaybeEnterOrExitProbeRtt(event_time, is_round_start, min_rtt_expired);
395
396 // Calculate number of packets acked and lost.
397 QuicByteCount bytes_acked =
398 sampler_.total_bytes_acked() - total_bytes_acked_before;
399 QuicByteCount bytes_lost = 0;
400 for (const auto& packet : lost_packets) {
401 bytes_lost += packet.bytes_lost;
402 }
403
404 // After the model is updated, recalculate the pacing rate and congestion
405 // window.
406 CalculatePacingRate();
407 CalculateCongestionWindow(bytes_acked, excess_acked);
408 CalculateRecoveryWindow(bytes_acked, bytes_lost);
409
410 // Cleanup internal state.
411 sampler_.RemoveObsoletePackets(unacked_packets_->GetLeastUnacked());
412}
413
414CongestionControlType BbrSender::GetCongestionControlType() const {
415 return kBBR;
416}
417
418QuicTime::Delta BbrSender::GetMinRtt() const {
419 return !min_rtt_.IsZero() ? min_rtt_ : rtt_stats_->initial_rtt();
420}
421
422QuicByteCount BbrSender::GetTargetCongestionWindow(float gain) const {
423 QuicByteCount bdp = GetMinRtt() * BandwidthEstimate();
424 QuicByteCount congestion_window = gain * bdp;
425
426 // BDP estimate will be zero if no bandwidth samples are available yet.
427 if (congestion_window == 0) {
428 congestion_window = gain * initial_congestion_window_;
429 }
430
431 return std::max(congestion_window, min_congestion_window_);
432}
433
434QuicByteCount BbrSender::ProbeRttCongestionWindow() const {
435 if (probe_rtt_based_on_bdp_) {
436 return GetTargetCongestionWindow(kModerateProbeRttMultiplier);
437 }
438 return min_congestion_window_;
439}
440
wub967ba572019-04-01 09:27:52 -0700441void BbrSender::EnterStartupMode(QuicTime now) {
442 if (stats_) {
443 ++stats_->slowstart_count;
444 DCHECK_EQ(stats_->slowstart_start_time, QuicTime::Zero()) << mode_;
445 stats_->slowstart_start_time = now;
446 }
QUICHE teama6ef0a62019-03-07 20:34:33 -0500447 mode_ = STARTUP;
448 pacing_gain_ = high_gain_;
449 congestion_window_gain_ = high_cwnd_gain_;
450}
451
452void BbrSender::EnterProbeBandwidthMode(QuicTime now) {
453 mode_ = PROBE_BW;
454 congestion_window_gain_ = congestion_window_gain_constant_;
455
456 // Pick a random offset for the gain cycle out of {0, 2..7} range. 1 is
457 // excluded because in that case increased gain and decreased gain would not
458 // follow each other.
459 cycle_current_offset_ = random_->RandUint64() % (kGainCycleLength - 1);
460 if (cycle_current_offset_ >= 1) {
461 cycle_current_offset_ += 1;
462 }
463
464 last_cycle_start_ = now;
465 pacing_gain_ = kPacingGain[cycle_current_offset_];
466}
467
468void BbrSender::DiscardLostPackets(const LostPacketVector& lost_packets) {
469 for (const LostPacket& packet : lost_packets) {
470 sampler_.OnPacketLost(packet.packet_number);
wub967ba572019-04-01 09:27:52 -0700471 if (mode_ == STARTUP) {
472 if (stats_) {
473 ++stats_->slowstart_packets_lost;
474 stats_->slowstart_bytes_lost += packet.bytes_lost;
475 }
476 if (startup_rate_reduction_multiplier_ != 0) {
477 startup_bytes_lost_ += packet.bytes_lost;
478 }
QUICHE teama6ef0a62019-03-07 20:34:33 -0500479 }
480 }
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_;
wub967ba572019-04-01 09:27:52 -0700488 if (stats_ && InSlowStart()) {
489 ++stats_->slowstart_num_rtts;
490 }
QUICHE teama6ef0a62019-03-07 20:34:33 -0500491 return true;
492 }
493
494 return false;
495}
496
497bool BbrSender::UpdateBandwidthAndMinRtt(
498 QuicTime now,
499 const AckedPacketVector& acked_packets) {
500 QuicTime::Delta sample_min_rtt = QuicTime::Delta::Infinite();
501 for (const auto& packet : acked_packets) {
502 if (packet.bytes_acked == 0) {
503 // Skip acked packets with 0 in flight bytes when updating bandwidth.
504 continue;
505 }
506 BandwidthSample bandwidth_sample =
507 sampler_.OnPacketAcknowledged(now, packet.packet_number);
508 last_sample_is_app_limited_ = bandwidth_sample.is_app_limited;
509 has_non_app_limited_sample_ |= !bandwidth_sample.is_app_limited;
510 if (!bandwidth_sample.rtt.IsZero()) {
511 sample_min_rtt = std::min(sample_min_rtt, bandwidth_sample.rtt);
512 }
513
514 if (!bandwidth_sample.is_app_limited ||
515 bandwidth_sample.bandwidth > BandwidthEstimate()) {
516 max_bandwidth_.Update(bandwidth_sample.bandwidth, round_trip_count_);
517 }
518 }
519
520 // If none of the RTT samples are valid, return immediately.
521 if (sample_min_rtt.IsInfinite()) {
522 return false;
523 }
524 min_rtt_since_last_probe_rtt_ =
525 std::min(min_rtt_since_last_probe_rtt_, sample_min_rtt);
526
527 // Do not expire min_rtt if none was ever available.
528 bool min_rtt_expired =
529 !min_rtt_.IsZero() && (now > (min_rtt_timestamp_ + kMinRttExpiry));
530
531 if (min_rtt_expired || sample_min_rtt < min_rtt_ || min_rtt_.IsZero()) {
532 QUIC_DVLOG(2) << "Min RTT updated, old value: " << min_rtt_
533 << ", new value: " << sample_min_rtt
534 << ", current time: " << now.ToDebuggingValue();
535
536 if (min_rtt_expired && ShouldExtendMinRttExpiry()) {
537 min_rtt_expired = false;
538 } else {
539 min_rtt_ = sample_min_rtt;
540 }
541 min_rtt_timestamp_ = now;
542 // Reset since_last_probe_rtt fields.
543 min_rtt_since_last_probe_rtt_ = QuicTime::Delta::Infinite();
544 app_limited_since_last_probe_rtt_ = false;
545 }
546 DCHECK(!min_rtt_.IsZero());
547
548 return min_rtt_expired;
549}
550
551bool BbrSender::ShouldExtendMinRttExpiry() const {
552 if (probe_rtt_disabled_if_app_limited_ && app_limited_since_last_probe_rtt_) {
553 // Extend the current min_rtt if we've been app limited recently.
554 return true;
555 }
556 const bool min_rtt_increased_since_last_probe =
557 min_rtt_since_last_probe_rtt_ > min_rtt_ * kSimilarMinRttThreshold;
558 if (probe_rtt_skipped_if_similar_rtt_ && app_limited_since_last_probe_rtt_ &&
559 !min_rtt_increased_since_last_probe) {
560 // Extend the current min_rtt if we've been app limited recently and an rtt
561 // has been measured in that time that's less than 12.5% more than the
562 // current min_rtt.
563 return true;
564 }
565 return false;
566}
567
568void BbrSender::UpdateGainCyclePhase(QuicTime now,
569 QuicByteCount prior_in_flight,
570 bool has_losses) {
571 const QuicByteCount bytes_in_flight = unacked_packets_->bytes_in_flight();
572 // In most cases, the cycle is advanced after an RTT passes.
573 bool should_advance_gain_cycling = now - last_cycle_start_ > GetMinRtt();
574
575 // If the pacing gain is above 1.0, the connection is trying to probe the
576 // bandwidth by increasing the number of bytes in flight to at least
577 // pacing_gain * BDP. Make sure that it actually reaches the target, as long
578 // as there are no losses suggesting that the buffers are not able to hold
579 // that much.
580 if (pacing_gain_ > 1.0 && !has_losses &&
581 prior_in_flight < GetTargetCongestionWindow(pacing_gain_)) {
582 should_advance_gain_cycling = false;
583 }
584
585 // If pacing gain is below 1.0, the connection is trying to drain the extra
586 // queue which could have been incurred by probing prior to it. If the number
587 // of bytes in flight falls down to the estimated BDP value earlier, conclude
588 // that the queue has been successfully drained and exit this cycle early.
589 if (pacing_gain_ < 1.0 && bytes_in_flight <= GetTargetCongestionWindow(1)) {
590 should_advance_gain_cycling = true;
591 }
592
593 if (should_advance_gain_cycling) {
594 cycle_current_offset_ = (cycle_current_offset_ + 1) % kGainCycleLength;
595 last_cycle_start_ = now;
596 // Stay in low gain mode until the target BDP is hit.
597 // Low gain mode will be exited immediately when the target BDP is achieved.
598 if (drain_to_target_ && pacing_gain_ < 1 &&
599 kPacingGain[cycle_current_offset_] == 1 &&
600 bytes_in_flight > GetTargetCongestionWindow(1)) {
601 return;
602 }
603 pacing_gain_ = kPacingGain[cycle_current_offset_];
604 }
605}
606
607void BbrSender::CheckIfFullBandwidthReached() {
608 if (last_sample_is_app_limited_) {
609 return;
610 }
611
612 QuicBandwidth target = bandwidth_at_last_round_ * kStartupGrowthTarget;
613 if (BandwidthEstimate() >= target) {
614 bandwidth_at_last_round_ = BandwidthEstimate();
615 rounds_without_bandwidth_gain_ = 0;
616 if (expire_ack_aggregation_in_startup_) {
617 // Expire old excess delivery measurements now that bandwidth increased.
618 max_ack_height_.Reset(0, round_trip_count_);
619 }
620 return;
621 }
622
623 rounds_without_bandwidth_gain_++;
624 if ((rounds_without_bandwidth_gain_ >= num_startup_rtts_) ||
625 (exit_startup_on_loss_ && InRecovery())) {
626 DCHECK(has_non_app_limited_sample_);
627 is_at_full_bandwidth_ = true;
628 }
629}
630
631void BbrSender::MaybeExitStartupOrDrain(QuicTime now) {
632 if (mode_ == STARTUP && is_at_full_bandwidth_) {
wub967ba572019-04-01 09:27:52 -0700633 OnExitStartup(now);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500634 mode_ = DRAIN;
635 pacing_gain_ = drain_gain_;
636 congestion_window_gain_ = high_cwnd_gain_;
637 }
638 if (mode_ == DRAIN &&
639 unacked_packets_->bytes_in_flight() <= GetTargetCongestionWindow(1)) {
640 EnterProbeBandwidthMode(now);
641 }
642}
643
wub967ba572019-04-01 09:27:52 -0700644void BbrSender::OnExitStartup(QuicTime now) {
645 DCHECK_EQ(mode_, STARTUP);
646 if (stats_) {
647 DCHECK_NE(stats_->slowstart_start_time, QuicTime::Zero());
648 if (now > stats_->slowstart_start_time) {
649 stats_->slowstart_duration =
650 now - stats_->slowstart_start_time + stats_->slowstart_duration;
651 }
652 stats_->slowstart_start_time = QuicTime::Zero();
653 }
654}
655
QUICHE teama6ef0a62019-03-07 20:34:33 -0500656void BbrSender::MaybeEnterOrExitProbeRtt(QuicTime now,
657 bool is_round_start,
658 bool min_rtt_expired) {
659 if (min_rtt_expired && !exiting_quiescence_ && mode_ != PROBE_RTT) {
wub967ba572019-04-01 09:27:52 -0700660 if (InSlowStart()) {
661 OnExitStartup(now);
662 }
QUICHE teama6ef0a62019-03-07 20:34:33 -0500663 mode_ = PROBE_RTT;
664 pacing_gain_ = 1;
665 // Do not decide on the time to exit PROBE_RTT until the |bytes_in_flight|
666 // is at the target small value.
667 exit_probe_rtt_at_ = QuicTime::Zero();
668 }
669
670 if (mode_ == PROBE_RTT) {
671 sampler_.OnAppLimited();
672
673 if (exit_probe_rtt_at_ == QuicTime::Zero()) {
674 // If the window has reached the appropriate size, schedule exiting
675 // PROBE_RTT. The CWND during PROBE_RTT is kMinimumCongestionWindow, but
676 // we allow an extra packet since QUIC checks CWND before sending a
677 // packet.
678 if (unacked_packets_->bytes_in_flight() <
679 ProbeRttCongestionWindow() + kMaxPacketSize) {
680 exit_probe_rtt_at_ = now + kProbeRttTime;
681 probe_rtt_round_passed_ = false;
682 }
683 } else {
684 if (is_round_start) {
685 probe_rtt_round_passed_ = true;
686 }
687 if (now >= exit_probe_rtt_at_ && probe_rtt_round_passed_) {
688 min_rtt_timestamp_ = now;
689 if (!is_at_full_bandwidth_) {
wub967ba572019-04-01 09:27:52 -0700690 EnterStartupMode(now);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500691 } else {
692 EnterProbeBandwidthMode(now);
693 }
694 }
695 }
696 }
697
698 exiting_quiescence_ = false;
699}
700
701void BbrSender::UpdateRecoveryState(QuicPacketNumber last_acked_packet,
702 bool has_losses,
703 bool is_round_start) {
704 // Exit recovery when there are no losses for a round.
705 if (has_losses) {
706 end_recovery_at_ = last_sent_packet_;
707 }
708
709 switch (recovery_state_) {
710 case NOT_IN_RECOVERY:
711 // Enter conservation on the first loss.
712 if (has_losses) {
713 recovery_state_ = CONSERVATION;
714 // This will cause the |recovery_window_| to be set to the correct
715 // value in CalculateRecoveryWindow().
716 recovery_window_ = 0;
717 // Since the conservation phase is meant to be lasting for a whole
718 // round, extend the current round as if it were started right now.
719 current_round_trip_end_ = last_sent_packet_;
720 if (GetQuicReloadableFlag(quic_bbr_app_limited_recovery) &&
721 last_sample_is_app_limited_) {
722 QUIC_RELOADABLE_FLAG_COUNT(quic_bbr_app_limited_recovery);
723 is_app_limited_recovery_ = true;
724 }
725 }
726 break;
727
728 case CONSERVATION:
729 if (is_round_start) {
730 recovery_state_ = GROWTH;
731 }
732 QUIC_FALLTHROUGH_INTENDED;
733
734 case GROWTH:
735 // Exit recovery if appropriate.
736 if (!has_losses && last_acked_packet > end_recovery_at_) {
737 recovery_state_ = NOT_IN_RECOVERY;
738 is_app_limited_recovery_ = false;
739 }
740
741 break;
742 }
743 if (recovery_state_ != NOT_IN_RECOVERY && is_app_limited_recovery_) {
744 sampler_.OnAppLimited();
745 }
746}
747
748// TODO(ianswett): Move this logic into BandwidthSampler.
749QuicByteCount BbrSender::UpdateAckAggregationBytes(
750 QuicTime ack_time,
751 QuicByteCount newly_acked_bytes) {
752 // Compute how many bytes are expected to be delivered, assuming max bandwidth
753 // is correct.
754 QuicByteCount expected_bytes_acked =
755 max_bandwidth_.GetBest() * (ack_time - aggregation_epoch_start_time_);
756 // Reset the current aggregation epoch as soon as the ack arrival rate is less
757 // than or equal to the max bandwidth.
758 if (aggregation_epoch_bytes_ <= expected_bytes_acked) {
759 // Reset to start measuring a new aggregation epoch.
760 aggregation_epoch_bytes_ = newly_acked_bytes;
761 aggregation_epoch_start_time_ = ack_time;
762 return 0;
763 }
764
765 // Compute how many extra bytes were delivered vs max bandwidth.
766 // Include the bytes most recently acknowledged to account for stretch acks.
767 aggregation_epoch_bytes_ += newly_acked_bytes;
768 max_ack_height_.Update(aggregation_epoch_bytes_ - expected_bytes_acked,
769 round_trip_count_);
770 return aggregation_epoch_bytes_ - expected_bytes_acked;
771}
772
773void BbrSender::CalculatePacingRate() {
774 if (BandwidthEstimate().IsZero()) {
775 return;
776 }
777
778 QuicBandwidth target_rate = pacing_gain_ * BandwidthEstimate();
779 if (is_at_full_bandwidth_) {
780 pacing_rate_ = target_rate;
781 return;
782 }
783
784 // Pace at the rate of initial_window / RTT as soon as RTT measurements are
785 // available.
786 if (pacing_rate_.IsZero() && !rtt_stats_->min_rtt().IsZero()) {
787 pacing_rate_ = QuicBandwidth::FromBytesAndTimeDelta(
788 initial_congestion_window_, rtt_stats_->min_rtt());
789 return;
790 }
791 // Slow the pacing rate in STARTUP once loss has ever been detected.
792 const bool has_ever_detected_loss = end_recovery_at_.IsInitialized();
793 if (slower_startup_ && has_ever_detected_loss &&
794 has_non_app_limited_sample_) {
795 pacing_rate_ = kStartupAfterLossGain * BandwidthEstimate();
796 return;
797 }
798
799 // Slow the pacing rate in STARTUP by the bytes_lost / CWND.
800 if (startup_rate_reduction_multiplier_ != 0 && has_ever_detected_loss &&
801 has_non_app_limited_sample_) {
802 pacing_rate_ =
803 (1 - (startup_bytes_lost_ * startup_rate_reduction_multiplier_ * 1.0f /
804 congestion_window_)) *
805 target_rate;
806 // Ensure the pacing rate doesn't drop below the startup growth target times
807 // the bandwidth estimate.
808 pacing_rate_ =
809 std::max(pacing_rate_, kStartupGrowthTarget * BandwidthEstimate());
810 return;
811 }
812
813 // Do not decrease the pacing rate during startup.
814 pacing_rate_ = std::max(pacing_rate_, target_rate);
815}
816
817void BbrSender::CalculateCongestionWindow(QuicByteCount bytes_acked,
818 QuicByteCount excess_acked) {
819 if (mode_ == PROBE_RTT) {
820 return;
821 }
822
823 QuicByteCount target_window =
824 GetTargetCongestionWindow(congestion_window_gain_);
825 if (is_at_full_bandwidth_) {
826 // Add the max recently measured ack aggregation to CWND.
827 target_window += max_ack_height_.GetBest();
828 } else if (enable_ack_aggregation_during_startup_) {
829 // Add the most recent excess acked. Because CWND never decreases in
830 // STARTUP, this will automatically create a very localized max filter.
831 target_window += excess_acked;
832 }
833
834 // Instead of immediately setting the target CWND as the new one, BBR grows
835 // the CWND towards |target_window| by only increasing it |bytes_acked| at a
836 // time.
837 const bool add_bytes_acked =
838 !GetQuicReloadableFlag(quic_bbr_no_bytes_acked_in_startup_recovery) ||
839 !InRecovery();
840 if (is_at_full_bandwidth_) {
841 congestion_window_ =
842 std::min(target_window, congestion_window_ + bytes_acked);
843 } else if (add_bytes_acked &&
844 (congestion_window_ < target_window ||
845 sampler_.total_bytes_acked() < initial_congestion_window_)) {
846 // If the connection is not yet out of startup phase, do not decrease the
847 // window.
848 congestion_window_ = congestion_window_ + bytes_acked;
849 }
850
851 // Enforce the limits on the congestion window.
852 congestion_window_ = std::max(congestion_window_, min_congestion_window_);
853 congestion_window_ = std::min(congestion_window_, max_congestion_window_);
854}
855
856void BbrSender::CalculateRecoveryWindow(QuicByteCount bytes_acked,
857 QuicByteCount bytes_lost) {
858 if (rate_based_startup_ && mode_ == STARTUP) {
859 return;
860 }
861
862 if (recovery_state_ == NOT_IN_RECOVERY) {
863 return;
864 }
865
866 // Set up the initial recovery window.
867 if (recovery_window_ == 0) {
868 recovery_window_ = unacked_packets_->bytes_in_flight() + bytes_acked;
869 recovery_window_ = std::max(min_congestion_window_, recovery_window_);
870 return;
871 }
872
873 // Remove losses from the recovery window, while accounting for a potential
874 // integer underflow.
875 recovery_window_ = recovery_window_ >= bytes_lost
876 ? recovery_window_ - bytes_lost
877 : kMaxSegmentSize;
878
879 // In CONSERVATION mode, just subtracting losses is sufficient. In GROWTH,
880 // release additional |bytes_acked| to achieve a slow-start-like behavior.
881 if (recovery_state_ == GROWTH) {
882 recovery_window_ += bytes_acked;
883 }
884
885 // Sanity checks. Ensure that we always allow to send at least an MSS or
886 // |bytes_acked| in response, whichever is larger.
887 recovery_window_ = std::max(
888 recovery_window_, unacked_packets_->bytes_in_flight() + bytes_acked);
889 if (GetQuicReloadableFlag(quic_bbr_one_mss_conservation)) {
890 recovery_window_ =
891 std::max(recovery_window_,
892 unacked_packets_->bytes_in_flight() + kMaxSegmentSize);
893 }
894 recovery_window_ = std::max(min_congestion_window_, recovery_window_);
895}
896
vasilvvc48c8712019-03-11 13:38:16 -0700897std::string BbrSender::GetDebugState() const {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500898 std::ostringstream stream;
899 stream << ExportDebugState();
900 return stream.str();
901}
902
903void BbrSender::OnApplicationLimited(QuicByteCount bytes_in_flight) {
904 if (bytes_in_flight >= GetCongestionWindow()) {
905 return;
906 }
907 if (flexible_app_limited_ && IsPipeSufficientlyFull()) {
908 return;
909 }
910
911 app_limited_since_last_probe_rtt_ = true;
912 sampler_.OnAppLimited();
913 QUIC_DVLOG(2) << "Becoming application limited. Last sent packet: "
914 << last_sent_packet_ << ", CWND: " << GetCongestionWindow();
915}
916
917BbrSender::DebugState BbrSender::ExportDebugState() const {
918 return DebugState(*this);
919}
920
vasilvvc48c8712019-03-11 13:38:16 -0700921static std::string ModeToString(BbrSender::Mode mode) {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500922 switch (mode) {
923 case BbrSender::STARTUP:
924 return "STARTUP";
925 case BbrSender::DRAIN:
926 return "DRAIN";
927 case BbrSender::PROBE_BW:
928 return "PROBE_BW";
929 case BbrSender::PROBE_RTT:
930 return "PROBE_RTT";
931 }
932 return "???";
933}
934
935std::ostream& operator<<(std::ostream& os, const BbrSender::Mode& mode) {
936 os << ModeToString(mode);
937 return os;
938}
939
940std::ostream& operator<<(std::ostream& os, const BbrSender::DebugState& state) {
941 os << "Mode: " << ModeToString(state.mode) << std::endl;
942 os << "Maximum bandwidth: " << state.max_bandwidth << std::endl;
943 os << "Round trip counter: " << state.round_trip_count << std::endl;
944 os << "Gain cycle index: " << static_cast<int>(state.gain_cycle_index)
945 << std::endl;
946 os << "Congestion window: " << state.congestion_window << " bytes"
947 << std::endl;
948
949 if (state.mode == BbrSender::STARTUP) {
950 os << "(startup) Bandwidth at last round: " << state.bandwidth_at_last_round
951 << std::endl;
952 os << "(startup) Rounds without gain: "
953 << state.rounds_without_bandwidth_gain << std::endl;
954 }
955
956 os << "Minimum RTT: " << state.min_rtt << std::endl;
957 os << "Minimum RTT timestamp: " << state.min_rtt_timestamp.ToDebuggingValue()
958 << std::endl;
959
960 os << "Last sample is app-limited: "
961 << (state.last_sample_is_app_limited ? "yes" : "no");
962
963 return os;
964}
965
966} // namespace quic