gfe-relnote: In QUIC, add a connection option to use 2 * rttvar when calculating PTO timeout. Protected by existing gfe2_reloadable_flag_quic_enable_pto.

PiperOrigin-RevId: 278451908
Change-Id: I5f68cad58734bd52e70efce51eefd84646050cac
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index f09c595..9707fb5 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -207,6 +207,8 @@
                                                  // since 1st PTO.
 const QuicTag kPEB2 = TAG('P', 'E', 'B', '2');   // Start exponential backoff
                                                  // since 2nd PTO.
+const QuicTag kPVS1 = TAG('P', 'V', 'S', '1');   // Use 2 * rttvar when
+                                                 // calculating PTO timeout.
 
 // Optional support of truncated Connection IDs.  If sent by a peer, the value
 // is the minimum number of bytes allowed for the connection ID sent to the
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 06f1ebb..443ecbc 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -444,11 +444,11 @@
     }
     if (config.HasClientSentConnectionOption(k7PTO, perspective_)) {
       max_consecutive_ptos_ = 6;
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 3, 7);
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 3, 8);
     }
     if (config.HasClientSentConnectionOption(k8PTO, perspective_)) {
       max_consecutive_ptos_ = 7;
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 4, 7);
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 4, 8);
     }
   }
   if (config.HasClientSentConnectionOption(kNSTP, perspective_)) {
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc
index f9ec1c0..deec82f 100644
--- a/quic/core/quic_sent_packet_manager.cc
+++ b/quic/core/quic_sent_packet_manager.cc
@@ -105,6 +105,7 @@
       skip_packet_number_for_pto_(false),
       always_include_max_ack_delay_for_pto_timeout_(true),
       pto_exponential_backoff_start_point_(0),
+      pto_rttvar_multiplier_(4),
       neuter_handshake_packets_once_(
           GetQuicReloadableFlag(quic_neuter_handshake_packets_once)) {
   SetSendAlgorithm(congestion_control_type);
@@ -151,12 +152,12 @@
   if (GetQuicReloadableFlag(quic_enable_pto)) {
     if (config.HasClientSentConnectionOption(k2PTO, perspective)) {
       pto_enabled_ = true;
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 2, 7);
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 2, 8);
     }
     if (config.HasClientSentConnectionOption(k1PTO, perspective)) {
       pto_enabled_ = true;
       max_probe_packets_per_pto_ = 1;
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 1, 7);
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 1, 8);
     }
   }
 
@@ -174,17 +175,21 @@
 
   if (pto_enabled_) {
     if (config.HasClientSentConnectionOption(kPTOA, perspective)) {
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 5, 7);
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 5, 8);
       always_include_max_ack_delay_for_pto_timeout_ = false;
     }
     if (config.HasClientSentConnectionOption(kPEB1, perspective)) {
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 6, 7);
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 6, 8);
       StartExponentialBackoffAfterNthPto(1);
     }
     if (config.HasClientSentConnectionOption(kPEB2, perspective)) {
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 7, 7);
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 7, 8);
       StartExponentialBackoffAfterNthPto(2);
     }
+    if (config.HasClientSentConnectionOption(kPVS1, perspective)) {
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 8, 8);
+      pto_rttvar_multiplier_ = 2;
+    }
   }
 
   // Configure congestion control.
@@ -1045,7 +1050,7 @@
   }
   const QuicTime::Delta pto_delay =
       rtt_stats_.smoothed_rtt() +
-      std::max(4 * rtt_stats_.mean_deviation(),
+      std::max(pto_rttvar_multiplier_ * rtt_stats_.mean_deviation(),
                QuicTime::Delta::FromMilliseconds(1)) +
       (ShouldAddMaxAckDelay() ? peer_max_ack_delay_ : QuicTime::Delta::Zero());
   return pto_delay * (1 << (consecutive_pto_count_ -
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h
index 5d362f4..2c3cf99 100644
--- a/quic/core/quic_sent_packet_manager.h
+++ b/quic/core/quic_sent_packet_manager.h
@@ -641,6 +641,9 @@
   // since nth PTO.
   size_t pto_exponential_backoff_start_point_;
 
+  // The multiplier of rttvar when calculating PTO timeout.
+  int pto_rttvar_multiplier_;
+
   // Latched value of quic_neuter_handshake_packets_once.
   const bool neuter_handshake_packets_once_;
 };
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc
index b7ca395..539ce97 100644
--- a/quic/core/quic_sent_packet_manager_test.cc
+++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -3026,6 +3026,35 @@
             manager_.GetRetransmissionTime());
 }
 
+TEST_F(QuicSentPacketManagerTest, PtoTimeoutRttVarMultiple) {
+  EnablePto(k1PTO);
+  // Use 2 * rttvar
+  QuicConfig config;
+  QuicTagVector options;
+  options.push_back(kPVS1);
+  QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+  EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+  EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+  manager_.SetFromConfig(config);
+
+  EXPECT_CALL(*send_algorithm_, PacingRate(_))
+      .WillRepeatedly(Return(QuicBandwidth::Zero()));
+  EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+      .WillRepeatedly(Return(10 * kDefaultTCPMSS));
+  RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats());
+  rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
+                       QuicTime::Delta::Zero(), QuicTime::Zero());
+  QuicTime::Delta srtt = rtt_stats->smoothed_rtt();
+
+  SendDataPacket(1, ENCRYPTION_FORWARD_SECURE);
+  // Verify PTO is correctly set based on 2 times rtt var.
+  QuicTime::Delta expected_pto_delay =
+      srtt + 2 * rtt_stats->mean_deviation() +
+      QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs);
+  EXPECT_EQ(clock_.Now() + expected_pto_delay,
+            manager_.GetRetransmissionTime());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/quic_time.h b/quic/core/quic_time.h
index 9a5ae7d..d4429e3 100644
--- a/quic/core/quic_time.h
+++ b/quic/core/quic_time.h
@@ -210,6 +210,9 @@
 inline bool operator>=(QuicTime::Delta lhs, QuicTime::Delta rhs) {
   return !(lhs < rhs);
 }
+inline QuicTime::Delta operator<<(QuicTime::Delta lhs, size_t rhs) {
+  return QuicTime::Delta(lhs.time_offset_ << rhs);
+}
 inline QuicTime::Delta operator>>(QuicTime::Delta lhs, size_t rhs) {
   return QuicTime::Delta(lhs.time_offset_ >> rhs);
 }