blob: 3c8c787c3cb3a648932115be671dcf11299d27f5 [file] [log] [blame]
QUICHE team7872c772019-07-23 10:19:37 -04001// 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 "net/third_party/quiche/src/quic/core/congestion_control/bbr2_probe_bw.h"
6
7#include "net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.h"
8#include "net/third_party/quiche/src/quic/core/congestion_control/bbr2_sender.h"
9#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
10#include "net/third_party/quiche/src/quic/core/quic_time.h"
11#include "net/third_party/quiche/src/quic/core/quic_types.h"
wub32dce0f2019-12-18 15:25:24 -080012#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
dmcardle16df40e2020-01-03 14:50:41 -080013#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
QUICHE team7872c772019-07-23 10:19:37 -040014
15namespace quic {
16
17void Bbr2ProbeBwMode::Enter(const Bbr2CongestionEvent& congestion_event) {
18 if (cycle_.phase == CyclePhase::PROBE_NOT_STARTED) {
19 // First time entering PROBE_BW. Start a new probing cycle.
20 EnterProbeDown(/*probed_too_high=*/false, /*stopped_risky_probe=*/false,
21 congestion_event);
22 } else {
23 // Transitioning from PROBE_RTT to PROBE_BW. Re-enter the last phase before
24 // PROBE_RTT.
25 DCHECK(cycle_.phase == CyclePhase::PROBE_CRUISE ||
26 cycle_.phase == CyclePhase::PROBE_REFILL);
27 cycle_.cycle_start_time = congestion_event.event_time;
28 if (cycle_.phase == CyclePhase::PROBE_CRUISE) {
29 EnterProbeCruise(congestion_event);
30 } else if (cycle_.phase == CyclePhase::PROBE_REFILL) {
31 EnterProbeRefill(cycle_.probe_up_rounds, congestion_event);
32 }
33 }
34}
35
36Bbr2Mode Bbr2ProbeBwMode::OnCongestionEvent(
37 QuicByteCount prior_in_flight,
38 QuicTime event_time,
39 const AckedPacketVector& /*acked_packets*/,
40 const LostPacketVector& /*lost_packets*/,
41 const Bbr2CongestionEvent& congestion_event) {
42 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
wub2fd0c6c2019-11-25 07:09:16 -080053 bool switch_to_probe_rtt = false;
54
QUICHE team7872c772019-07-23 10:19:37 -040055 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)) {
wub2fd0c6c2019-11-25 07:09:16 -080062 switch_to_probe_rtt = true;
QUICHE team7872c772019-07-23 10:19:37 -040063 }
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
wub2fd0c6c2019-11-25 07:09:16 -080070 // 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 }
QUICHE team7872c772019-07-23 10:19:37 -040076
wub2fd0c6c2019-11-25 07:09:16 -080077 return switch_to_probe_rtt ? Bbr2Mode::PROBE_RTT : Bbr2Mode::PROBE_BW;
QUICHE team7872c772019-07-23 10:19:37 -040078}
79
80Limits<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
86 return NoGreaterThan(std::min(model_->inflight_lo(), model_->inflight_hi()));
87}
88
89bool Bbr2ProbeBwMode::IsProbingForBandwidth() const {
90 return cycle_.phase == CyclePhase::PROBE_REFILL ||
91 cycle_.phase == CyclePhase::PROBE_UP;
92}
93
94void Bbr2ProbeBwMode::UpdateProbeDown(
95 QuicByteCount prior_in_flight,
96 const Bbr2CongestionEvent& congestion_event) {
97 DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_DOWN);
98
99 if (cycle_.rounds_in_phase == 1 && congestion_event.end_of_round_trip) {
100 cycle_.is_sample_from_probing = false;
101
102 if (!congestion_event.last_sample_is_app_limited) {
103 QUIC_DVLOG(2)
104 << sender_
105 << " Advancing max bw filter after one round in PROBE_DOWN.";
106 model_->AdvanceMaxBandwidthFilter();
107 cycle_.has_advanced_max_bw = true;
108 }
109
110 if (last_cycle_stopped_risky_probe_ && !last_cycle_probed_too_high_) {
111 EnterProbeRefill(/*probe_up_rounds=*/0, congestion_event);
112 return;
113 }
114 }
115
116 MaybeAdaptUpperBounds(congestion_event);
117
118 if (IsTimeToProbeBandwidth(congestion_event)) {
119 EnterProbeRefill(/*probe_up_rounds=*/0, congestion_event);
120 return;
121 }
122
123 if (HasStayedLongEnoughInProbeDown(congestion_event)) {
124 QUIC_DVLOG(3) << sender_ << " Proportional time based PROBE_DOWN exit";
125 EnterProbeCruise(congestion_event);
126 return;
127 }
128
129 const QuicByteCount inflight_with_headroom =
130 model_->inflight_hi_with_headroom();
131 QUIC_DVLOG(3)
132 << sender_
133 << " Checking if have enough inflight headroom. prior_in_flight:"
134 << prior_in_flight
135 << ", inflight_with_headroom:" << inflight_with_headroom;
136 if (prior_in_flight > inflight_with_headroom) {
137 // Stay in PROBE_DOWN.
138 return;
139 }
140
141 // Transition to PROBE_CRUISE iff we've drained to target.
142 QuicByteCount bdp = model_->BDP(model_->MaxBandwidth());
143 QUIC_DVLOG(3) << sender_ << " Checking if drained to target. prior_in_flight:"
144 << prior_in_flight << ", bdp:" << bdp;
145 if (prior_in_flight < bdp) {
146 EnterProbeCruise(congestion_event);
147 }
148}
149
150Bbr2ProbeBwMode::AdaptUpperBoundsResult Bbr2ProbeBwMode::MaybeAdaptUpperBounds(
151 const Bbr2CongestionEvent& congestion_event) {
wubc690c9c2019-12-23 08:27:12 -0800152 const SendTimeState& send_state =
153 model_->one_bw_sample_per_ack_event()
154 ? congestion_event.last_packet_send_state
155 : SendStateOfLargestPacket(congestion_event);
QUICHE team7872c772019-07-23 10:19:37 -0400156 if (!send_state.is_valid) {
157 QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
158 << ": NOT_ADAPTED_INVALID_SAMPLE";
159 return NOT_ADAPTED_INVALID_SAMPLE;
160 }
161
wub28e34682020-01-15 13:27:13 -0800162 bool has_enough_loss_events = true;
163 if (model_->always_count_loss_events()) {
164 QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_always_count_loss_events, 2, 2);
165 has_enough_loss_events =
166 model_->loss_events_in_round() >= Params().probe_bw_full_loss_count;
167 }
168
169 if (has_enough_loss_events && model_->IsInflightTooHigh(congestion_event)) {
QUICHE team7872c772019-07-23 10:19:37 -0400170 if (cycle_.is_sample_from_probing) {
171 cycle_.is_sample_from_probing = false;
172
173 if (!send_state.is_app_limited) {
174 QuicByteCount inflight_at_send = BytesInFlight(send_state);
wub32dce0f2019-12-18 15:25:24 -0800175 if (GetQuicReloadableFlag(quic_bbr2_cut_inflight_hi_gradually)) {
176 QuicByteCount inflight_target =
177 sender_->GetTargetBytesInflight() * (1.0 - Params().beta);
178 if (inflight_at_send >= inflight_target) {
179 // The new code does not change behavior.
180 QUIC_CODE_COUNT(quic_bbr2_cut_inflight_hi_gradually_noop);
181 } else {
182 // The new code actually cuts inflight_hi slower than before.
183 QUIC_CODE_COUNT(quic_bbr2_cut_inflight_hi_gradually_in_effect);
184 }
185 model_->set_inflight_hi(std::max(inflight_at_send, inflight_target));
186 } else {
187 model_->set_inflight_hi(inflight_at_send);
188 }
QUICHE team7872c772019-07-23 10:19:37 -0400189 }
190
191 QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
192 << ": ADAPTED_PROBED_TOO_HIGH";
193 return ADAPTED_PROBED_TOO_HIGH;
194 }
195 return ADAPTED_OK;
196 }
197
198 if (model_->inflight_hi() == model_->inflight_hi_default()) {
199 QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
200 << ": NOT_ADAPTED_INFLIGHT_HIGH_NOT_SET";
201 return NOT_ADAPTED_INFLIGHT_HIGH_NOT_SET;
202 }
203
204 const QuicByteCount inflight_at_send = BytesInFlight(send_state);
205
206 // Raise the upper bound for inflight.
207 if (inflight_at_send > model_->inflight_hi()) {
208 QUIC_DVLOG(3)
209 << sender_ << " " << cycle_.phase
210 << ": Adapting inflight_hi from inflight_at_send. inflight_at_send:"
211 << inflight_at_send << ", old inflight_hi:" << model_->inflight_hi();
212 model_->set_inflight_hi(inflight_at_send);
213 }
214
215 return ADAPTED_OK;
216}
217
218bool Bbr2ProbeBwMode::IsTimeToProbeBandwidth(
219 const Bbr2CongestionEvent& congestion_event) const {
wub9528ecf2020-01-16 13:16:48 -0800220 if (HasCycleLasted(cycle_.probe_wait_time, congestion_event)) {
221 return true;
222 }
223
224 if (IsTimeToProbeForRenoCoexistence(1.0, congestion_event)) {
225 ++sender_->connection_stats_->bbr_num_short_cycles_for_reno_coexistence;
226 return true;
227 }
228 return false;
QUICHE team7872c772019-07-23 10:19:37 -0400229}
230
231// QUIC only. Used to prevent a Bbr2 flow from staying in PROBE_DOWN for too
232// long, as seen in some multi-sender simulator tests.
233bool Bbr2ProbeBwMode::HasStayedLongEnoughInProbeDown(
234 const Bbr2CongestionEvent& congestion_event) const {
wub720c8e62019-12-02 16:01:56 -0800235 if (exit_probe_down_after_one_rtt_) {
236 QUIC_RELOADABLE_FLAG_COUNT(quic_bbr2_exit_probe_bw_down_after_one_rtt);
237 // Stay in PROBE_DOWN for at most the time of a min rtt, as it is done in
238 // BBRv1. The intention here is to figure out whether the performance
239 // regression in BBRv2 is because it stays in PROBE_DOWN for too long.
240 // TODO(wub): Consider exit after a full round instead, which typically
241 // indicates most(if not all) packets sent during PROBE_UP have been acked.
242 return HasPhaseLasted(model_->MinRtt(), congestion_event);
243 }
QUICHE team7872c772019-07-23 10:19:37 -0400244 // The amount of time to stay in PROBE_DOWN, as a fraction of probe wait time.
245 const double kProbeWaitFraction = 0.2;
246 return HasCycleLasted(cycle_.probe_wait_time * kProbeWaitFraction,
247 congestion_event) ||
248 IsTimeToProbeForRenoCoexistence(kProbeWaitFraction, congestion_event);
249}
250
251bool Bbr2ProbeBwMode::HasCycleLasted(
252 QuicTime::Delta duration,
253 const Bbr2CongestionEvent& congestion_event) const {
254 bool result =
255 (congestion_event.event_time - cycle_.cycle_start_time) > duration;
256 QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
257 << ": HasCycleLasted=" << result << ". elapsed:"
258 << (congestion_event.event_time - cycle_.cycle_start_time)
259 << ", duration:" << duration;
260 return result;
261}
262
263bool Bbr2ProbeBwMode::HasPhaseLasted(
264 QuicTime::Delta duration,
265 const Bbr2CongestionEvent& congestion_event) const {
266 bool result =
267 (congestion_event.event_time - cycle_.phase_start_time) > duration;
268 QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
269 << ": HasPhaseLasted=" << result << ". elapsed:"
270 << (congestion_event.event_time - cycle_.phase_start_time)
271 << ", duration:" << duration;
272 return result;
273}
274
275bool Bbr2ProbeBwMode::IsTimeToProbeForRenoCoexistence(
276 double probe_wait_fraction,
277 const Bbr2CongestionEvent& /*congestion_event*/) const {
278 uint64_t rounds = Params().probe_bw_probe_max_rounds;
279 if (Params().probe_bw_probe_reno_gain > 0.0) {
wub29579902019-12-18 10:16:17 -0800280 QuicByteCount target_bytes_inflight = sender_->GetTargetBytesInflight();
281 uint64_t reno_rounds = Params().probe_bw_probe_reno_gain *
282 target_bytes_inflight / kDefaultTCPMSS;
QUICHE team7872c772019-07-23 10:19:37 -0400283 rounds = std::min(rounds, reno_rounds);
284 }
285 bool result = cycle_.rounds_since_probe >= (rounds * probe_wait_fraction);
286 QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
287 << ": IsTimeToProbeForRenoCoexistence=" << result
288 << ". rounds_since_probe:" << cycle_.rounds_since_probe
289 << ", rounds:" << rounds
290 << ", probe_wait_fraction:" << probe_wait_fraction;
291 return result;
292}
293
294void Bbr2ProbeBwMode::RaiseInflightHighSlope() {
295 DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_UP);
296 uint64_t growth_this_round = 1 << cycle_.probe_up_rounds;
297 // The number 30 below means |growth_this_round| is capped at 1G and the lower
298 // bound of |probe_up_bytes| is (practically) 1 mss, at this speed inflight_hi
299 // grows by approximately 1 packet per packet acked.
300 cycle_.probe_up_rounds = std::min<uint64_t>(cycle_.probe_up_rounds + 1, 30);
301 uint64_t probe_up_bytes = sender_->GetCongestionWindow() / growth_this_round;
302 cycle_.probe_up_bytes =
303 std::max<QuicByteCount>(probe_up_bytes, kDefaultTCPMSS);
304 QUIC_DVLOG(3) << sender_ << " Rasing inflight_hi slope. probe_up_rounds:"
305 << cycle_.probe_up_rounds
306 << ", probe_up_bytes:" << cycle_.probe_up_bytes;
307}
308
309void Bbr2ProbeBwMode::ProbeInflightHighUpward(
310 const Bbr2CongestionEvent& congestion_event) {
311 DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_UP);
312 if (!model_->IsCongestionWindowLimited(congestion_event)) {
313 QUIC_DVLOG(3) << sender_
wub3034a7d2019-09-27 13:33:14 -0700314 << " Raising inflight_hi early return: Not cwnd limited.";
QUICHE team7872c772019-07-23 10:19:37 -0400315 // Not fully utilizing cwnd, so can't safely grow.
316 return;
317 }
318
wub680d5632019-11-25 08:49:25 -0800319 if (congestion_event.prior_cwnd < model_->inflight_hi()) {
wub3034a7d2019-09-27 13:33:14 -0700320 QUIC_DVLOG(3)
321 << sender_
322 << " Raising inflight_hi early return: inflight_hi not fully used.";
323 // Not fully using inflight_hi, so don't grow it.
324 return;
325 }
326
QUICHE team7872c772019-07-23 10:19:37 -0400327 // Increase inflight_hi by the number of probe_up_bytes within probe_up_acked.
328 cycle_.probe_up_acked += congestion_event.bytes_acked;
329 if (cycle_.probe_up_acked >= cycle_.probe_up_bytes) {
330 uint64_t delta = cycle_.probe_up_acked / cycle_.probe_up_bytes;
331 cycle_.probe_up_acked -= delta * cycle_.probe_up_bytes;
wub3034a7d2019-09-27 13:33:14 -0700332 QuicByteCount new_inflight_hi =
333 model_->inflight_hi() + delta * kDefaultTCPMSS;
334 if (new_inflight_hi > model_->inflight_hi()) {
335 QUIC_DVLOG(3) << sender_ << " Raising inflight_hi from "
336 << model_->inflight_hi() << " to " << new_inflight_hi
337 << ". probe_up_bytes:" << cycle_.probe_up_bytes
338 << ", delta:" << delta
339 << ", (new)probe_up_acked:" << cycle_.probe_up_acked;
340
341 model_->set_inflight_hi(new_inflight_hi);
wub680d5632019-11-25 08:49:25 -0800342 } else {
wub3034a7d2019-09-27 13:33:14 -0700343 QUIC_BUG << "Not growing inflight_hi due to wrap around. Old value:"
344 << model_->inflight_hi() << ", new value:" << new_inflight_hi;
345 }
QUICHE team7872c772019-07-23 10:19:37 -0400346 }
347
348 if (congestion_event.end_of_round_trip) {
349 RaiseInflightHighSlope();
350 }
351}
352
353void Bbr2ProbeBwMode::UpdateProbeCruise(
354 const Bbr2CongestionEvent& congestion_event) {
355 DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_CRUISE);
356 MaybeAdaptUpperBounds(congestion_event);
357 DCHECK(!cycle_.is_sample_from_probing);
358
359 if (IsTimeToProbeBandwidth(congestion_event)) {
360 EnterProbeRefill(/*probe_up_rounds=*/0, congestion_event);
361 return;
362 }
363}
364
365void Bbr2ProbeBwMode::UpdateProbeRefill(
366 const Bbr2CongestionEvent& congestion_event) {
367 DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_REFILL);
368 MaybeAdaptUpperBounds(congestion_event);
369 DCHECK(!cycle_.is_sample_from_probing);
370
371 if (cycle_.rounds_in_phase > 0 && congestion_event.end_of_round_trip) {
372 EnterProbeUp(congestion_event);
373 return;
374 }
375}
376
377void Bbr2ProbeBwMode::UpdateProbeUp(
378 QuicByteCount prior_in_flight,
379 const Bbr2CongestionEvent& congestion_event) {
380 DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_UP);
381 if (MaybeAdaptUpperBounds(congestion_event) == ADAPTED_PROBED_TOO_HIGH) {
382 EnterProbeDown(/*probed_too_high=*/true, /*stopped_risky_probe=*/false,
383 congestion_event);
384 return;
385 }
386
387 // TODO(wub): Consider exit PROBE_UP after a certain number(e.g. 64) of RTTs.
388
389 ProbeInflightHighUpward(congestion_event);
390
391 bool is_risky = false;
392 bool is_queuing = false;
393 if (last_cycle_probed_too_high_ && prior_in_flight >= model_->inflight_hi()) {
394 is_risky = true;
395 QUIC_DVLOG(3) << sender_
396 << " Probe is too risky. last_cycle_probed_too_high_:"
397 << last_cycle_probed_too_high_
398 << ", prior_in_flight:" << prior_in_flight
399 << ", inflight_hi:" << model_->inflight_hi();
400 // TCP uses min_rtt instead of a full round:
401 // HasPhaseLasted(model_->MinRtt(), congestion_event)
402 } else if (cycle_.rounds_in_phase > 0) {
403 QuicByteCount bdp = model_->BDP(model_->MaxBandwidth());
404 QuicByteCount queuing_threshold =
405 (Params().probe_bw_probe_inflight_gain * bdp) + 2 * kDefaultTCPMSS;
406 is_queuing = prior_in_flight >= queuing_threshold;
407 QUIC_DVLOG(3) << sender_
408 << " Checking if building up a queue. prior_in_flight:"
409 << prior_in_flight << ", threshold:" << queuing_threshold
410 << ", is_queuing:" << is_queuing
411 << ", max_bw:" << model_->MaxBandwidth()
412 << ", min_rtt:" << model_->MinRtt();
413 }
414
415 if (is_risky || is_queuing) {
416 EnterProbeDown(/*probed_too_high=*/false, /*stopped_risky_probe=*/is_risky,
417 congestion_event);
418 }
419}
420
421void Bbr2ProbeBwMode::EnterProbeDown(
422 bool probed_too_high,
423 bool stopped_risky_probe,
424 const Bbr2CongestionEvent& congestion_event) {
425 QUIC_DVLOG(2) << sender_ << " Phase change: " << cycle_.phase << " ==> "
426 << CyclePhase::PROBE_DOWN << " after "
427 << congestion_event.event_time - cycle_.phase_start_time
428 << ", or " << cycle_.rounds_in_phase
429 << " rounds. probed_too_high:" << probed_too_high
430 << ", stopped_risky_probe:" << stopped_risky_probe << " @ "
431 << congestion_event.event_time;
432 last_cycle_probed_too_high_ = probed_too_high;
433 last_cycle_stopped_risky_probe_ = stopped_risky_probe;
434
435 cycle_.cycle_start_time = congestion_event.event_time;
436 cycle_.phase = CyclePhase::PROBE_DOWN;
437 cycle_.rounds_in_phase = 0;
438 cycle_.phase_start_time = congestion_event.event_time;
wub9528ecf2020-01-16 13:16:48 -0800439 ++sender_->connection_stats_->bbr_num_cycles;
QUICHE team7872c772019-07-23 10:19:37 -0400440
441 // Pick probe wait time.
442 cycle_.rounds_since_probe =
443 sender_->RandomUint64(Params().probe_bw_max_probe_rand_rounds);
444 cycle_.probe_wait_time =
445 Params().probe_bw_probe_base_duration +
446 QuicTime::Delta::FromMicroseconds(sender_->RandomUint64(
447 Params().probe_bw_probe_max_rand_duration.ToMicroseconds()));
448
449 cycle_.probe_up_bytes = std::numeric_limits<QuicByteCount>::max();
450 cycle_.has_advanced_max_bw = false;
451 model_->RestartRound();
452}
453
454void Bbr2ProbeBwMode::EnterProbeCruise(
455 const Bbr2CongestionEvent& congestion_event) {
456 if (cycle_.phase == CyclePhase::PROBE_DOWN) {
457 ExitProbeDown(congestion_event);
458 }
459 QUIC_DVLOG(2) << sender_ << " Phase change: " << cycle_.phase << " ==> "
460 << CyclePhase::PROBE_CRUISE << " after "
461 << congestion_event.event_time - cycle_.phase_start_time
462 << ", or " << cycle_.rounds_in_phase << " rounds. @ "
463 << congestion_event.event_time;
wub680d5632019-11-25 08:49:25 -0800464
465 model_->cap_inflight_lo(model_->inflight_hi());
QUICHE team7872c772019-07-23 10:19:37 -0400466 cycle_.phase = CyclePhase::PROBE_CRUISE;
467 cycle_.rounds_in_phase = 0;
468 cycle_.phase_start_time = congestion_event.event_time;
469 cycle_.is_sample_from_probing = false;
470}
471
472void Bbr2ProbeBwMode::EnterProbeRefill(
473 uint64_t probe_up_rounds,
474 const Bbr2CongestionEvent& congestion_event) {
475 if (cycle_.phase == CyclePhase::PROBE_DOWN) {
476 ExitProbeDown(congestion_event);
477 }
478 QUIC_DVLOG(2) << sender_ << " Phase change: " << cycle_.phase << " ==> "
479 << CyclePhase::PROBE_REFILL << " after "
480 << congestion_event.event_time - cycle_.phase_start_time
481 << ", or " << cycle_.rounds_in_phase
482 << " rounds. probe_up_rounds:" << probe_up_rounds << " @ "
483 << congestion_event.event_time;
484 cycle_.phase = CyclePhase::PROBE_REFILL;
485 cycle_.rounds_in_phase = 0;
486 cycle_.phase_start_time = congestion_event.event_time;
487 cycle_.is_sample_from_probing = false;
488 last_cycle_stopped_risky_probe_ = false;
489
490 model_->clear_bandwidth_lo();
491 model_->clear_inflight_lo();
492 cycle_.probe_up_rounds = probe_up_rounds;
493 cycle_.probe_up_acked = 0;
494 model_->RestartRound();
495}
496
497void Bbr2ProbeBwMode::EnterProbeUp(
498 const Bbr2CongestionEvent& congestion_event) {
499 DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_REFILL);
500 QUIC_DVLOG(2) << sender_ << " Phase change: " << cycle_.phase << " ==> "
501 << CyclePhase::PROBE_UP << " after "
502 << congestion_event.event_time - cycle_.phase_start_time
503 << ", or " << cycle_.rounds_in_phase << " rounds. @ "
504 << congestion_event.event_time;
505 cycle_.phase = CyclePhase::PROBE_UP;
506 cycle_.rounds_in_phase = 0;
507 cycle_.phase_start_time = congestion_event.event_time;
508 cycle_.is_sample_from_probing = true;
509 RaiseInflightHighSlope();
510
511 model_->RestartRound();
512}
513
514void Bbr2ProbeBwMode::ExitProbeDown(
515 const Bbr2CongestionEvent& /*congestion_event*/) {
516 DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_DOWN);
517 if (!cycle_.has_advanced_max_bw) {
518 QUIC_DVLOG(2) << sender_ << " Advancing max bw filter at end of cycle.";
519 model_->AdvanceMaxBandwidthFilter();
520 cycle_.has_advanced_max_bw = true;
521 }
522}
523
524// static
525const char* Bbr2ProbeBwMode::CyclePhaseToString(CyclePhase phase) {
526 switch (phase) {
527 case CyclePhase::PROBE_NOT_STARTED:
528 return "PROBE_NOT_STARTED";
529 case CyclePhase::PROBE_UP:
530 return "PROBE_UP";
531 case CyclePhase::PROBE_DOWN:
532 return "PROBE_DOWN";
533 case CyclePhase::PROBE_CRUISE:
534 return "PROBE_CRUISE";
535 case CyclePhase::PROBE_REFILL:
536 return "PROBE_REFILL";
537 default:
538 break;
539 }
540 return "<Invalid CyclePhase>";
541}
542
543std::ostream& operator<<(std::ostream& os,
544 const Bbr2ProbeBwMode::CyclePhase phase) {
545 return os << Bbr2ProbeBwMode::CyclePhaseToString(phase);
546}
547
548Bbr2ProbeBwMode::DebugState Bbr2ProbeBwMode::ExportDebugState() const {
549 DebugState s;
550 s.phase = cycle_.phase;
551 s.cycle_start_time = cycle_.cycle_start_time;
552 s.phase_start_time = cycle_.phase_start_time;
553 return s;
554}
555
556std::ostream& operator<<(std::ostream& os,
557 const Bbr2ProbeBwMode::DebugState& state) {
558 os << "[PROBE_BW] phase: " << state.phase << "\n";
559 os << "[PROBE_BW] cycle_start_time: " << state.cycle_start_time << "\n";
560 os << "[PROBE_BW] phase_start_time: " << state.phase_start_time << "\n";
561 return os;
562}
563
564const Bbr2Params& Bbr2ProbeBwMode::Params() const {
565 return sender_->Params();
566}
567
568float Bbr2ProbeBwMode::PacingGainForPhase(
569 Bbr2ProbeBwMode::CyclePhase phase) const {
570 if (phase == Bbr2ProbeBwMode::CyclePhase::PROBE_UP) {
571 return Params().probe_bw_probe_up_pacing_gain;
572 }
573 if (phase == Bbr2ProbeBwMode::CyclePhase::PROBE_DOWN) {
574 return Params().probe_bw_probe_down_pacing_gain;
575 }
576 return Params().probe_bw_default_pacing_gain;
577}
578
579} // namespace quic