blob: cdd8547a578860fc17165ba43c66cdd261780135 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "quiche/quic/core/congestion_control/uber_loss_algorithm.h"
#include <algorithm>
#include "quiche/quic/core/crypto/crypto_protocol.h"
#include "quiche/quic/platform/api/quic_bug_tracker.h"
namespace quic {
UberLossAlgorithm::UberLossAlgorithm() {
for (int8_t i = INITIAL_DATA; i < NUM_PACKET_NUMBER_SPACES; ++i) {
general_loss_algorithms_[i].Initialize(static_cast<PacketNumberSpace>(i),
this);
}
}
void UberLossAlgorithm::SetFromConfig(const QuicConfig& config,
Perspective perspective) {
if (config.HasClientRequestedIndependentOption(kELDT, perspective) &&
tuner_ != nullptr) {
tuning_configured_ = true;
MaybeStartTuning();
}
}
LossDetectionInterface::DetectionStats UberLossAlgorithm::DetectLosses(
const QuicUnackedPacketMap& unacked_packets, QuicTime time,
const RttStats& rtt_stats, QuicPacketNumber /*largest_newly_acked*/,
const AckedPacketVector& packets_acked, LostPacketVector* packets_lost) {
DetectionStats overall_stats;
for (int8_t i = INITIAL_DATA; i < NUM_PACKET_NUMBER_SPACES; ++i) {
const QuicPacketNumber largest_acked =
unacked_packets.GetLargestAckedOfPacketNumberSpace(
static_cast<PacketNumberSpace>(i));
if (!largest_acked.IsInitialized() ||
unacked_packets.GetLeastUnacked() > largest_acked) {
// Skip detecting losses if no packet has been received for this packet
// number space or the least_unacked is greater than largest_acked.
continue;
}
DetectionStats stats = general_loss_algorithms_[i].DetectLosses(
unacked_packets, time, rtt_stats, largest_acked, packets_acked,
packets_lost);
overall_stats.sent_packets_max_sequence_reordering =
std::max(overall_stats.sent_packets_max_sequence_reordering,
stats.sent_packets_max_sequence_reordering);
overall_stats.sent_packets_num_borderline_time_reorderings +=
stats.sent_packets_num_borderline_time_reorderings;
overall_stats.total_loss_detection_response_time +=
stats.total_loss_detection_response_time;
}
return overall_stats;
}
QuicTime UberLossAlgorithm::GetLossTimeout() const {
QuicTime loss_timeout = QuicTime::Zero();
// Returns the earliest non-zero loss timeout.
for (int8_t i = INITIAL_DATA; i < NUM_PACKET_NUMBER_SPACES; ++i) {
const QuicTime timeout = general_loss_algorithms_[i].GetLossTimeout();
if (!loss_timeout.IsInitialized()) {
loss_timeout = timeout;
continue;
}
if (timeout.IsInitialized()) {
loss_timeout = std::min(loss_timeout, timeout);
}
}
return loss_timeout;
}
void UberLossAlgorithm::SpuriousLossDetected(
const QuicUnackedPacketMap& unacked_packets, const RttStats& rtt_stats,
QuicTime ack_receive_time, QuicPacketNumber packet_number,
QuicPacketNumber previous_largest_acked) {
general_loss_algorithms_[unacked_packets.GetPacketNumberSpace(packet_number)]
.SpuriousLossDetected(unacked_packets, rtt_stats, ack_receive_time,
packet_number, previous_largest_acked);
}
void UberLossAlgorithm::SetLossDetectionTuner(
std::unique_ptr<LossDetectionTunerInterface> tuner) {
if (tuner_ != nullptr) {
QUIC_BUG(quic_bug_10469_1)
<< "LossDetectionTuner can only be set once when session begins.";
return;
}
tuner_ = std::move(tuner);
}
void UberLossAlgorithm::MaybeStartTuning() {
if (tuner_started_ || !tuning_configured_ || !min_rtt_available_ ||
!user_agent_known_ || !reorder_happened_) {
return;
}
tuner_started_ = tuner_->Start(&tuned_parameters_);
if (!tuner_started_) {
return;
}
if (tuned_parameters_.reordering_shift.has_value() &&
tuned_parameters_.reordering_threshold.has_value()) {
QUIC_DLOG(INFO) << "Setting reordering shift to "
<< *tuned_parameters_.reordering_shift
<< ", and reordering threshold to "
<< *tuned_parameters_.reordering_threshold;
SetReorderingShift(*tuned_parameters_.reordering_shift);
SetReorderingThreshold(*tuned_parameters_.reordering_threshold);
} else {
QUIC_BUG(quic_bug_10469_2)
<< "Tuner started but some parameters are missing";
}
}
void UberLossAlgorithm::OnConfigNegotiated() {}
void UberLossAlgorithm::OnMinRttAvailable() {
min_rtt_available_ = true;
MaybeStartTuning();
}
void UberLossAlgorithm::OnUserAgentIdKnown() {
user_agent_known_ = true;
MaybeStartTuning();
}
void UberLossAlgorithm::OnConnectionClosed() {
if (tuner_ != nullptr && tuner_started_) {
tuner_->Finish(tuned_parameters_);
}
}
void UberLossAlgorithm::OnReorderingDetected() {
const bool tuner_started_before = tuner_started_;
const bool reorder_happened_before = reorder_happened_;
reorder_happened_ = true;
MaybeStartTuning();
if (!tuner_started_before && tuner_started_) {
if (reorder_happened_before) {
QUIC_CODE_COUNT(quic_loss_tuner_started_after_first_reorder);
} else {
QUIC_CODE_COUNT(quic_loss_tuner_started_on_first_reorder);
}
}
}
void UberLossAlgorithm::SetReorderingShift(int reordering_shift) {
for (int8_t i = INITIAL_DATA; i < NUM_PACKET_NUMBER_SPACES; ++i) {
general_loss_algorithms_[i].set_reordering_shift(reordering_shift);
}
}
void UberLossAlgorithm::SetReorderingThreshold(
QuicPacketCount reordering_threshold) {
for (int8_t i = INITIAL_DATA; i < NUM_PACKET_NUMBER_SPACES; ++i) {
general_loss_algorithms_[i].set_reordering_threshold(reordering_threshold);
}
}
void UberLossAlgorithm::EnableAdaptiveReorderingThreshold() {
for (int8_t i = INITIAL_DATA; i < NUM_PACKET_NUMBER_SPACES; ++i) {
general_loss_algorithms_[i].set_use_adaptive_reordering_threshold(true);
}
}
void UberLossAlgorithm::DisableAdaptiveReorderingThreshold() {
for (int8_t i = INITIAL_DATA; i < NUM_PACKET_NUMBER_SPACES; ++i) {
general_loss_algorithms_[i].set_use_adaptive_reordering_threshold(false);
}
}
void UberLossAlgorithm::EnableAdaptiveTimeThreshold() {
for (int8_t i = INITIAL_DATA; i < NUM_PACKET_NUMBER_SPACES; ++i) {
general_loss_algorithms_[i].enable_adaptive_time_threshold();
}
}
QuicPacketCount UberLossAlgorithm::GetPacketReorderingThreshold() const {
return general_loss_algorithms_[APPLICATION_DATA].reordering_threshold();
}
int UberLossAlgorithm::GetPacketReorderingShift() const {
return general_loss_algorithms_[APPLICATION_DATA].reordering_shift();
}
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();
}
}
void UberLossAlgorithm::ResetLossDetection(PacketNumberSpace space) {
if (space >= NUM_PACKET_NUMBER_SPACES) {
QUIC_BUG(quic_bug_10469_3) << "Invalid packet number space: " << space;
return;
}
general_loss_algorithms_[space].Reset();
}
} // namespace quic