In BBRv2 STARTUP, exit after the same number of loss events(2) as PROBE_UP when the B206 connection option is supplied.

Protected by quic_reloadable_flag_quic_bbr2_startup_probe_up_loss_events.

PiperOrigin-RevId: 400785473
diff --git a/quic/core/congestion_control/bbr2_sender.cc b/quic/core/congestion_control/bbr2_sender.cc
index 6938f6d..bddb06a 100644
--- a/quic/core/congestion_control/bbr2_sender.cc
+++ b/quic/core/congestion_control/bbr2_sender.cc
@@ -200,6 +200,12 @@
     QUIC_RELOADABLE_FLAG_COUNT(quic_bbr2_ignore_inflight_hi_in_probe_up);
     params_.probe_up_ignore_inflight_hi = true;
   }
+
+  if (GetQuicReloadableFlag(quic_bbr2_startup_probe_up_loss_events) &&
+      ContainsQuicTag(connection_options, kB206)) {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_bbr2_startup_probe_up_loss_events);
+    params_.startup_full_loss_count = params_.probe_bw_full_loss_count;
+  }
 }
 
 Limits<QuicByteCount> Bbr2Sender::GetCwndLimitsByMode() const {
diff --git a/quic/core/congestion_control/bbr2_simulator_test.cc b/quic/core/congestion_control/bbr2_simulator_test.cc
index 9c2cca1..18a9829 100644
--- a/quic/core/congestion_control/bbr2_simulator_test.cc
+++ b/quic/core/congestion_control/bbr2_simulator_test.cc
@@ -463,6 +463,26 @@
   EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f);
 }
 
+TEST_F(Bbr2DefaultTopologyTest, SimpleTransferB206) {
+  SetQuicReloadableFlag(quic_bbr2_startup_probe_up_loss_events, true);
+  SetConnectionOption(kB206);
+  DefaultTopologyParams params;
+  CreateNetwork(params);
+
+  // Transfer 12MB.
+  DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
+  EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
+
+  EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
+                   sender_->ExportDebugState().bandwidth_hi, 0.01f);
+
+  EXPECT_LE(sender_loss_rate_in_packets(), 0.05);
+  // The margin here is high, because the aggregation greatly increases
+  // smoothed rtt.
+  EXPECT_GE(params.RTT() * 4, rtt_stats()->smoothed_rtt());
+  EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f);
+}
+
 TEST_F(Bbr2DefaultTopologyTest, SimpleTransferBBRB) {
   SetQuicReloadableFlag(quic_bbr_use_send_rate_in_max_ack_height_tracker, true);
   SetConnectionOption(kBBRB);
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index 2afa363..ed6bdae 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -165,6 +165,7 @@
                                                  // inflight dips below 1.25*BW.
 const QuicTag kB203 = TAG('B', '2', '0', '3');   // Ignore inflight_hi until
                                                  // PROBE_UP is exited.
+const QuicTag kB206 = TAG('B', '2', '0', '6');   // Exit STARTUP after 2 losses.
 const QuicTag kNTLP = TAG('N', 'T', 'L', 'P');   // No tail loss probe
 const QuicTag k1TLP = TAG('1', 'T', 'L', 'P');   // 1 tail loss probe
 const QuicTag k1RTO = TAG('1', 'R', 'T', 'O');   // Send 1 packet upon RTO
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 5773ec5..d7e9676 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -119,6 +119,8 @@
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_match_ietf_reset_code, false)
 // When the STMP connection option is sent by the client, timestamps in the QUIC ACK frame are sent and processed.
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_timestamps, false)
+// When the flag is true, exit STARTUP after the same number of loss events as PROBE_UP.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_startup_probe_up_loss_events, true)
 // When true, QuicDispatcher will silently drop QUIC packets that have invalid flags.
 QUIC_FLAG(FLAGS_quic_restart_flag_quic_drop_invalid_flags, true)
 // When true, QuicDispatcher will silently drop incoming packets whose UDP source port is on the blocklist.