gfe-relnote: Notify SendAlgorithm on neutered packets in QUIC. Protected by (enabling blocked) --gfe2_reloadable_flag_quic_avoid_overestimate_bandwidth_with_aggregation.
This is split from cl/279746200 for easier review. I'll unblock this flag in that cl.
PiperOrigin-RevId: 296301871
Change-Id: I499357505e3b817a82b6f4f2172f7b890b684b32
diff --git a/quic/core/congestion_control/bandwidth_sampler.cc b/quic/core/congestion_control/bandwidth_sampler.cc
index 6ecd886..6272664 100644
--- a/quic/core/congestion_control/bandwidth_sampler.cc
+++ b/quic/core/congestion_control/bandwidth_sampler.cc
@@ -73,6 +73,7 @@
: total_bytes_sent_(0),
total_bytes_acked_(0),
total_bytes_lost_(0),
+ total_bytes_neutered_(0),
total_bytes_sent_at_last_acked_packet_(0),
last_acked_packet_sent_time_(QuicTime::Zero()),
last_acked_packet_ack_time_(QuicTime::Zero()),
@@ -147,6 +148,14 @@
"in it.";
}
+void BandwidthSampler::OnPacketNeutered(QuicPacketNumber packet_number) {
+ connection_state_map_.Remove(
+ packet_number, [&](const ConnectionStateOnSentPacket& sent_packet) {
+ QUIC_CODE_COUNT(quic_bandwidth_sampler_packet_neutered);
+ total_bytes_neutered_ += sent_packet.size;
+ });
+}
+
BandwidthSamplerInterface::CongestionEventSample
BandwidthSampler::OnCongestionEvent(QuicTime ack_time,
const AckedPacketVector& acked_packets,
@@ -390,6 +399,10 @@
return total_bytes_lost_;
}
+QuicByteCount BandwidthSampler::total_bytes_neutered() const {
+ return total_bytes_neutered_;
+}
+
bool BandwidthSampler::is_app_limited() const {
return is_app_limited_;
}
diff --git a/quic/core/congestion_control/bandwidth_sampler.h b/quic/core/congestion_control/bandwidth_sampler.h
index df63fa5..90b20fc 100644
--- a/quic/core/congestion_control/bandwidth_sampler.h
+++ b/quic/core/congestion_control/bandwidth_sampler.h
@@ -149,6 +149,8 @@
QuicByteCount bytes_in_flight,
HasRetransmittableData has_retransmittable_data) = 0;
+ virtual void OnPacketNeutered(QuicPacketNumber packet_number) = 0;
+
// Notifies the sampler that the |packet_number| is acknowledged. Returns a
// bandwidth sample. If no bandwidth sample is available,
// QuicBandwidth::Zero() is returned.
@@ -206,10 +208,11 @@
// Remove all the packets lower than the specified packet number.
virtual void RemoveObsoletePackets(QuicPacketNumber least_unacked) = 0;
- // Total number of bytes sent/acked/lost in the connection.
+ // Total number of bytes sent/acked/lost/neutered in the connection.
virtual QuicByteCount total_bytes_sent() const = 0;
virtual QuicByteCount total_bytes_acked() const = 0;
virtual QuicByteCount total_bytes_lost() const = 0;
+ virtual QuicByteCount total_bytes_neutered() const = 0;
// Application-limited information exported for debugging.
virtual bool is_app_limited() const = 0;
@@ -308,6 +311,7 @@
QuicByteCount bytes,
QuicByteCount bytes_in_flight,
HasRetransmittableData has_retransmittable_data) override;
+ void OnPacketNeutered(QuicPacketNumber packet_number) override;
BandwidthSample OnPacketAcknowledged(QuicTime ack_time,
QuicPacketNumber packet_number) override;
CongestionEventSample OnCongestionEvent(
@@ -329,6 +333,7 @@
QuicByteCount total_bytes_sent() const override;
QuicByteCount total_bytes_acked() const override;
QuicByteCount total_bytes_lost() const override;
+ QuicByteCount total_bytes_neutered() const override;
bool is_app_limited() const override;
@@ -432,6 +437,9 @@
// The total number of congestion controlled bytes which were lost.
QuicByteCount total_bytes_lost_;
+ // The total number of congestion controlled bytes which have been neutered.
+ QuicByteCount total_bytes_neutered_;
+
// The value of |total_bytes_sent_| at the time the last acknowledged packet
// was sent. Valid only when |last_acked_packet_sent_time_| is valid.
QuicByteCount total_bytes_sent_at_last_acked_packet_;
diff --git a/quic/core/congestion_control/bandwidth_sampler_test.cc b/quic/core/congestion_control/bandwidth_sampler_test.cc
index 9a3c456..13fa1c0 100644
--- a/quic/core/congestion_control/bandwidth_sampler_test.cc
+++ b/quic/core/congestion_control/bandwidth_sampler_test.cc
@@ -566,16 +566,39 @@
EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
}
+TEST_F(BandwidthSamplerTest, NeuterPacket) {
+ SendPacket(1);
+ EXPECT_EQ(0u, sampler_.total_bytes_neutered());
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+ sampler_.OnPacketNeutered(QuicPacketNumber(1));
+ EXPECT_LT(0u, sampler_.total_bytes_neutered());
+ EXPECT_EQ(0u, sampler_.total_bytes_acked());
+
+ // If packet 1 is acked it should not produce a bandwidth sample.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+ BandwidthSampler::CongestionEventSample sample = sampler_.OnCongestionEvent(
+ clock_.Now(),
+ {AckedPacket(QuicPacketNumber(1), kRegularPacketSize, clock_.Now())}, {},
+ max_bandwidth_, est_bandwidth_upper_bound_, round_trip_count_);
+ EXPECT_EQ(0u, sampler_.total_bytes_acked());
+ EXPECT_EQ(QuicBandwidth::Zero(), sample.sample_max_bandwidth);
+ EXPECT_FALSE(sample.sample_is_app_limited);
+ EXPECT_EQ(QuicTime::Delta::Infinite(), sample.sample_rtt);
+ EXPECT_EQ(0u, sample.sample_max_inflight);
+ EXPECT_EQ(0u, sample.extra_acked);
+}
+
TEST_F(BandwidthSamplerTest, CongestionEventSampleDefaultValues) {
// Make sure a default constructed CongestionEventSample has the correct
// initial values for BandwidthSampler::OnCongestionEvent() to work.
BandwidthSampler::CongestionEventSample sample;
- DCHECK_EQ(QuicBandwidth::Zero(), sample.sample_max_bandwidth);
- DCHECK(!sample.sample_is_app_limited);
- DCHECK_EQ(QuicTime::Delta::Infinite(), sample.sample_rtt);
- DCHECK_EQ(0u, sample.sample_max_inflight);
- DCHECK_EQ(0u, sample.extra_acked);
+ EXPECT_EQ(QuicBandwidth::Zero(), sample.sample_max_bandwidth);
+ EXPECT_FALSE(sample.sample_is_app_limited);
+ EXPECT_EQ(QuicTime::Delta::Infinite(), sample.sample_rtt);
+ EXPECT_EQ(0u, sample.sample_max_inflight);
+ EXPECT_EQ(0u, sample.extra_acked);
}
// 1) Send 2 packets, 2) Ack both in 1 event, 3) Repeat.
diff --git a/quic/core/congestion_control/bbr2_misc.h b/quic/core/congestion_control/bbr2_misc.h
index 1d6fb35..2226566 100644
--- a/quic/core/congestion_control/bbr2_misc.h
+++ b/quic/core/congestion_control/bbr2_misc.h
@@ -359,6 +359,10 @@
return bandwidth_sampler_.max_ack_height();
}
+ void OnPacketNeutered(QuicPacketNumber packet_number) {
+ bandwidth_sampler_.OnPacketNeutered(packet_number);
+ }
+
uint64_t num_ack_aggregation_epochs() const {
return bandwidth_sampler_.num_ack_aggregation_epochs();
}
diff --git a/quic/core/congestion_control/bbr2_sender.cc b/quic/core/congestion_control/bbr2_sender.cc
index d795ad1..108cb34 100644
--- a/quic/core/congestion_control/bbr2_sender.cc
+++ b/quic/core/congestion_control/bbr2_sender.cc
@@ -284,6 +284,10 @@
is_retransmittable);
}
+void Bbr2Sender::OnPacketNeutered(QuicPacketNumber packet_number) {
+ model_.OnPacketNeutered(packet_number);
+}
+
bool Bbr2Sender::CanSend(QuicByteCount bytes_in_flight) {
const bool result = bytes_in_flight < GetCongestionWindow();
return result;
diff --git a/quic/core/congestion_control/bbr2_sender.h b/quic/core/congestion_control/bbr2_sender.h
index 2bba285..64b6fcb 100644
--- a/quic/core/congestion_control/bbr2_sender.h
+++ b/quic/core/congestion_control/bbr2_sender.h
@@ -64,6 +64,8 @@
QuicByteCount bytes,
HasRetransmittableData is_retransmittable) override;
+ void OnPacketNeutered(QuicPacketNumber packet_number) override;
+
void OnRetransmissionTimeout(bool /*packets_retransmitted*/) override {}
void OnConnectionMigration() override {}
diff --git a/quic/core/congestion_control/bbr_sender.cc b/quic/core/congestion_control/bbr_sender.cc
index 3fd6139..da723c4 100644
--- a/quic/core/congestion_control/bbr_sender.cc
+++ b/quic/core/congestion_control/bbr_sender.cc
@@ -181,6 +181,10 @@
is_retransmittable);
}
+void BbrSender::OnPacketNeutered(QuicPacketNumber packet_number) {
+ sampler_.OnPacketNeutered(packet_number);
+}
+
bool BbrSender::CanSend(QuicByteCount bytes_in_flight) {
return bytes_in_flight < GetCongestionWindow();
}
diff --git a/quic/core/congestion_control/bbr_sender.h b/quic/core/congestion_control/bbr_sender.h
index 87d3272..a58b8d5 100644
--- a/quic/core/congestion_control/bbr_sender.h
+++ b/quic/core/congestion_control/bbr_sender.h
@@ -118,6 +118,7 @@
QuicPacketNumber packet_number,
QuicByteCount bytes,
HasRetransmittableData is_retransmittable) override;
+ void OnPacketNeutered(QuicPacketNumber packet_number) override;
void OnRetransmissionTimeout(bool /*packets_retransmitted*/) override {}
void OnConnectionMigration() override {}
bool CanSend(QuicByteCount bytes_in_flight) override;
diff --git a/quic/core/congestion_control/send_algorithm_interface.h b/quic/core/congestion_control/send_algorithm_interface.h
index a049e67..b242951 100644
--- a/quic/core/congestion_control/send_algorithm_interface.h
+++ b/quic/core/congestion_control/send_algorithm_interface.h
@@ -110,6 +110,9 @@
QuicByteCount bytes,
HasRetransmittableData is_retransmittable) = 0;
+ // Inform that |packet_number| has been neutered.
+ virtual void OnPacketNeutered(QuicPacketNumber packet_number) = 0;
+
// Called when the retransmission timeout fires. Neither OnPacketAbandoned
// nor OnPacketLost will be called for these packets.
virtual void OnRetransmissionTimeout(bool packets_retransmitted) = 0;
diff --git a/quic/core/congestion_control/tcp_cubic_sender_bytes.h b/quic/core/congestion_control/tcp_cubic_sender_bytes.h
index 9144b8a..7a36f76 100644
--- a/quic/core/congestion_control/tcp_cubic_sender_bytes.h
+++ b/quic/core/congestion_control/tcp_cubic_sender_bytes.h
@@ -61,6 +61,7 @@
QuicPacketNumber packet_number,
QuicByteCount bytes,
HasRetransmittableData is_retransmittable) override;
+ void OnPacketNeutered(QuicPacketNumber /*packet_number*/) override {}
void OnRetransmissionTimeout(bool packets_retransmitted) override;
bool CanSend(QuicByteCount bytes_in_flight) override;
QuicBandwidth PacingRate(QuicByteCount bytes_in_flight) const override;
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index 54a91e2..aa383b9 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -121,6 +121,9 @@
const QuicTag kIW20 = TAG('I', 'W', '2', '0'); // Force ICWND to 20
const QuicTag kIW50 = TAG('I', 'W', '5', '0'); // Force ICWND to 50
const QuicTag kB2ON = TAG('B', '2', 'O', 'N'); // Enable BBRv2
+const QuicTag kBSAO = TAG('B', 'S', 'A', 'O'); // Avoid Overestimation in
+ // Bandwidth Sampler with ack
+ // aggregation
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_connection_test.cc b/quic/core/quic_connection_test.cc
index 28524c8..c17424e 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -1053,6 +1053,7 @@
EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true));
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
.Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, OnPacketNeutered(_)).Times(AnyNumber());
EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
.WillRepeatedly(Return(kDefaultTCPMSS));
EXPECT_CALL(*send_algorithm_, PacingRate(_))
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc
index e4f46d8..a055299 100644
--- a/quic/core/quic_sent_packet_manager.cc
+++ b/quic/core/quic_sent_packet_manager.cc
@@ -431,11 +431,25 @@
}
void QuicSentPacketManager::NeuterUnencryptedPackets() {
- unacked_packets_.NeuterUnencryptedPackets();
+ for (QuicPacketNumber packet_number :
+ unacked_packets_.NeuterUnencryptedPackets()) {
+ if (avoid_overestimate_bandwidth_with_aggregation_) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(
+ quic_avoid_overestimate_bandwidth_with_aggregation, 1, 4);
+ send_algorithm_->OnPacketNeutered(packet_number);
+ }
+ }
}
void QuicSentPacketManager::NeuterHandshakePackets() {
- unacked_packets_.NeuterHandshakePackets();
+ for (QuicPacketNumber packet_number :
+ unacked_packets_.NeuterHandshakePackets()) {
+ if (avoid_overestimate_bandwidth_with_aggregation_) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(
+ quic_avoid_overestimate_bandwidth_with_aggregation, 2, 4);
+ send_algorithm_->OnPacketNeutered(packet_number);
+ }
+ }
}
bool QuicSentPacketManager::ShouldAddMaxAckDelay() const {
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h
index d8fc069..59bb356 100644
--- a/quic/core/quic_sent_packet_manager.h
+++ b/quic/core/quic_sent_packet_manager.h
@@ -648,6 +648,9 @@
// True if any 1-RTT packet gets sent.
bool one_rtt_packet_sent_;
+
+ const bool avoid_overestimate_bandwidth_with_aggregation_ =
+ GetQuicReloadableFlag(quic_avoid_overestimate_bandwidth_with_aggregation);
};
} // namespace quic
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc
index 08e918e..60c84a7 100644
--- a/quic/core/quic_sent_packet_manager_test.cc
+++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -111,6 +111,7 @@
.WillRepeatedly(Return(QuicBandwidth::Zero()));
EXPECT_CALL(*send_algorithm_, InSlowStart()).Times(AnyNumber());
EXPECT_CALL(*send_algorithm_, InRecovery()).Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, OnPacketNeutered(_)).Times(AnyNumber());
EXPECT_CALL(*network_change_visitor_, OnPathMtuIncreased(1000))
.Times(AnyNumber());
EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(true));
@@ -3343,6 +3344,31 @@
manager_.GetRetransmissionTime());
}
+TEST_F(QuicSentPacketManagerTest, SetHandshakeConfirmed) {
+ QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT);
+ manager_.EnableMultiplePacketNumberSpacesSupport();
+
+ SendDataPacket(1, ENCRYPTION_INITIAL);
+
+ SendDataPacket(2, ENCRYPTION_HANDSHAKE);
+
+ EXPECT_CALL(notifier_, OnFrameAcked(_, _, _))
+ .WillOnce(
+ Invoke([](const QuicFrame& /*frame*/, QuicTime::Delta ack_delay_time,
+ QuicTime receive_timestamp) {
+ EXPECT_TRUE(ack_delay_time.IsZero());
+ EXPECT_EQ(receive_timestamp, QuicTime::Zero());
+ return true;
+ }));
+
+ if (GetQuicReloadableFlag(
+ quic_avoid_overestimate_bandwidth_with_aggregation)) {
+ EXPECT_CALL(*send_algorithm_, OnPacketNeutered(QuicPacketNumber(2)))
+ .Times(1);
+ }
+ manager_.SetHandshakeConfirmed();
+}
+
// Regresstion test for b/148841700.
TEST_F(QuicSentPacketManagerTest, NeuterUnencryptedPackets) {
SendCryptoPacket(1);
@@ -3357,6 +3383,11 @@
EXPECT_CALL(notifier_, OnFrameAcked(_, _, _)).Times(0);
}
EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
+ if (GetQuicReloadableFlag(
+ quic_avoid_overestimate_bandwidth_with_aggregation)) {
+ EXPECT_CALL(*send_algorithm_, OnPacketNeutered(QuicPacketNumber(1)))
+ .Times(1);
+ }
manager_.NeuterUnencryptedPackets();
}
diff --git a/quic/core/quic_unacked_packet_map.cc b/quic/core/quic_unacked_packet_map.cc
index c80593f..c856353 100644
--- a/quic/core/quic_unacked_packet_map.cc
+++ b/quic/core/quic_unacked_packet_map.cc
@@ -207,17 +207,21 @@
RemoveFromInFlight(info);
}
-void QuicUnackedPacketMap::NeuterUnencryptedPackets() {
+QuicInlinedVector<QuicPacketNumber, 2>
+QuicUnackedPacketMap::NeuterUnencryptedPackets() {
+ QuicInlinedVector<QuicPacketNumber, 2> neutered_packets;
QuicPacketNumber packet_number = GetLeastUnacked();
for (QuicUnackedPacketMap::iterator it = unacked_packets_.begin();
it != unacked_packets_.end(); ++it, ++packet_number) {
if (!it->retransmittable_frames.empty() &&
it->encryption_level == ENCRYPTION_INITIAL) {
+ QUIC_DVLOG(2) << "Neutering unencrypted packet " << packet_number;
// Once the connection swithes to forward secure, no unencrypted packets
// will be sent. The data has been abandoned in the cryto stream. Remove
// it from in flight.
RemoveFromInFlight(packet_number);
it->state = NEUTERED;
+ neutered_packets.push_back(packet_number);
if (GetQuicReloadableFlag(quic_neuter_unencrypted_control_frames) ||
supports_multiple_packet_number_spaces_) {
if (GetQuicReloadableFlag(quic_neuter_unencrypted_control_frames)) {
@@ -234,18 +238,23 @@
if (supports_multiple_packet_number_spaces_) {
last_inflight_packets_sent_time_[INITIAL_DATA] = QuicTime::Zero();
}
+ return neutered_packets;
}
-void QuicUnackedPacketMap::NeuterHandshakePackets() {
+QuicInlinedVector<QuicPacketNumber, 2>
+QuicUnackedPacketMap::NeuterHandshakePackets() {
+ QuicInlinedVector<QuicPacketNumber, 2> neutered_packets;
QuicPacketNumber packet_number = GetLeastUnacked();
for (QuicUnackedPacketMap::iterator it = unacked_packets_.begin();
it != unacked_packets_.end(); ++it, ++packet_number) {
if (!it->retransmittable_frames.empty() &&
GetPacketNumberSpace(it->encryption_level) == HANDSHAKE_DATA) {
+ QUIC_DVLOG(2) << "Neutering handshake packet " << packet_number;
RemoveFromInFlight(packet_number);
// Notify session that the data has been delivered (but do not notify
// send algorithm).
it->state = NEUTERED;
+ neutered_packets.push_back(packet_number);
// TODO(b/148868195): use NotifyFramesNeutered.
NotifyFramesAcked(*it, QuicTime::Delta::Zero(), QuicTime::Zero());
}
@@ -253,6 +262,7 @@
if (supports_multiple_packet_number_spaces()) {
last_inflight_packets_sent_time_[HANDSHAKE_DATA] = QuicTime::Zero();
}
+ return neutered_packets;
}
bool QuicUnackedPacketMap::HasInFlightPackets() const {
diff --git a/quic/core/quic_unacked_packet_map.h b/quic/core/quic_unacked_packet_map.h
index d155fcc..8492fb0 100644
--- a/quic/core/quic_unacked_packet_map.h
+++ b/quic/core/quic_unacked_packet_map.h
@@ -66,13 +66,13 @@
void RemoveFromInFlight(QuicPacketNumber packet_number);
// Called to neuter all unencrypted packets to ensure they do not get
- // retransmitted.
- void NeuterUnencryptedPackets();
+ // retransmitted. Returns a vector of neutered packet numbers.
+ QuicInlinedVector<QuicPacketNumber, 2> NeuterUnencryptedPackets();
// Called to neuter packets in handshake packet number space to ensure they do
- // not get retransmitted.
+ // not get retransmitted. Returns a vector of neutered packet numbers.
// TODO(fayang): Consider to combine this with NeuterUnencryptedPackets.
- void NeuterHandshakePackets();
+ QuicInlinedVector<QuicPacketNumber, 2> NeuterHandshakePackets();
// Returns true if |packet_number| has retransmittable frames. This will
// return false if all frames of this packet are either non-retransmittable or
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
index 7ecac5b..035e15b 100644
--- a/quic/test_tools/quic_test_utils.h
+++ b/quic/test_tools/quic_test_utils.h
@@ -940,6 +940,7 @@
QuicPacketNumber,
QuicByteCount,
HasRetransmittableData));
+ MOCK_METHOD1(OnPacketNeutered, void(QuicPacketNumber));
MOCK_METHOD1(OnRetransmissionTimeout, void(bool));
MOCK_METHOD0(OnConnectionMigration, void());
MOCK_METHOD0(RevertRetransmissionTimeout, void());