Add QUIC connection option B2RC to disable reno coexistence for BBR2.

Protected by FLAGS_quic_reloadable_flag_quic_bbr2_disable_reno_coexistence.

PiperOrigin-RevId: 345675835
Change-Id: I976a81192b6abdc4b16463d970e43863a9f745ea
diff --git a/quic/core/congestion_control/bbr2_misc.h b/quic/core/congestion_control/bbr2_misc.h
index 2134bd6..f81d3cd 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 option 'B2DL'.
   bool use_bytes_delivered_for_inflight_hi = false;
+
+  // Can be disabled by connection option 'B2RC'.
+  bool enable_reno_coexistence = true;
 };
 
 class QUIC_EXPORT_PRIVATE RoundTripCounter {
diff --git a/quic/core/congestion_control/bbr2_probe_bw.cc b/quic/core/congestion_control/bbr2_probe_bw.cc
index 1b01d9d..5f38005 100644
--- a/quic/core/congestion_control/bbr2_probe_bw.cc
+++ b/quic/core/congestion_control/bbr2_probe_bw.cc
@@ -325,6 +325,10 @@
 bool Bbr2ProbeBwMode::IsTimeToProbeForRenoCoexistence(
     double probe_wait_fraction,
     const Bbr2CongestionEvent& /*congestion_event*/) const {
+  if (!Params().enable_reno_coexistence) {
+    return false;
+  }
+
   uint64_t rounds = Params().probe_bw_probe_max_rounds;
   if (Params().probe_bw_probe_reno_gain > 0.0) {
     QuicByteCount target_bytes_inflight = sender_->GetTargetBytesInflight();
diff --git a/quic/core/congestion_control/bbr2_sender.cc b/quic/core/congestion_control/bbr2_sender.cc
index 2545fa4..5d672d1 100644
--- a/quic/core/congestion_control/bbr2_sender.cc
+++ b/quic/core/congestion_control/bbr2_sender.cc
@@ -150,6 +150,11 @@
     QUIC_RELOADABLE_FLAG_COUNT(quic_bbr2_use_bytes_delivered);
     params_.use_bytes_delivered_for_inflight_hi = true;
   }
+  if (GetQuicReloadableFlag(quic_bbr2_disable_reno_coexistence) &&
+      ContainsQuicTag(connection_options, kB2RC)) {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_bbr2_disable_reno_coexistence);
+    params_.enable_reno_coexistence = false;
+  }
   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 b7fef49..ec7eeeb 100644
--- a/quic/core/congestion_control/bbr2_simulator_test.cc
+++ b/quic/core/congestion_control/bbr2_simulator_test.cc
@@ -432,6 +432,25 @@
   EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f);
 }
 
+TEST_F(Bbr2DefaultTopologyTest, SimpleTransferB2RC) {
+  SetConnectionOption(kB2RC);
+  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, SimpleTransferSmallBuffer) {
   DefaultTopologyParams params;
   params.switch_queue_capacity_in_bdp = 0.5;
@@ -1245,7 +1264,7 @@
     receiver_multiplexer_ =
         std::make_unique<simulator::QuicEndpointMultiplexer>(
             "Receiver multiplexer", receiver_endpoint_pointers);
-    sender_1_ = SetupBbr2Sender(sender_endpoints_[0].get());
+    sender_0_ = SetupBbr2Sender(sender_endpoints_[0].get());
   }
 
   ~Bbr2MultiSenderTest() {
@@ -1311,6 +1330,14 @@
     return sender;
   }
 
+  void SetConnectionOption(SendAlgorithmInterface* sender, QuicTag option) {
+    QuicConfig config;
+    QuicTagVector options;
+    options.push_back(option);
+    QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+    sender->SetFromConfig(config, Perspective::IS_SERVER);
+  }
+
   void CreateNetwork(const MultiSenderTopologyParams& params) {
     QUIC_LOG(INFO) << "CreateNetwork with parameters: " << params.ToString();
     switch_ = std::make_unique<simulator::Switch>(&simulator_, "Switch",
@@ -1344,7 +1371,7 @@
   std::vector<std::unique_ptr<simulator::QuicEndpoint>> sender_endpoints_;
   std::vector<std::unique_ptr<simulator::QuicEndpoint>> receiver_endpoints_;
   std::unique_ptr<simulator::QuicEndpointMultiplexer> receiver_multiplexer_;
-  Bbr2Sender* sender_1_;
+  Bbr2Sender* sender_0_;
 
   std::unique_ptr<simulator::Switch> switch_;
   std::vector<std::unique_ptr<simulator::SymmetricLink>> network_links_;
@@ -1503,7 +1530,39 @@
   MultiSenderTopologyParams params;
   CreateNetwork(params);
 
-  const QuicByteCount transfer_size = 50 * 1024 * 1024;
+  const QuicByteCount transfer_size = 10 * 1024 * 1024;
+  const QuicTime::Delta transfer_time =
+      params.BottleneckBandwidth().TransferTime(transfer_size);
+  QUIC_LOG(INFO) << "Single flow transfer time: " << transfer_time;
+
+  // Transfer 10% of data in first transfer.
+  sender_endpoints_[0]->AddBytesToTransfer(transfer_size);
+  bool simulator_result = simulator_.RunUntilOrTimeout(
+      [this]() {
+        return receiver_endpoints_[0]->bytes_received() >= 0.1 * transfer_size;
+      },
+      transfer_time);
+  ASSERT_TRUE(simulator_result);
+
+  // Start the second transfer and wait until both finish.
+  sender_endpoints_[1]->AddBytesToTransfer(transfer_size);
+  simulator_result = simulator_.RunUntilOrTimeout(
+      [this]() {
+        return receiver_endpoints_[0]->bytes_received() == transfer_size &&
+               receiver_endpoints_[1]->bytes_received() == transfer_size;
+      },
+      3 * transfer_time);
+  ASSERT_TRUE(simulator_result);
+}
+
+TEST_F(Bbr2MultiSenderTest, QUIC_SLOW_TEST(Bbr2VsRenoB2RC)) {
+  SetConnectionOption(sender_0_, kB2RC);
+  SetupTcpSender(sender_endpoints_[1].get(), /*reno=*/true);
+
+  MultiSenderTopologyParams params;
+  CreateNetwork(params);
+
+  const QuicByteCount transfer_size = 10 * 1024 * 1024;
   const QuicTime::Delta transfer_time =
       params.BottleneckBandwidth().TransferTime(transfer_size);
   QUIC_LOG(INFO) << "Single flow transfer time: " << transfer_time;
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index ddd3dbd..9b09027 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -137,6 +137,8 @@
                                                  // loss, set inflight_hi to the
                                                  // max of inflight@send and max
                                                  // bytes delivered in round.
+const QuicTag kB2RC = TAG('B', '2', 'R', 'C');   // Disable Reno-coexistence for
+                                                 // BBR2.
 const QuicTag kBSAO = TAG('B', 'S', 'A', 'O');   // Avoid Overestimation in
                                                  // Bandwidth Sampler with ack
                                                  // aggregation
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 0edd9db..0a3a97f 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -11,6 +11,7 @@
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_allocate_stream_sequencer_buffer_blocks_on_demand, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_allow_client_enabled_bbr_v2, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_avoid_too_low_probe_bw_cwnd, false)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_disable_reno_coexistence, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_fewer_startup_round_trips, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_startup_loss_exit_use_max_delivered, true)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_use_bytes_delivered, false)