1 // Copyright 2019 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/bbr2_probe_bw.h"
6
7 #include "quiche/quic/core/congestion_control/bbr2_misc.h"
8 #include "quiche/quic/core/congestion_control/bbr2_sender.h"
9 #include "quiche/quic/core/quic_bandwidth.h"
10 #include "quiche/quic/core/quic_time.h"
11 #include "quiche/quic/core/quic_types.h"
12 #include "quiche/quic/platform/api/quic_flag_utils.h"
13 #include "quiche/quic/platform/api/quic_logging.h"
14
15 namespace quic {
16
Enter(QuicTime now,const Bbr2CongestionEvent *)17 void Bbr2ProbeBwMode::Enter(QuicTime now,
18 const Bbr2CongestionEvent* /*congestion_event*/) {
19 if (cycle_.phase == CyclePhase::PROBE_NOT_STARTED) {
20 // First time entering PROBE_BW. Start a new probing cycle.
21 EnterProbeDown(/*probed_too_high=*/false, /*stopped_risky_probe=*/false,
22 now);
23 } else {
24 // Transitioning from PROBE_RTT to PROBE_BW. Re-enter the last phase before
25 // PROBE_RTT.
26 QUICHE_DCHECK(cycle_.phase == CyclePhase::PROBE_CRUISE ||
27 cycle_.phase == CyclePhase::PROBE_REFILL);
28 cycle_.cycle_start_time = now;
29 if (cycle_.phase == CyclePhase::PROBE_CRUISE) {
30 EnterProbeCruise(now);
31 } else if (cycle_.phase == CyclePhase::PROBE_REFILL) {
32 EnterProbeRefill(cycle_.probe_up_rounds, now);
33 }
34 }
35 }
36
OnCongestionEvent(QuicByteCount prior_in_flight,QuicTime event_time,const AckedPacketVector &,const LostPacketVector &,const Bbr2CongestionEvent & congestion_event)37 Bbr2Mode Bbr2ProbeBwMode::OnCongestionEvent(
38 QuicByteCount prior_in_flight, QuicTime event_time,
39 const AckedPacketVector& /*acked_packets*/,
40 const LostPacketVector& /*lost_packets*/,
41 const Bbr2CongestionEvent& congestion_event) {
42 QUICHE_DCHECK_NE(cycle_.phase, CyclePhase::PROBE_NOT_STARTED);
43
44 if (congestion_event.end_of_round_trip) {
45 if (cycle_.cycle_start_time != event_time) {
46 ++cycle_.rounds_since_probe;
47 }
48 if (cycle_.phase_start_time != event_time) {
49 ++cycle_.rounds_in_phase;
50 }
51 }
52
53 bool switch_to_probe_rtt = false;
54
55 if (cycle_.phase == CyclePhase::PROBE_UP) {
56 UpdateProbeUp(prior_in_flight, congestion_event);
57 } else if (cycle_.phase == CyclePhase::PROBE_DOWN) {
58 UpdateProbeDown(prior_in_flight, congestion_event);
59 // Maybe transition to PROBE_RTT at the end of this cycle.
60 if (cycle_.phase != CyclePhase::PROBE_DOWN &&
61 model_->MaybeExpireMinRtt(congestion_event)) {
62 switch_to_probe_rtt = true;
63 }
64 } else if (cycle_.phase == CyclePhase::PROBE_CRUISE) {
65 UpdateProbeCruise(congestion_event);
66 } else if (cycle_.phase == CyclePhase::PROBE_REFILL) {
67 UpdateProbeRefill(congestion_event);
68 }
69
70 // Do not need to set the gains if switching to PROBE_RTT, they will be set
71 // when Bbr2ProbeRttMode::Enter is called.
72 if (!switch_to_probe_rtt) {
73 model_->set_pacing_gain(PacingGainForPhase(cycle_.phase));
74 model_->set_cwnd_gain(Params().probe_bw_cwnd_gain);
75 }
76
77 return switch_to_probe_rtt ? Bbr2Mode::PROBE_RTT : Bbr2Mode::PROBE_BW;
78 }
79
GetCwndLimits() const80 Limits<QuicByteCount> Bbr2ProbeBwMode::GetCwndLimits() const {
81 if (cycle_.phase == CyclePhase::PROBE_CRUISE) {
82 return NoGreaterThan(
83 std::min(model_->inflight_lo(), model_->inflight_hi_with_headroom()));
84 }
85 if (Params().probe_up_ignore_inflight_hi &&
86 cycle_.phase == CyclePhase::PROBE_UP) {
87 // Similar to STARTUP.
88 return NoGreaterThan(model_->inflight_lo());
89 }
90
91 return NoGreaterThan(std::min(model_->inflight_lo(), model_->inflight_hi()));
92 }
93
IsProbingForBandwidth() const94 bool Bbr2ProbeBwMode::IsProbingForBandwidth() const {
95 return cycle_.phase == CyclePhase::PROBE_REFILL ||
96 cycle_.phase == CyclePhase::PROBE_UP;
97 }
98
OnExitQuiescence(QuicTime now,QuicTime quiescence_start_time)99 Bbr2Mode Bbr2ProbeBwMode::OnExitQuiescence(QuicTime now,
100 QuicTime quiescence_start_time) {
101 QUIC_DVLOG(3) << sender_ << " Postponing min_rtt_timestamp("
102 << model_->MinRttTimestamp() << ") by "
103 << now - quiescence_start_time;
104 model_->PostponeMinRttTimestamp(now - quiescence_start_time);
105 return Bbr2Mode::PROBE_BW;
106 }
107
108 // TODO(ianswett): Remove prior_in_flight from UpdateProbeDown.
UpdateProbeDown(QuicByteCount prior_in_flight,const Bbr2CongestionEvent & congestion_event)109 void Bbr2ProbeBwMode::UpdateProbeDown(
110 QuicByteCount prior_in_flight,
111 const Bbr2CongestionEvent& congestion_event) {
112 QUICHE_DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_DOWN);
113
114 if (cycle_.rounds_in_phase == 1 && congestion_event.end_of_round_trip) {
115 cycle_.is_sample_from_probing = false;
116
117 if (!congestion_event.last_packet_send_state.is_app_limited) {
118 QUIC_DVLOG(2)
119 << sender_
120 << " Advancing max bw filter after one round in PROBE_DOWN.";
121 model_->AdvanceMaxBandwidthFilter();
122 cycle_.has_advanced_max_bw = true;
123 }
124
125 if (last_cycle_stopped_risky_probe_ && !last_cycle_probed_too_high_) {
126 EnterProbeRefill(/*probe_up_rounds=*/0, congestion_event.event_time);
127 return;
128 }
129 }
130
131 MaybeAdaptUpperBounds(congestion_event);
132
133 if (IsTimeToProbeBandwidth(congestion_event)) {
134 EnterProbeRefill(/*probe_up_rounds=*/0, congestion_event.event_time);
135 return;
136 }
137
138 if (HasStayedLongEnoughInProbeDown(congestion_event)) {
139 QUIC_DVLOG(3) << sender_ << " Proportional time based PROBE_DOWN exit";
140 EnterProbeCruise(congestion_event.event_time);
141 return;
142 }
143
144 const QuicByteCount inflight_with_headroom =
145 model_->inflight_hi_with_headroom();
146 QUIC_DVLOG(3)
147 << sender_
148 << " Checking if have enough inflight headroom. prior_in_flight:"
149 << prior_in_flight << " congestion_event.bytes_in_flight:"
150 << congestion_event.bytes_in_flight
151 << ", inflight_with_headroom:" << inflight_with_headroom;
152 QuicByteCount bytes_in_flight = congestion_event.bytes_in_flight;
153
154 if (bytes_in_flight > inflight_with_headroom) {
155 // Stay in PROBE_DOWN.
156 return;
157 }
158
159 // Transition to PROBE_CRUISE iff we've drained to target.
160 QuicByteCount bdp = model_->BDP();
161 QUIC_DVLOG(3) << sender_ << " Checking if drained to target. bytes_in_flight:"
162 << bytes_in_flight << ", bdp:" << bdp;
163 if (bytes_in_flight < bdp) {
164 EnterProbeCruise(congestion_event.event_time);
165 }
166 }
167
MaybeAdaptUpperBounds(const Bbr2CongestionEvent & congestion_event)168 Bbr2ProbeBwMode::AdaptUpperBoundsResult Bbr2ProbeBwMode::MaybeAdaptUpperBounds(
169 const Bbr2CongestionEvent& congestion_event) {
170 const SendTimeState& send_state = congestion_event.last_packet_send_state;
171 if (!send_state.is_valid) {
172 QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
173 << ": NOT_ADAPTED_INVALID_SAMPLE";
174 return NOT_ADAPTED_INVALID_SAMPLE;
175 }
176
177 // TODO(ianswett): Rename to bytes_delivered if
178 // use_bytes_delivered_for_inflight_hi is default enabled.
179 QuicByteCount inflight_at_send = BytesInFlight(send_state);
180 if (Params().use_bytes_delivered_for_inflight_hi) {
181 if (congestion_event.last_packet_send_state.total_bytes_acked <=
182 model_->total_bytes_acked()) {
183 inflight_at_send =
184 model_->total_bytes_acked() -
185 congestion_event.last_packet_send_state.total_bytes_acked;
186 } else {
187 QUIC_BUG(quic_bug_10436_1)
188 << "Total_bytes_acked(" << model_->total_bytes_acked()
189 << ") < send_state.total_bytes_acked("
190 << congestion_event.last_packet_send_state.total_bytes_acked << ")";
191 }
192 }
193 // TODO(ianswett): Inflight too high is really checking for loss, not
194 // inflight.
195 if (model_->IsInflightTooHigh(congestion_event,
196 Params().probe_bw_full_loss_count)) {
197 if (cycle_.is_sample_from_probing) {
198 cycle_.is_sample_from_probing = false;
199 if (!send_state.is_app_limited ||
200 Params().max_probe_up_queue_rounds > 0) {
201 const QuicByteCount inflight_target =
202 sender_->GetTargetBytesInflight() * (1.0 - Params().beta);
203 if (inflight_at_send >= inflight_target) {
204 // The new code does not change behavior.
205 QUIC_CODE_COUNT(quic_bbr2_cut_inflight_hi_gradually_noop);
206 } else {
207 // The new code actually cuts inflight_hi slower than before.
208 QUIC_CODE_COUNT(quic_bbr2_cut_inflight_hi_gradually_in_effect);
209 }
210 if (Params().limit_inflight_hi_by_max_delivered) {
211 QuicByteCount new_inflight_hi =
212 std::max(inflight_at_send, inflight_target);
213 if (new_inflight_hi >= model_->max_bytes_delivered_in_round()) {
214 QUIC_CODE_COUNT(quic_bbr2_cut_inflight_hi_max_delivered_noop);
215 } else {
216 QUIC_CODE_COUNT(quic_bbr2_cut_inflight_hi_max_delivered_in_effect);
217 new_inflight_hi = model_->max_bytes_delivered_in_round();
218 }
219 QUIC_DVLOG(3) << sender_
220 << " Setting inflight_hi due to loss. new_inflight_hi:"
221 << new_inflight_hi
222 << ", inflight_at_send:" << inflight_at_send
223 << ", inflight_target:" << inflight_target
224 << ", max_bytes_delivered_in_round:"
225 << model_->max_bytes_delivered_in_round() << " @ "
226 << congestion_event.event_time;
227 model_->set_inflight_hi(new_inflight_hi);
228 } else {
229 model_->set_inflight_hi(std::max(inflight_at_send, inflight_target));
230 }
231 }
232
233 QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
234 << ": ADAPTED_PROBED_TOO_HIGH";
235 return ADAPTED_PROBED_TOO_HIGH;
236 }
237 return ADAPTED_OK;
238 }
239
240 if (model_->inflight_hi() == model_->inflight_hi_default()) {
241 QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
242 << ": NOT_ADAPTED_INFLIGHT_HIGH_NOT_SET";
243 return NOT_ADAPTED_INFLIGHT_HIGH_NOT_SET;
244 }
245
246 // Raise the upper bound for inflight.
247 if (inflight_at_send > model_->inflight_hi()) {
248 QUIC_DVLOG(3)
249 << sender_ << " " << cycle_.phase
250 << ": Adapting inflight_hi from inflight_at_send. inflight_at_send:"
251 << inflight_at_send << ", old inflight_hi:" << model_->inflight_hi();
252 model_->set_inflight_hi(inflight_at_send);
253 }
254
255 return ADAPTED_OK;
256 }
257
IsTimeToProbeBandwidth(const Bbr2CongestionEvent & congestion_event) const258 bool Bbr2ProbeBwMode::IsTimeToProbeBandwidth(
259 const Bbr2CongestionEvent& congestion_event) const {
260 if (HasCycleLasted(cycle_.probe_wait_time, congestion_event)) {
261 return true;
262 }
263
264 if (IsTimeToProbeForRenoCoexistence(1.0, congestion_event)) {
265 ++sender_->connection_stats_->bbr_num_short_cycles_for_reno_coexistence;
266 return true;
267 }
268 return false;
269 }
270
271 // QUIC only. Used to prevent a Bbr2 flow from staying in PROBE_DOWN for too
272 // long, as seen in some multi-sender simulator tests.
HasStayedLongEnoughInProbeDown(const Bbr2CongestionEvent & congestion_event) const273 bool Bbr2ProbeBwMode::HasStayedLongEnoughInProbeDown(
274 const Bbr2CongestionEvent& congestion_event) const {
275 // Stay in PROBE_DOWN for at most the time of a min rtt, as it is done in
276 // BBRv1.
277 // TODO(wub): Consider exit after a full round instead, which typically
278 // indicates most(if not all) packets sent during PROBE_UP have been acked.
279 return HasPhaseLasted(model_->MinRtt(), congestion_event);
280 }
281
HasCycleLasted(QuicTime::Delta duration,const Bbr2CongestionEvent & congestion_event) const282 bool Bbr2ProbeBwMode::HasCycleLasted(
283 QuicTime::Delta duration,
284 const Bbr2CongestionEvent& congestion_event) const {
285 bool result =
286 (congestion_event.event_time - cycle_.cycle_start_time) > duration;
287 QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
288 << ": HasCycleLasted=" << result << ". elapsed:"
289 << (congestion_event.event_time - cycle_.cycle_start_time)
290 << ", duration:" << duration;
291 return result;
292 }
293
HasPhaseLasted(QuicTime::Delta duration,const Bbr2CongestionEvent & congestion_event) const294 bool Bbr2ProbeBwMode::HasPhaseLasted(
295 QuicTime::Delta duration,
296 const Bbr2CongestionEvent& congestion_event) const {
297 bool result =
298 (congestion_event.event_time - cycle_.phase_start_time) > duration;
299 QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
300 << ": HasPhaseLasted=" << result << ". elapsed:"
301 << (congestion_event.event_time - cycle_.phase_start_time)
302 << ", duration:" << duration;
303 return result;
304 }
305
IsTimeToProbeForRenoCoexistence(double probe_wait_fraction,const Bbr2CongestionEvent &) const306 bool Bbr2ProbeBwMode::IsTimeToProbeForRenoCoexistence(
307 double probe_wait_fraction,
308 const Bbr2CongestionEvent& /*congestion_event*/) const {
309 if (!Params().enable_reno_coexistence) {
310 return false;
311 }
312
313 uint64_t rounds = Params().probe_bw_probe_max_rounds;
314 if (Params().probe_bw_probe_reno_gain > 0.0) {
315 QuicByteCount target_bytes_inflight = sender_->GetTargetBytesInflight();
316 uint64_t reno_rounds = Params().probe_bw_probe_reno_gain *
317 target_bytes_inflight / kDefaultTCPMSS;
318 rounds = std::min(rounds, reno_rounds);
319 }
320 bool result = cycle_.rounds_since_probe >= (rounds * probe_wait_fraction);
321 QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
322 << ": IsTimeToProbeForRenoCoexistence=" << result
323 << ". rounds_since_probe:" << cycle_.rounds_since_probe
324 << ", rounds:" << rounds
325 << ", probe_wait_fraction:" << probe_wait_fraction;
326 return result;
327 }
328
RaiseInflightHighSlope()329 void Bbr2ProbeBwMode::RaiseInflightHighSlope() {
330 QUICHE_DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_UP);
331 uint64_t growth_this_round = 1 << cycle_.probe_up_rounds;
332 // The number 30 below means |growth_this_round| is capped at 1G and the lower
333 // bound of |probe_up_bytes| is (practically) 1 mss, at this speed inflight_hi
334 // grows by approximately 1 packet per packet acked.
335 cycle_.probe_up_rounds = std::min<uint64_t>(cycle_.probe_up_rounds + 1, 30);
336 uint64_t probe_up_bytes = sender_->GetCongestionWindow() / growth_this_round;
337 cycle_.probe_up_bytes =
338 std::max<QuicByteCount>(probe_up_bytes, kDefaultTCPMSS);
339 QUIC_DVLOG(3) << sender_ << " Rasing inflight_hi slope. probe_up_rounds:"
340 << cycle_.probe_up_rounds
341 << ", probe_up_bytes:" << cycle_.probe_up_bytes;
342 }
343
ProbeInflightHighUpward(const Bbr2CongestionEvent & congestion_event)344 void Bbr2ProbeBwMode::ProbeInflightHighUpward(
345 const Bbr2CongestionEvent& congestion_event) {
346 QUICHE_DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_UP);
347 if (Params().probe_up_ignore_inflight_hi) {
348 // When inflight_hi is disabled in PROBE_UP, it increases when
349 // the number of bytes delivered in a round is larger inflight_hi.
350 return;
351 }
352 if (Params().probe_up_simplify_inflight_hi) {
353 // Raise inflight_hi exponentially if it was utilized this round.
354 cycle_.probe_up_acked += congestion_event.bytes_acked;
355 if (!congestion_event.end_of_round_trip) {
356 return;
357 }
358 if (!model_->inflight_hi_limited_in_round() ||
359 model_->loss_events_in_round() > 0) {
360 cycle_.probe_up_acked = 0;
361 return;
362 }
363 } else {
364 if (congestion_event.prior_bytes_in_flight < congestion_event.prior_cwnd) {
365 QUIC_DVLOG(3) << sender_
366 << " Raising inflight_hi early return: Not cwnd limited.";
367 // Not fully utilizing cwnd, so can't safely grow.
368 return;
369 }
370
371 if (congestion_event.prior_cwnd < model_->inflight_hi()) {
372 QUIC_DVLOG(3)
373 << sender_
374 << " Raising inflight_hi early return: inflight_hi not fully used.";
375 // Not fully using inflight_hi, so don't grow it.
376 return;
377 }
378
379 // Increase inflight_hi by the number of probe_up_bytes within
380 // probe_up_acked.
381 cycle_.probe_up_acked += congestion_event.bytes_acked;
382 }
383
384 if (cycle_.probe_up_acked >= cycle_.probe_up_bytes) {
385 uint64_t delta = cycle_.probe_up_acked / cycle_.probe_up_bytes;
386 cycle_.probe_up_acked -= delta * cycle_.probe_up_bytes;
387 QuicByteCount new_inflight_hi =
388 model_->inflight_hi() + delta * kDefaultTCPMSS;
389 if (new_inflight_hi > model_->inflight_hi()) {
390 QUIC_DVLOG(3) << sender_ << " Raising inflight_hi from "
391 << model_->inflight_hi() << " to " << new_inflight_hi
392 << ". probe_up_bytes:" << cycle_.probe_up_bytes
393 << ", delta:" << delta
394 << ", (new)probe_up_acked:" << cycle_.probe_up_acked;
395
396 model_->set_inflight_hi(new_inflight_hi);
397 } else {
398 QUIC_BUG(quic_bug_10436_2)
399 << "Not growing inflight_hi due to wrap around. Old value:"
400 << model_->inflight_hi() << ", new value:" << new_inflight_hi;
401 }
402 }
403
404 if (congestion_event.end_of_round_trip) {
405 RaiseInflightHighSlope();
406 }
407 }
408
UpdateProbeCruise(const Bbr2CongestionEvent & congestion_event)409 void Bbr2ProbeBwMode::UpdateProbeCruise(
410 const Bbr2CongestionEvent& congestion_event) {
411 QUICHE_DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_CRUISE);
412 MaybeAdaptUpperBounds(congestion_event);
413 QUICHE_DCHECK(!cycle_.is_sample_from_probing);
414
415 if (IsTimeToProbeBandwidth(congestion_event)) {
416 EnterProbeRefill(/*probe_up_rounds=*/0, congestion_event.event_time);
417 return;
418 }
419 }
420
UpdateProbeRefill(const Bbr2CongestionEvent & congestion_event)421 void Bbr2ProbeBwMode::UpdateProbeRefill(
422 const Bbr2CongestionEvent& congestion_event) {
423 QUICHE_DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_REFILL);
424 MaybeAdaptUpperBounds(congestion_event);
425 QUICHE_DCHECK(!cycle_.is_sample_from_probing);
426
427 if (cycle_.rounds_in_phase > 0 && congestion_event.end_of_round_trip) {
428 EnterProbeUp(congestion_event.event_time);
429 return;
430 }
431 }
432
UpdateProbeUp(QuicByteCount prior_in_flight,const Bbr2CongestionEvent & congestion_event)433 void Bbr2ProbeBwMode::UpdateProbeUp(
434 QuicByteCount prior_in_flight,
435 const Bbr2CongestionEvent& congestion_event) {
436 QUICHE_DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_UP);
437 if (MaybeAdaptUpperBounds(congestion_event) == ADAPTED_PROBED_TOO_HIGH) {
438 EnterProbeDown(/*probed_too_high=*/true, /*stopped_risky_probe=*/false,
439 congestion_event.event_time);
440 return;
441 }
442
443 // TODO(wub): Consider exit PROBE_UP after a certain number(e.g. 64) of RTTs.
444
445 ProbeInflightHighUpward(congestion_event);
446
447 bool is_risky = false;
448 bool is_queuing = false;
449 if (last_cycle_probed_too_high_ && prior_in_flight >= model_->inflight_hi()) {
450 is_risky = true;
451 QUIC_DVLOG(3) << sender_
452 << " Probe is too risky. last_cycle_probed_too_high_:"
453 << last_cycle_probed_too_high_
454 << ", prior_in_flight:" << prior_in_flight
455 << ", inflight_hi:" << model_->inflight_hi();
456 // TCP uses min_rtt instead of a full round:
457 // HasPhaseLasted(model_->MinRtt(), congestion_event)
458 } else if (cycle_.rounds_in_phase > 0) {
459 if (Params().max_probe_up_queue_rounds > 0) {
460 if (congestion_event.end_of_round_trip) {
461 model_->CheckPersistentQueue(congestion_event,
462 Params().full_bw_threshold);
463 if (model_->rounds_with_queueing() >=
464 Params().max_probe_up_queue_rounds) {
465 QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_probe_two_rounds, 3, 3);
466 is_queuing = true;
467 }
468 }
469 } else {
470 QuicByteCount queuing_threshold_extra_bytes =
471 model_->QueueingThresholdExtraBytes();
472 if (Params().add_ack_height_to_queueing_threshold) {
473 queuing_threshold_extra_bytes += model_->MaxAckHeight();
474 }
475 QuicByteCount queuing_threshold =
476 (Params().full_bw_threshold * model_->BDP()) +
477 queuing_threshold_extra_bytes;
478
479 is_queuing = congestion_event.bytes_in_flight >= queuing_threshold;
480
481 QUIC_DVLOG(3) << sender_
482 << " Checking if building up a queue. prior_in_flight:"
483 << prior_in_flight
484 << ", post_in_flight:" << congestion_event.bytes_in_flight
485 << ", threshold:" << queuing_threshold
486 << ", is_queuing:" << is_queuing
487 << ", max_bw:" << model_->MaxBandwidth()
488 << ", min_rtt:" << model_->MinRtt();
489 }
490 }
491
492 if (is_risky || is_queuing) {
493 EnterProbeDown(/*probed_too_high=*/false, /*stopped_risky_probe=*/is_risky,
494 congestion_event.event_time);
495 }
496 }
497
EnterProbeDown(bool probed_too_high,bool stopped_risky_probe,QuicTime now)498 void Bbr2ProbeBwMode::EnterProbeDown(bool probed_too_high,
499 bool stopped_risky_probe, QuicTime now) {
500 QUIC_DVLOG(2) << sender_ << " Phase change: " << cycle_.phase << " ==> "
501 << CyclePhase::PROBE_DOWN << " after "
502 << now - cycle_.phase_start_time << ", or "
503 << cycle_.rounds_in_phase
504 << " rounds. probed_too_high:" << probed_too_high
505 << ", stopped_risky_probe:" << stopped_risky_probe << " @ "
506 << now;
507 last_cycle_probed_too_high_ = probed_too_high;
508 last_cycle_stopped_risky_probe_ = stopped_risky_probe;
509
510 cycle_.cycle_start_time = now;
511 cycle_.phase = CyclePhase::PROBE_DOWN;
512 cycle_.rounds_in_phase = 0;
513 cycle_.phase_start_time = now;
514 ++sender_->connection_stats_->bbr_num_cycles;
515 if (Params().bw_lo_mode_ != Bbr2Params::QuicBandwidthLoMode::DEFAULT) {
516 // Clear bandwidth lo if it was set in PROBE_UP, because losses in PROBE_UP
517 // should not permanently change bandwidth_lo.
518 // It's possible for bandwidth_lo to be set during REFILL, but if that was
519 // a valid value, it'll quickly be rediscovered.
520 model_->clear_bandwidth_lo();
521 }
522
523 // Pick probe wait time.
524 cycle_.rounds_since_probe =
525 sender_->RandomUint64(Params().probe_bw_max_probe_rand_rounds);
526 cycle_.probe_wait_time =
527 Params().probe_bw_probe_base_duration +
528 QuicTime::Delta::FromMicroseconds(sender_->RandomUint64(
529 Params().probe_bw_probe_max_rand_duration.ToMicroseconds()));
530
531 cycle_.probe_up_bytes = std::numeric_limits<QuicByteCount>::max();
532 cycle_.probe_up_app_limited_since_inflight_hi_limited_ = false;
533 cycle_.has_advanced_max_bw = false;
534 model_->RestartRoundEarly();
535 }
536
EnterProbeCruise(QuicTime now)537 void Bbr2ProbeBwMode::EnterProbeCruise(QuicTime now) {
538 if (cycle_.phase == CyclePhase::PROBE_DOWN) {
539 ExitProbeDown();
540 }
541 QUIC_DVLOG(2) << sender_ << " Phase change: " << cycle_.phase << " ==> "
542 << CyclePhase::PROBE_CRUISE << " after "
543 << now - cycle_.phase_start_time << ", or "
544 << cycle_.rounds_in_phase << " rounds. @ " << now;
545
546 model_->cap_inflight_lo(model_->inflight_hi());
547 cycle_.phase = CyclePhase::PROBE_CRUISE;
548 cycle_.rounds_in_phase = 0;
549 cycle_.phase_start_time = now;
550 cycle_.is_sample_from_probing = false;
551 }
552
EnterProbeRefill(uint64_t probe_up_rounds,QuicTime now)553 void Bbr2ProbeBwMode::EnterProbeRefill(uint64_t probe_up_rounds, QuicTime now) {
554 if (cycle_.phase == CyclePhase::PROBE_DOWN) {
555 ExitProbeDown();
556 }
557 QUIC_DVLOG(2) << sender_ << " Phase change: " << cycle_.phase << " ==> "
558 << CyclePhase::PROBE_REFILL << " after "
559 << now - cycle_.phase_start_time << ", or "
560 << cycle_.rounds_in_phase
561 << " rounds. probe_up_rounds:" << probe_up_rounds << " @ "
562 << now;
563 cycle_.phase = CyclePhase::PROBE_REFILL;
564 cycle_.rounds_in_phase = 0;
565 cycle_.phase_start_time = now;
566 cycle_.is_sample_from_probing = false;
567 last_cycle_stopped_risky_probe_ = false;
568
569 model_->clear_bandwidth_lo();
570 model_->clear_inflight_lo();
571 cycle_.probe_up_rounds = probe_up_rounds;
572 cycle_.probe_up_acked = 0;
573 model_->RestartRoundEarly();
574 }
575
EnterProbeUp(QuicTime now)576 void Bbr2ProbeBwMode::EnterProbeUp(QuicTime now) {
577 QUICHE_DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_REFILL);
578 QUIC_DVLOG(2) << sender_ << " Phase change: " << cycle_.phase << " ==> "
579 << CyclePhase::PROBE_UP << " after "
580 << now - cycle_.phase_start_time << ", or "
581 << cycle_.rounds_in_phase << " rounds. @ " << now;
582 cycle_.phase = CyclePhase::PROBE_UP;
583 cycle_.rounds_in_phase = 0;
584 cycle_.phase_start_time = now;
585 cycle_.is_sample_from_probing = true;
586 RaiseInflightHighSlope();
587
588 model_->RestartRoundEarly();
589 }
590
ExitProbeDown()591 void Bbr2ProbeBwMode::ExitProbeDown() {
592 QUICHE_DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_DOWN);
593 if (!cycle_.has_advanced_max_bw) {
594 QUIC_DVLOG(2) << sender_ << " Advancing max bw filter at end of cycle.";
595 model_->AdvanceMaxBandwidthFilter();
596 cycle_.has_advanced_max_bw = true;
597 }
598 }
599
600 // static
CyclePhaseToString(CyclePhase phase)601 const char* Bbr2ProbeBwMode::CyclePhaseToString(CyclePhase phase) {
602 switch (phase) {
603 case CyclePhase::PROBE_NOT_STARTED:
604 return "PROBE_NOT_STARTED";
605 case CyclePhase::PROBE_UP:
606 return "PROBE_UP";
607 case CyclePhase::PROBE_DOWN:
608 return "PROBE_DOWN";
609 case CyclePhase::PROBE_CRUISE:
610 return "PROBE_CRUISE";
611 case CyclePhase::PROBE_REFILL:
612 return "PROBE_REFILL";
613 default:
614 break;
615 }
616 return "<Invalid CyclePhase>";
617 }
618
operator <<(std::ostream & os,const Bbr2ProbeBwMode::CyclePhase phase)619 std::ostream& operator<<(std::ostream& os,
620 const Bbr2ProbeBwMode::CyclePhase phase) {
621 return os << Bbr2ProbeBwMode::CyclePhaseToString(phase);
622 }
623
ExportDebugState() const624 Bbr2ProbeBwMode::DebugState Bbr2ProbeBwMode::ExportDebugState() const {
625 DebugState s;
626 s.phase = cycle_.phase;
627 s.cycle_start_time = cycle_.cycle_start_time;
628 s.phase_start_time = cycle_.phase_start_time;
629 return s;
630 }
631
operator <<(std::ostream & os,const Bbr2ProbeBwMode::DebugState & state)632 std::ostream& operator<<(std::ostream& os,
633 const Bbr2ProbeBwMode::DebugState& state) {
634 os << "[PROBE_BW] phase: " << state.phase << "\n";
635 os << "[PROBE_BW] cycle_start_time: " << state.cycle_start_time << "\n";
636 os << "[PROBE_BW] phase_start_time: " << state.phase_start_time << "\n";
637 return os;
638 }
639
Params() const640 const Bbr2Params& Bbr2ProbeBwMode::Params() const { return sender_->Params(); }
641
PacingGainForPhase(Bbr2ProbeBwMode::CyclePhase phase) const642 float Bbr2ProbeBwMode::PacingGainForPhase(
643 Bbr2ProbeBwMode::CyclePhase phase) const {
644 if (phase == Bbr2ProbeBwMode::CyclePhase::PROBE_UP) {
645 return Params().probe_bw_probe_up_pacing_gain;
646 }
647 if (phase == Bbr2ProbeBwMode::CyclePhase::PROBE_DOWN) {
648 return Params().probe_bw_probe_down_pacing_gain;
649 }
650 return Params().probe_bw_default_pacing_gain;
651 }
652
653 } // namespace quic
654