blob: 887d553371f6c85bfaaa0a4304e611fc87c10e28 [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"
12
13namespace quic {
14
15void Bbr2ProbeBwMode::Enter(const Bbr2CongestionEvent& congestion_event) {
16 if (cycle_.phase == CyclePhase::PROBE_NOT_STARTED) {
17 // First time entering PROBE_BW. Start a new probing cycle.
18 EnterProbeDown(/*probed_too_high=*/false, /*stopped_risky_probe=*/false,
19 congestion_event);
20 } else {
21 // Transitioning from PROBE_RTT to PROBE_BW. Re-enter the last phase before
22 // PROBE_RTT.
23 DCHECK(cycle_.phase == CyclePhase::PROBE_CRUISE ||
24 cycle_.phase == CyclePhase::PROBE_REFILL);
25 cycle_.cycle_start_time = congestion_event.event_time;
26 if (cycle_.phase == CyclePhase::PROBE_CRUISE) {
27 EnterProbeCruise(congestion_event);
28 } else if (cycle_.phase == CyclePhase::PROBE_REFILL) {
29 EnterProbeRefill(cycle_.probe_up_rounds, congestion_event);
30 }
31 }
32}
33
34Bbr2Mode Bbr2ProbeBwMode::OnCongestionEvent(
35 QuicByteCount prior_in_flight,
36 QuicTime event_time,
37 const AckedPacketVector& /*acked_packets*/,
38 const LostPacketVector& /*lost_packets*/,
39 const Bbr2CongestionEvent& congestion_event) {
40 DCHECK_NE(cycle_.phase, CyclePhase::PROBE_NOT_STARTED);
41
42 if (congestion_event.end_of_round_trip) {
43 if (cycle_.cycle_start_time != event_time) {
44 ++cycle_.rounds_since_probe;
45 }
46 if (cycle_.phase_start_time != event_time) {
47 ++cycle_.rounds_in_phase;
48 }
49 }
50
51 if (cycle_.phase == CyclePhase::PROBE_UP) {
52 UpdateProbeUp(prior_in_flight, congestion_event);
53 } else if (cycle_.phase == CyclePhase::PROBE_DOWN) {
54 UpdateProbeDown(prior_in_flight, congestion_event);
55 // Maybe transition to PROBE_RTT at the end of this cycle.
56 if (cycle_.phase != CyclePhase::PROBE_DOWN &&
57 model_->MaybeExpireMinRtt(congestion_event)) {
58 return Bbr2Mode::PROBE_RTT;
59 }
60 } else if (cycle_.phase == CyclePhase::PROBE_CRUISE) {
61 UpdateProbeCruise(congestion_event);
62 } else if (cycle_.phase == CyclePhase::PROBE_REFILL) {
63 UpdateProbeRefill(congestion_event);
64 }
65
66 model_->set_pacing_gain(PacingGainForPhase(cycle_.phase));
67 model_->set_cwnd_gain(Params().probe_bw_cwnd_gain);
68
69 return Bbr2Mode::PROBE_BW;
70}
71
72Limits<QuicByteCount> Bbr2ProbeBwMode::GetCwndLimits() const {
73 if (cycle_.phase == CyclePhase::PROBE_CRUISE) {
74 return NoGreaterThan(
75 std::min(model_->inflight_lo(), model_->inflight_hi_with_headroom()));
76 }
77
78 return NoGreaterThan(std::min(model_->inflight_lo(), model_->inflight_hi()));
79}
80
81bool Bbr2ProbeBwMode::IsProbingForBandwidth() const {
82 return cycle_.phase == CyclePhase::PROBE_REFILL ||
83 cycle_.phase == CyclePhase::PROBE_UP;
84}
85
86void Bbr2ProbeBwMode::UpdateProbeDown(
87 QuicByteCount prior_in_flight,
88 const Bbr2CongestionEvent& congestion_event) {
89 DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_DOWN);
90
91 if (cycle_.rounds_in_phase == 1 && congestion_event.end_of_round_trip) {
92 cycle_.is_sample_from_probing = false;
93
94 if (!congestion_event.last_sample_is_app_limited) {
95 QUIC_DVLOG(2)
96 << sender_
97 << " Advancing max bw filter after one round in PROBE_DOWN.";
98 model_->AdvanceMaxBandwidthFilter();
99 cycle_.has_advanced_max_bw = true;
100 }
101
102 if (last_cycle_stopped_risky_probe_ && !last_cycle_probed_too_high_) {
103 EnterProbeRefill(/*probe_up_rounds=*/0, congestion_event);
104 return;
105 }
106 }
107
108 MaybeAdaptUpperBounds(congestion_event);
109
110 if (IsTimeToProbeBandwidth(congestion_event)) {
111 EnterProbeRefill(/*probe_up_rounds=*/0, congestion_event);
112 return;
113 }
114
115 if (HasStayedLongEnoughInProbeDown(congestion_event)) {
116 QUIC_DVLOG(3) << sender_ << " Proportional time based PROBE_DOWN exit";
117 EnterProbeCruise(congestion_event);
118 return;
119 }
120
121 const QuicByteCount inflight_with_headroom =
122 model_->inflight_hi_with_headroom();
123 QUIC_DVLOG(3)
124 << sender_
125 << " Checking if have enough inflight headroom. prior_in_flight:"
126 << prior_in_flight
127 << ", inflight_with_headroom:" << inflight_with_headroom;
128 if (prior_in_flight > inflight_with_headroom) {
129 // Stay in PROBE_DOWN.
130 return;
131 }
132
133 // Transition to PROBE_CRUISE iff we've drained to target.
134 QuicByteCount bdp = model_->BDP(model_->MaxBandwidth());
135 QUIC_DVLOG(3) << sender_ << " Checking if drained to target. prior_in_flight:"
136 << prior_in_flight << ", bdp:" << bdp;
137 if (prior_in_flight < bdp) {
138 EnterProbeCruise(congestion_event);
139 }
140}
141
142Bbr2ProbeBwMode::AdaptUpperBoundsResult Bbr2ProbeBwMode::MaybeAdaptUpperBounds(
143 const Bbr2CongestionEvent& congestion_event) {
144 const SendTimeState& send_state = SendStateOfLargestPacket(congestion_event);
145 if (!send_state.is_valid) {
146 QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
147 << ": NOT_ADAPTED_INVALID_SAMPLE";
148 return NOT_ADAPTED_INVALID_SAMPLE;
149 }
150
151 if (model_->IsInflightTooHigh(congestion_event)) {
152 if (cycle_.is_sample_from_probing) {
153 cycle_.is_sample_from_probing = false;
154
155 if (!send_state.is_app_limited) {
156 QuicByteCount inflight_at_send = BytesInFlight(send_state);
157 model_->set_inflight_hi(inflight_at_send);
158 }
159
160 QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
161 << ": ADAPTED_PROBED_TOO_HIGH";
162 return ADAPTED_PROBED_TOO_HIGH;
163 }
164 return ADAPTED_OK;
165 }
166
167 if (model_->inflight_hi() == model_->inflight_hi_default()) {
168 QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
169 << ": NOT_ADAPTED_INFLIGHT_HIGH_NOT_SET";
170 return NOT_ADAPTED_INFLIGHT_HIGH_NOT_SET;
171 }
172
173 const QuicByteCount inflight_at_send = BytesInFlight(send_state);
174
175 // Raise the upper bound for inflight.
176 if (inflight_at_send > model_->inflight_hi()) {
177 QUIC_DVLOG(3)
178 << sender_ << " " << cycle_.phase
179 << ": Adapting inflight_hi from inflight_at_send. inflight_at_send:"
180 << inflight_at_send << ", old inflight_hi:" << model_->inflight_hi();
181 model_->set_inflight_hi(inflight_at_send);
182 }
183
184 return ADAPTED_OK;
185}
186
187bool Bbr2ProbeBwMode::IsTimeToProbeBandwidth(
188 const Bbr2CongestionEvent& congestion_event) const {
189 return HasCycleLasted(cycle_.probe_wait_time, congestion_event) ||
190 IsTimeToProbeForRenoCoexistence(1.0, congestion_event);
191}
192
193// QUIC only. Used to prevent a Bbr2 flow from staying in PROBE_DOWN for too
194// long, as seen in some multi-sender simulator tests.
195bool Bbr2ProbeBwMode::HasStayedLongEnoughInProbeDown(
196 const Bbr2CongestionEvent& congestion_event) const {
197 // The amount of time to stay in PROBE_DOWN, as a fraction of probe wait time.
198 const double kProbeWaitFraction = 0.2;
199 return HasCycleLasted(cycle_.probe_wait_time * kProbeWaitFraction,
200 congestion_event) ||
201 IsTimeToProbeForRenoCoexistence(kProbeWaitFraction, congestion_event);
202}
203
204bool Bbr2ProbeBwMode::HasCycleLasted(
205 QuicTime::Delta duration,
206 const Bbr2CongestionEvent& congestion_event) const {
207 bool result =
208 (congestion_event.event_time - cycle_.cycle_start_time) > duration;
209 QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
210 << ": HasCycleLasted=" << result << ". elapsed:"
211 << (congestion_event.event_time - cycle_.cycle_start_time)
212 << ", duration:" << duration;
213 return result;
214}
215
216bool Bbr2ProbeBwMode::HasPhaseLasted(
217 QuicTime::Delta duration,
218 const Bbr2CongestionEvent& congestion_event) const {
219 bool result =
220 (congestion_event.event_time - cycle_.phase_start_time) > duration;
221 QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
222 << ": HasPhaseLasted=" << result << ". elapsed:"
223 << (congestion_event.event_time - cycle_.phase_start_time)
224 << ", duration:" << duration;
225 return result;
226}
227
228bool Bbr2ProbeBwMode::IsTimeToProbeForRenoCoexistence(
229 double probe_wait_fraction,
230 const Bbr2CongestionEvent& /*congestion_event*/) const {
231 uint64_t rounds = Params().probe_bw_probe_max_rounds;
232 if (Params().probe_bw_probe_reno_gain > 0.0) {
233 QuicByteCount bdp = model_->BDP(model_->BandwidthEstimate());
234 QuicByteCount inflight_bytes =
235 std::min(bdp, sender_->GetCongestionWindow());
236 uint64_t reno_rounds =
237 Params().probe_bw_probe_reno_gain * inflight_bytes / kDefaultTCPMSS;
238 rounds = std::min(rounds, reno_rounds);
239 }
240 bool result = cycle_.rounds_since_probe >= (rounds * probe_wait_fraction);
241 QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
242 << ": IsTimeToProbeForRenoCoexistence=" << result
243 << ". rounds_since_probe:" << cycle_.rounds_since_probe
244 << ", rounds:" << rounds
245 << ", probe_wait_fraction:" << probe_wait_fraction;
246 return result;
247}
248
249void Bbr2ProbeBwMode::RaiseInflightHighSlope() {
250 DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_UP);
251 uint64_t growth_this_round = 1 << cycle_.probe_up_rounds;
252 // The number 30 below means |growth_this_round| is capped at 1G and the lower
253 // bound of |probe_up_bytes| is (practically) 1 mss, at this speed inflight_hi
254 // grows by approximately 1 packet per packet acked.
255 cycle_.probe_up_rounds = std::min<uint64_t>(cycle_.probe_up_rounds + 1, 30);
256 uint64_t probe_up_bytes = sender_->GetCongestionWindow() / growth_this_round;
257 cycle_.probe_up_bytes =
258 std::max<QuicByteCount>(probe_up_bytes, kDefaultTCPMSS);
259 QUIC_DVLOG(3) << sender_ << " Rasing inflight_hi slope. probe_up_rounds:"
260 << cycle_.probe_up_rounds
261 << ", probe_up_bytes:" << cycle_.probe_up_bytes;
262}
263
264void Bbr2ProbeBwMode::ProbeInflightHighUpward(
265 const Bbr2CongestionEvent& congestion_event) {
266 DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_UP);
267 if (!model_->IsCongestionWindowLimited(congestion_event)) {
268 QUIC_DVLOG(3) << sender_
wub3034a7d2019-09-27 13:33:14 -0700269 << " Raising inflight_hi early return: Not cwnd limited.";
QUICHE team7872c772019-07-23 10:19:37 -0400270 // Not fully utilizing cwnd, so can't safely grow.
271 return;
272 }
273
wub3034a7d2019-09-27 13:33:14 -0700274 if (GetQuicReloadableFlag(quic_bbr2_fix_inflight_bounds) &&
275 congestion_event.prior_cwnd < model_->inflight_hi()) {
276 QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_fix_inflight_bounds, 1, 2);
277 QUIC_DVLOG(3)
278 << sender_
279 << " Raising inflight_hi early return: inflight_hi not fully used.";
280 // Not fully using inflight_hi, so don't grow it.
281 return;
282 }
283
QUICHE team7872c772019-07-23 10:19:37 -0400284 // Increase inflight_hi by the number of probe_up_bytes within probe_up_acked.
285 cycle_.probe_up_acked += congestion_event.bytes_acked;
286 if (cycle_.probe_up_acked >= cycle_.probe_up_bytes) {
287 uint64_t delta = cycle_.probe_up_acked / cycle_.probe_up_bytes;
288 cycle_.probe_up_acked -= delta * cycle_.probe_up_bytes;
wub3034a7d2019-09-27 13:33:14 -0700289 QuicByteCount new_inflight_hi =
290 model_->inflight_hi() + delta * kDefaultTCPMSS;
291 if (new_inflight_hi > model_->inflight_hi()) {
292 QUIC_DVLOG(3) << sender_ << " Raising inflight_hi from "
293 << model_->inflight_hi() << " to " << new_inflight_hi
294 << ". probe_up_bytes:" << cycle_.probe_up_bytes
295 << ", delta:" << delta
296 << ", (new)probe_up_acked:" << cycle_.probe_up_acked;
297
298 model_->set_inflight_hi(new_inflight_hi);
wub8886b8c2019-09-29 17:54:09 -0700299 } else if (GetQuicReloadableFlag(quic_bbr2_fix_inflight_bounds)) {
wub3034a7d2019-09-27 13:33:14 -0700300 QUIC_BUG << "Not growing inflight_hi due to wrap around. Old value:"
301 << model_->inflight_hi() << ", new value:" << new_inflight_hi;
302 }
QUICHE team7872c772019-07-23 10:19:37 -0400303 }
304
305 if (congestion_event.end_of_round_trip) {
306 RaiseInflightHighSlope();
307 }
308}
309
310void Bbr2ProbeBwMode::UpdateProbeCruise(
311 const Bbr2CongestionEvent& congestion_event) {
312 DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_CRUISE);
313 MaybeAdaptUpperBounds(congestion_event);
314 DCHECK(!cycle_.is_sample_from_probing);
315
316 if (IsTimeToProbeBandwidth(congestion_event)) {
317 EnterProbeRefill(/*probe_up_rounds=*/0, congestion_event);
318 return;
319 }
320}
321
322void Bbr2ProbeBwMode::UpdateProbeRefill(
323 const Bbr2CongestionEvent& congestion_event) {
324 DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_REFILL);
325 MaybeAdaptUpperBounds(congestion_event);
326 DCHECK(!cycle_.is_sample_from_probing);
327
328 if (cycle_.rounds_in_phase > 0 && congestion_event.end_of_round_trip) {
329 EnterProbeUp(congestion_event);
330 return;
331 }
332}
333
334void Bbr2ProbeBwMode::UpdateProbeUp(
335 QuicByteCount prior_in_flight,
336 const Bbr2CongestionEvent& congestion_event) {
337 DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_UP);
338 if (MaybeAdaptUpperBounds(congestion_event) == ADAPTED_PROBED_TOO_HIGH) {
339 EnterProbeDown(/*probed_too_high=*/true, /*stopped_risky_probe=*/false,
340 congestion_event);
341 return;
342 }
343
344 // TODO(wub): Consider exit PROBE_UP after a certain number(e.g. 64) of RTTs.
345
346 ProbeInflightHighUpward(congestion_event);
347
348 bool is_risky = false;
349 bool is_queuing = false;
350 if (last_cycle_probed_too_high_ && prior_in_flight >= model_->inflight_hi()) {
351 is_risky = true;
352 QUIC_DVLOG(3) << sender_
353 << " Probe is too risky. last_cycle_probed_too_high_:"
354 << last_cycle_probed_too_high_
355 << ", prior_in_flight:" << prior_in_flight
356 << ", inflight_hi:" << model_->inflight_hi();
357 // TCP uses min_rtt instead of a full round:
358 // HasPhaseLasted(model_->MinRtt(), congestion_event)
359 } else if (cycle_.rounds_in_phase > 0) {
360 QuicByteCount bdp = model_->BDP(model_->MaxBandwidth());
361 QuicByteCount queuing_threshold =
362 (Params().probe_bw_probe_inflight_gain * bdp) + 2 * kDefaultTCPMSS;
363 is_queuing = prior_in_flight >= queuing_threshold;
364 QUIC_DVLOG(3) << sender_
365 << " Checking if building up a queue. prior_in_flight:"
366 << prior_in_flight << ", threshold:" << queuing_threshold
367 << ", is_queuing:" << is_queuing
368 << ", max_bw:" << model_->MaxBandwidth()
369 << ", min_rtt:" << model_->MinRtt();
370 }
371
372 if (is_risky || is_queuing) {
373 EnterProbeDown(/*probed_too_high=*/false, /*stopped_risky_probe=*/is_risky,
374 congestion_event);
375 }
376}
377
378void Bbr2ProbeBwMode::EnterProbeDown(
379 bool probed_too_high,
380 bool stopped_risky_probe,
381 const Bbr2CongestionEvent& congestion_event) {
382 QUIC_DVLOG(2) << sender_ << " Phase change: " << cycle_.phase << " ==> "
383 << CyclePhase::PROBE_DOWN << " after "
384 << congestion_event.event_time - cycle_.phase_start_time
385 << ", or " << cycle_.rounds_in_phase
386 << " rounds. probed_too_high:" << probed_too_high
387 << ", stopped_risky_probe:" << stopped_risky_probe << " @ "
388 << congestion_event.event_time;
389 last_cycle_probed_too_high_ = probed_too_high;
390 last_cycle_stopped_risky_probe_ = stopped_risky_probe;
391
392 cycle_.cycle_start_time = congestion_event.event_time;
393 cycle_.phase = CyclePhase::PROBE_DOWN;
394 cycle_.rounds_in_phase = 0;
395 cycle_.phase_start_time = congestion_event.event_time;
396
397 // Pick probe wait time.
398 cycle_.rounds_since_probe =
399 sender_->RandomUint64(Params().probe_bw_max_probe_rand_rounds);
400 cycle_.probe_wait_time =
401 Params().probe_bw_probe_base_duration +
402 QuicTime::Delta::FromMicroseconds(sender_->RandomUint64(
403 Params().probe_bw_probe_max_rand_duration.ToMicroseconds()));
404
405 cycle_.probe_up_bytes = std::numeric_limits<QuicByteCount>::max();
406 cycle_.has_advanced_max_bw = false;
407 model_->RestartRound();
408}
409
410void Bbr2ProbeBwMode::EnterProbeCruise(
411 const Bbr2CongestionEvent& congestion_event) {
412 if (cycle_.phase == CyclePhase::PROBE_DOWN) {
413 ExitProbeDown(congestion_event);
414 }
415 QUIC_DVLOG(2) << sender_ << " Phase change: " << cycle_.phase << " ==> "
416 << CyclePhase::PROBE_CRUISE << " after "
417 << congestion_event.event_time - cycle_.phase_start_time
418 << ", or " << cycle_.rounds_in_phase << " rounds. @ "
419 << congestion_event.event_time;
wub3034a7d2019-09-27 13:33:14 -0700420 if (GetQuicReloadableFlag(quic_bbr2_fix_inflight_bounds)) {
421 QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_fix_inflight_bounds, 2, 2);
422 model_->cap_inflight_lo(model_->inflight_hi());
423 }
QUICHE team7872c772019-07-23 10:19:37 -0400424 cycle_.phase = CyclePhase::PROBE_CRUISE;
425 cycle_.rounds_in_phase = 0;
426 cycle_.phase_start_time = congestion_event.event_time;
427 cycle_.is_sample_from_probing = false;
428}
429
430void Bbr2ProbeBwMode::EnterProbeRefill(
431 uint64_t probe_up_rounds,
432 const Bbr2CongestionEvent& congestion_event) {
433 if (cycle_.phase == CyclePhase::PROBE_DOWN) {
434 ExitProbeDown(congestion_event);
435 }
436 QUIC_DVLOG(2) << sender_ << " Phase change: " << cycle_.phase << " ==> "
437 << CyclePhase::PROBE_REFILL << " after "
438 << congestion_event.event_time - cycle_.phase_start_time
439 << ", or " << cycle_.rounds_in_phase
440 << " rounds. probe_up_rounds:" << probe_up_rounds << " @ "
441 << congestion_event.event_time;
442 cycle_.phase = CyclePhase::PROBE_REFILL;
443 cycle_.rounds_in_phase = 0;
444 cycle_.phase_start_time = congestion_event.event_time;
445 cycle_.is_sample_from_probing = false;
446 last_cycle_stopped_risky_probe_ = false;
447
448 model_->clear_bandwidth_lo();
449 model_->clear_inflight_lo();
450 cycle_.probe_up_rounds = probe_up_rounds;
451 cycle_.probe_up_acked = 0;
452 model_->RestartRound();
453}
454
455void Bbr2ProbeBwMode::EnterProbeUp(
456 const Bbr2CongestionEvent& congestion_event) {
457 DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_REFILL);
458 QUIC_DVLOG(2) << sender_ << " Phase change: " << cycle_.phase << " ==> "
459 << CyclePhase::PROBE_UP << " after "
460 << congestion_event.event_time - cycle_.phase_start_time
461 << ", or " << cycle_.rounds_in_phase << " rounds. @ "
462 << congestion_event.event_time;
463 cycle_.phase = CyclePhase::PROBE_UP;
464 cycle_.rounds_in_phase = 0;
465 cycle_.phase_start_time = congestion_event.event_time;
466 cycle_.is_sample_from_probing = true;
467 RaiseInflightHighSlope();
468
469 model_->RestartRound();
470}
471
472void Bbr2ProbeBwMode::ExitProbeDown(
473 const Bbr2CongestionEvent& /*congestion_event*/) {
474 DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_DOWN);
475 if (!cycle_.has_advanced_max_bw) {
476 QUIC_DVLOG(2) << sender_ << " Advancing max bw filter at end of cycle.";
477 model_->AdvanceMaxBandwidthFilter();
478 cycle_.has_advanced_max_bw = true;
479 }
480}
481
482// static
483const char* Bbr2ProbeBwMode::CyclePhaseToString(CyclePhase phase) {
484 switch (phase) {
485 case CyclePhase::PROBE_NOT_STARTED:
486 return "PROBE_NOT_STARTED";
487 case CyclePhase::PROBE_UP:
488 return "PROBE_UP";
489 case CyclePhase::PROBE_DOWN:
490 return "PROBE_DOWN";
491 case CyclePhase::PROBE_CRUISE:
492 return "PROBE_CRUISE";
493 case CyclePhase::PROBE_REFILL:
494 return "PROBE_REFILL";
495 default:
496 break;
497 }
498 return "<Invalid CyclePhase>";
499}
500
501std::ostream& operator<<(std::ostream& os,
502 const Bbr2ProbeBwMode::CyclePhase phase) {
503 return os << Bbr2ProbeBwMode::CyclePhaseToString(phase);
504}
505
506Bbr2ProbeBwMode::DebugState Bbr2ProbeBwMode::ExportDebugState() const {
507 DebugState s;
508 s.phase = cycle_.phase;
509 s.cycle_start_time = cycle_.cycle_start_time;
510 s.phase_start_time = cycle_.phase_start_time;
511 return s;
512}
513
514std::ostream& operator<<(std::ostream& os,
515 const Bbr2ProbeBwMode::DebugState& state) {
516 os << "[PROBE_BW] phase: " << state.phase << "\n";
517 os << "[PROBE_BW] cycle_start_time: " << state.cycle_start_time << "\n";
518 os << "[PROBE_BW] phase_start_time: " << state.phase_start_time << "\n";
519 return os;
520}
521
522const Bbr2Params& Bbr2ProbeBwMode::Params() const {
523 return sender_->Params();
524}
525
526float Bbr2ProbeBwMode::PacingGainForPhase(
527 Bbr2ProbeBwMode::CyclePhase phase) const {
528 if (phase == Bbr2ProbeBwMode::CyclePhase::PROBE_UP) {
529 return Params().probe_bw_probe_up_pacing_gain;
530 }
531 if (phase == Bbr2ProbeBwMode::CyclePhase::PROBE_DOWN) {
532 return Params().probe_bw_probe_down_pacing_gain;
533 }
534 return Params().probe_bw_default_pacing_gain;
535}
536
537} // namespace quic