If B2SL option is set in QUIC BBR2, when STARTUP exits due to loss, it will set the inflight_hi to the max of bdp and max_bytes_delivered_in_round.
Protected by FLAGS_quic_reloadable_flag_quic_bbr2_startup_loss_exit_use_max_delivered.
PiperOrigin-RevId: 338737724
Change-Id: I2e7923606b6210af7f198e10687d99132dc23b07
diff --git a/quic/core/congestion_control/bbr2_misc.cc b/quic/core/congestion_control/bbr2_misc.cc
index 9f6744e..3e3937a 100644
--- a/quic/core/congestion_control/bbr2_misc.cc
+++ b/quic/core/congestion_control/bbr2_misc.cc
@@ -148,6 +148,18 @@
loss_events_in_round_++;
}
+ if (GetQuicReloadableFlag(quic_bbr2_startup_loss_exit_use_max_delivered) &&
+ congestion_event->bytes_acked > 0 &&
+ congestion_event->last_packet_send_state.is_valid &&
+ total_bytes_acked() >
+ congestion_event->last_packet_send_state.total_bytes_acked) {
+ QuicByteCount bytes_delivered =
+ total_bytes_acked() -
+ congestion_event->last_packet_send_state.total_bytes_acked;
+ max_bytes_delivered_in_round_ =
+ std::max(max_bytes_delivered_in_round_, bytes_delivered);
+ }
+
// |bandwidth_latest_| and |inflight_latest_| only increased within a round.
if (sample.sample_max_bandwidth > bandwidth_latest_) {
bandwidth_latest_ = sample.sample_max_bandwidth;
@@ -282,6 +294,9 @@
void Bbr2NetworkModel::RestartRound() {
bytes_lost_in_round_ = 0;
loss_events_in_round_ = 0;
+ if (GetQuicReloadableFlag(quic_bbr2_startup_loss_exit_use_max_delivered)) {
+ max_bytes_delivered_in_round_ = 0;
+ }
round_trip_counter_.RestartRound();
}
diff --git a/quic/core/congestion_control/bbr2_misc.h b/quic/core/congestion_control/bbr2_misc.h
index 80a7423..f544273 100644
--- a/quic/core/congestion_control/bbr2_misc.h
+++ b/quic/core/congestion_control/bbr2_misc.h
@@ -192,6 +192,9 @@
// Can be enabled by connection optoin 'B2HI'.
bool limit_inflight_hi_by_cwnd = false;
+
+ // Can be enabled by connection option 'B2SL'.
+ bool startup_loss_exit_use_max_delivered_for_inflight_hi = false;
};
class QUIC_EXPORT_PRIVATE RoundTripCounter {
@@ -421,6 +424,10 @@
int64_t loss_events_in_round() const { return loss_events_in_round_; }
+ QuicByteCount max_bytes_delivered_in_round() const {
+ return max_bytes_delivered_in_round_;
+ }
+
QuicPacketNumber end_of_app_limited_phase() const {
return bandwidth_sampler_.end_of_app_limited_phase();
}
@@ -473,6 +480,12 @@
// Number of loss marking events in the current round.
int64_t loss_events_in_round_ = 0;
+ // A max of bytes delivered among all congestion events in the current round.
+ // A congestions event's bytes delivered is the total bytes acked between time
+ // Ts and Ta, which is the time when the largest acked packet(within the
+ // congestion event) was sent and acked, respectively.
+ QuicByteCount max_bytes_delivered_in_round_ = 0;
+
// Max bandwidth in the current round. Updated once per congestion event.
QuicBandwidth bandwidth_latest_ = QuicBandwidth::Zero();
// Max bandwidth of recent rounds. Updated once per round.
diff --git a/quic/core/congestion_control/bbr2_sender.cc b/quic/core/congestion_control/bbr2_sender.cc
index 4e57a9a..1c53323 100644
--- a/quic/core/congestion_control/bbr2_sender.cc
+++ b/quic/core/congestion_control/bbr2_sender.cc
@@ -163,6 +163,10 @@
quic_bbr2_no_exit_startup_on_loss_with_bw_growth);
params_.always_exit_startup_on_excess_loss = false;
}
+ if (GetQuicReloadableFlag(quic_bbr2_startup_loss_exit_use_max_delivered) &&
+ ContainsQuicTag(connection_options, kB2SL)) {
+ params_.startup_loss_exit_use_max_delivered_for_inflight_hi = true;
+ }
if (ContainsQuicTag(connection_options, kBSAO)) {
model_.EnableOverestimateAvoidance();
}
diff --git a/quic/core/congestion_control/bbr2_simulator_test.cc b/quic/core/congestion_control/bbr2_simulator_test.cc
index 5e5ffee..3485c11 100644
--- a/quic/core/congestion_control/bbr2_simulator_test.cc
+++ b/quic/core/congestion_control/bbr2_simulator_test.cc
@@ -734,6 +734,44 @@
EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
}
+// Test exiting STARTUP earlier upon loss due to loss when connection option
+// B2SL is used.
+TEST_F(Bbr2DefaultTopologyTest, ExitStartupDueToLossB2SL) {
+ SetConnectionOption(kB2SL);
+ DefaultTopologyParams params;
+ params.switch_queue_capacity_in_bdp = 0.5;
+ CreateNetwork(params);
+
+ // Run until the full bandwidth is reached and check how many rounds it was.
+ sender_endpoint_.AddBytesToTransfer(12 * 1024 * 1024);
+ QuicRoundTripCount max_bw_round = 0;
+ QuicBandwidth max_bw(QuicBandwidth::Zero());
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this, &max_bw, &max_bw_round]() {
+ if (max_bw < sender_->ExportDebugState().bandwidth_hi) {
+ max_bw = sender_->ExportDebugState().bandwidth_hi;
+ max_bw_round = sender_->ExportDebugState().round_trip_count;
+ }
+ return sender_->ExportDebugState().startup.full_bandwidth_reached;
+ },
+ QuicTime::Delta::FromSeconds(5));
+ ASSERT_TRUE(simulator_result);
+ EXPECT_EQ(Bbr2Mode::DRAIN, sender_->ExportDebugState().mode);
+ EXPECT_GE(2u, sender_->ExportDebugState().round_trip_count - max_bw_round);
+ EXPECT_EQ(
+ 1u,
+ sender_->ExportDebugState().startup.round_trips_without_bandwidth_growth);
+ EXPECT_NE(0u, sender_connection_stats().packets_lost);
+ EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
+
+ if (GetQuicReloadableFlag(quic_bbr2_startup_loss_exit_use_max_delivered)) {
+ EXPECT_GT(sender_->ExportDebugState().inflight_hi, 1.2f * params.BDP());
+ } else {
+ EXPECT_APPROX_EQ(sender_->ExportDebugState().inflight_hi, params.BDP(),
+ 0.1f);
+ }
+}
+
TEST_F(Bbr2DefaultTopologyTest, SenderPoliced) {
DefaultTopologyParams params;
params.sender_policer_params = TrafficPolicerParams();
diff --git a/quic/core/congestion_control/bbr2_startup.cc b/quic/core/congestion_control/bbr2_startup.cc
index 8947766..5c47adb 100644
--- a/quic/core/congestion_control/bbr2_startup.cc
+++ b/quic/core/congestion_control/bbr2_startup.cc
@@ -107,11 +107,21 @@
// At the end of a round trip. Check if loss is too high in this round.
if (model_->IsInflightTooHigh(congestion_event,
Params().startup_full_loss_count)) {
- const QuicByteCount bdp = model_->BDP(model_->MaxBandwidth());
- QUIC_DVLOG(3) << sender_
- << " Exiting STARTUP due to loss. inflight_hi:" << bdp;
+ QuicByteCount new_inflight_hi = model_->BDP(model_->MaxBandwidth());
+ if (Params().startup_loss_exit_use_max_delivered_for_inflight_hi) {
+ if (new_inflight_hi < model_->max_bytes_delivered_in_round()) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(
+ quic_bbr2_startup_loss_exit_use_max_delivered, 1, 2);
+ new_inflight_hi = model_->max_bytes_delivered_in_round();
+ } else {
+ QUIC_RELOADABLE_FLAG_COUNT_N(
+ quic_bbr2_startup_loss_exit_use_max_delivered, 2, 2);
+ }
+ }
+ QUIC_DVLOG(3) << sender_ << " Exiting STARTUP due to loss. inflight_hi:"
+ << new_inflight_hi;
// TODO(ianswett): Add a shared method to set inflight_hi in the model.
- model_->set_inflight_hi(bdp);
+ model_->set_inflight_hi(new_inflight_hi);
full_bandwidth_reached_ = true;
sender_->connection_stats_->bbr_exit_startup_due_to_loss = true;
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index 50fa103..248d37c 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -131,6 +131,10 @@
const QuicTag kB2HI = TAG('B', '2', 'H', 'I'); // Limit inflight_hi reduction
// based on CWND.
const QuicTag kB2HR = TAG('B', '2', 'H', 'R'); // 15% inflight_hi headroom.
+const QuicTag kB2SL = TAG('B', '2', 'S', 'L'); // When exiting STARTUP due to
+ // loss, set inflight_hi to the
+ // max of bdp and max bytes
+ // delivered in round.
const QuicTag kBSAO = TAG('B', 'S', 'A', 'O'); // Avoid Overestimation in
// Bandwidth Sampler with ack
// aggregation