gfe-relnote: In QUIC, skip packet threshold loss detection if largest acked is a runt. Protected by gfe2_reloadable_flag_quic_skip_packet_threshold_loss_detection_with_runt.

PiperOrigin-RevId: 299908950
Change-Id: Ie2d6b5201447cf1352a14faabddc7a40e694c805
diff --git a/quic/core/congestion_control/general_loss_algorithm.cc b/quic/core/congestion_control/general_loss_algorithm.cc
index 5ef2237..5136648 100644
--- a/quic/core/congestion_control/general_loss_algorithm.cc
+++ b/quic/core/congestion_control/general_loss_algorithm.cc
@@ -18,6 +18,7 @@
       reordering_threshold_(kNumberOfNacksBeforeRetransmission),
       use_adaptive_reordering_threshold_(true),
       use_adaptive_time_threshold_(false),
+      use_packet_threshold_for_runt_packets_(true),
       least_in_flight_(1),
       packet_number_space_(NUM_PACKET_NUMBER_SPACES) {}
 
@@ -80,7 +81,17 @@
       continue;
     }
     // Packet threshold loss detection.
-    if (largest_newly_acked - packet_number >= reordering_threshold_) {
+    // Skip packet threshold loss detection if largest_newly_acked is a runt.
+    const bool skip_packet_threshold_detection =
+        !use_packet_threshold_for_runt_packets_ &&
+        it->bytes_sent >
+            unacked_packets.GetTransmissionInfo(largest_newly_acked).bytes_sent;
+    if (skip_packet_threshold_detection) {
+      QUIC_RELOADABLE_FLAG_COUNT_N(
+          quic_skip_packet_threshold_loss_detection_with_runt, 2, 2);
+    }
+    if (!skip_packet_threshold_detection &&
+        largest_newly_acked - packet_number >= reordering_threshold_) {
       packets_lost->push_back(LostPacket(packet_number, it->bytes_sent));
       continue;
     }
diff --git a/quic/core/congestion_control/general_loss_algorithm.h b/quic/core/congestion_control/general_loss_algorithm.h
index 9f7175f..57c483e 100644
--- a/quic/core/congestion_control/general_loss_algorithm.h
+++ b/quic/core/congestion_control/general_loss_algorithm.h
@@ -88,6 +88,14 @@
 
   void enable_adaptive_time_threshold() { use_adaptive_time_threshold_ = true; }
 
+  bool use_packet_threshold_for_runt_packets() const {
+    return use_packet_threshold_for_runt_packets_;
+  }
+
+  void disable_packet_threshold_for_runt_packets() {
+    use_packet_threshold_for_runt_packets_ = false;
+  }
+
  private:
   QuicTime loss_detection_timeout_;
   // Fraction of a max(SRTT, latest_rtt) to permit reordering before declaring
@@ -100,6 +108,8 @@
   bool use_adaptive_reordering_threshold_;
   // If true, uses adaptive time threshold for time based loss detection.
   bool use_adaptive_time_threshold_;
+  // If true, uses packet threshold when largest acked is a runt packet.
+  bool use_packet_threshold_for_runt_packets_;
   // The least in flight packet. Loss detection should start from this. Please
   // note, least_in_flight_ could be largest packet ever sent + 1.
   QuicPacketNumber least_in_flight_;
diff --git a/quic/core/congestion_control/general_loss_algorithm_test.cc b/quic/core/congestion_control/general_loss_algorithm_test.cc
index f3fb1f1..7bef933 100644
--- a/quic/core/congestion_control/general_loss_algorithm_test.cc
+++ b/quic/core/congestion_control/general_loss_algorithm_test.cc
@@ -32,19 +32,24 @@
 
   ~GeneralLossAlgorithmTest() override {}
 
-  void SendDataPacket(uint64_t packet_number) {
+  void SendDataPacket(uint64_t packet_number,
+                      QuicPacketLength encrypted_length) {
     QuicStreamFrame frame;
     frame.stream_id = QuicUtils::GetFirstBidirectionalStreamId(
         CurrentSupportedVersions()[0].transport_version,
         Perspective::IS_CLIENT);
     SerializedPacket packet(QuicPacketNumber(packet_number),
-                            PACKET_1BYTE_PACKET_NUMBER, nullptr, kDefaultLength,
-                            false, false);
+                            PACKET_1BYTE_PACKET_NUMBER, nullptr,
+                            encrypted_length, false, false);
     packet.retransmittable_frames.push_back(QuicFrame(frame));
     unacked_packets_.AddSentPacket(&packet, NOT_RETRANSMISSION, clock_.Now(),
                                    true);
   }
 
+  void SendDataPacket(uint64_t packet_number) {
+    SendDataPacket(packet_number, kDefaultLength);
+  }
+
   void SendAckPacket(uint64_t packet_number) {
     SerializedPacket packet(QuicPacketNumber(packet_number),
                             PACKET_1BYTE_PACKET_NUMBER, nullptr, kDefaultLength,
@@ -431,6 +436,30 @@
   EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
 }
 
+TEST_F(GeneralLossAlgorithmTest, NoPacketThresholdForRuntPackets) {
+  loss_algorithm_.disable_packet_threshold_for_runt_packets();
+  for (size_t i = 1; i <= 6; ++i) {
+    SendDataPacket(i);
+  }
+  // Send a small packet.
+  SendDataPacket(7, /*encrypted_length=*/kDefaultLength / 2);
+  // No packet threshold for runt packet.
+  AckedPacketVector packets_acked;
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(7));
+  packets_acked.push_back(AckedPacket(
+      QuicPacketNumber(7), kMaxOutgoingPacketSize, QuicTime::Zero()));
+  // Verify no packet is detected lost because packet 7 is a runt.
+  VerifyLosses(7, packets_acked, std::vector<uint64_t>{});
+  EXPECT_EQ(clock_.Now() + rtt_stats_.smoothed_rtt() +
+                (rtt_stats_.smoothed_rtt() >> 2),
+            loss_algorithm_.GetLossTimeout());
+  clock_.AdvanceTime(rtt_stats_.smoothed_rtt() +
+                     (rtt_stats_.smoothed_rtt() >> 2));
+  // Verify packets are declared lost because time threshold has passed.
+  VerifyLosses(7, packets_acked, {1, 2, 3, 4, 5, 6});
+  EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/congestion_control/uber_loss_algorithm.cc b/quic/core/congestion_control/uber_loss_algorithm.cc
index 30d213f..abea529 100644
--- a/quic/core/congestion_control/uber_loss_algorithm.cc
+++ b/quic/core/congestion_control/uber_loss_algorithm.cc
@@ -106,4 +106,10 @@
   }
 }
 
+void UberLossAlgorithm::DisablePacketThresholdForRuntPackets() {
+  for (int8_t i = INITIAL_DATA; i < NUM_PACKET_NUMBER_SPACES; ++i) {
+    general_loss_algorithms_[i].disable_packet_threshold_for_runt_packets();
+  }
+}
+
 }  // namespace quic
diff --git a/quic/core/congestion_control/uber_loss_algorithm.h b/quic/core/congestion_control/uber_loss_algorithm.h
index 397dda7..b133f09 100644
--- a/quic/core/congestion_control/uber_loss_algorithm.h
+++ b/quic/core/congestion_control/uber_loss_algorithm.h
@@ -85,6 +85,9 @@
   // Enable adaptive time threshold of all packet number spaces.
   void EnableAdaptiveTimeThreshold();
 
+  // Disable packet threshold loss detection for *runt* packets.
+  void DisablePacketThresholdForRuntPackets();
+
  private:
   friend class test::QuicSentPacketManagerPeer;
 
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index 9532b99..7f8d959 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -182,6 +182,8 @@
                                                  // threshold (default 1/4 RTT)
                                                  // and adaptive packet
                                                  // threshold
+const QuicTag kRUNT = TAG('R', 'U', 'N', 'T');   // No packet threshold loss
+                                                 // detection for "runt" packet.
 // TODO(fayang): Remove this connection option when QUIC_VERSION_35, is removed
 // Since MAX_HEADER_LIST_SIZE settings frame is supported instead.
 const QuicTag kSMHL = TAG('S', 'M', 'H', 'L');   // Support MAX_HEADER_LIST_SIZE
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc
index eb59a8e..bad8aa6 100644
--- a/quic/core/quic_sent_packet_manager.cc
+++ b/quic/core/quic_sent_packet_manager.cc
@@ -292,6 +292,13 @@
     uber_loss_algorithm_.EnableAdaptiveReorderingThreshold();
     uber_loss_algorithm_.EnableAdaptiveTimeThreshold();
   }
+  if (GetQuicReloadableFlag(
+          quic_skip_packet_threshold_loss_detection_with_runt) &&
+      config.HasClientRequestedIndependentOption(kRUNT, perspective)) {
+    QUIC_RELOADABLE_FLAG_COUNT_N(
+        quic_skip_packet_threshold_loss_detection_with_runt, 1, 2);
+    uber_loss_algorithm_.DisablePacketThresholdForRuntPackets();
+  }
   if (config.HasClientSentConnectionOption(kCONH, perspective)) {
     conservative_handshake_retransmits_ = true;
   }
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc
index 2a39f9b..b47e528 100644
--- a/quic/core/quic_sent_packet_manager_test.cc
+++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -3745,6 +3745,24 @@
   manager_.NeuterUnencryptedPackets();
 }
 
+TEST_F(QuicSentPacketManagerTest, NoPacketThresholdDetectionForRuntPackets) {
+  EXPECT_TRUE(
+      QuicSentPacketManagerPeer::UsePacketThresholdForRuntPackets(&manager_));
+
+  SetQuicReloadableFlag(quic_skip_packet_threshold_loss_detection_with_runt,
+                        true);
+  QuicConfig config;
+  QuicTagVector options;
+  options.push_back(kRUNT);
+  QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+  EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+  EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+  manager_.SetFromConfig(config);
+
+  EXPECT_FALSE(
+      QuicSentPacketManagerPeer::UsePacketThresholdForRuntPackets(&manager_));
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/test_tools/quic_sent_packet_manager_peer.cc b/quic/test_tools/quic_sent_packet_manager_peer.cc
index cd8297a..9666145 100644
--- a/quic/test_tools/quic_sent_packet_manager_peer.cc
+++ b/quic/test_tools/quic_sent_packet_manager_peer.cc
@@ -221,5 +221,12 @@
       .use_adaptive_time_threshold();
 }
 
+// static
+bool QuicSentPacketManagerPeer::UsePacketThresholdForRuntPackets(
+    QuicSentPacketManager* sent_packet_manager) {
+  return sent_packet_manager->uber_loss_algorithm_.general_loss_algorithms_[0]
+      .use_packet_threshold_for_runt_packets();
+}
+
 }  // namespace test
 }  // namespace quic
diff --git a/quic/test_tools/quic_sent_packet_manager_peer.h b/quic/test_tools/quic_sent_packet_manager_peer.h
index 3927189..aaa615b 100644
--- a/quic/test_tools/quic_sent_packet_manager_peer.h
+++ b/quic/test_tools/quic_sent_packet_manager_peer.h
@@ -101,6 +101,9 @@
 
   static bool AdaptiveTimeThresholdEnabled(
       QuicSentPacketManager* sent_packet_manager);
+
+  static bool UsePacketThresholdForRuntPackets(
+      QuicSentPacketManager* sent_packet_manager);
 };
 
 }  // namespace test