blob: a527417a19d69da5d5dd95acc07b9455fa3452cc [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 "net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.h"
#include "net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h"
#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
#include "net/third_party/quiche/src/quic/core/quic_time.h"
#include "net/third_party/quiche/src/quic/core/quic_types.h"
namespace quic {
namespace {
// Sensitivity in response to losses. 0 means no loss response.
// 0.3 is also used by TCP bbr and cubic.
const float kBeta = 0.3;
} // namespace
RoundTripCounter::RoundTripCounter() : round_trip_count_(0) {}
void RoundTripCounter::OnPacketSent(QuicPacketNumber packet_number) {
DCHECK(!last_sent_packet_.IsInitialized() ||
last_sent_packet_ < packet_number);
last_sent_packet_ = packet_number;
}
bool RoundTripCounter::OnPacketsAcked(QuicPacketNumber last_acked_packet) {
if (!end_of_round_trip_.IsInitialized() ||
last_acked_packet > end_of_round_trip_) {
round_trip_count_++;
end_of_round_trip_ = last_sent_packet_;
return true;
}
return false;
}
void RoundTripCounter::RestartRound() {
end_of_round_trip_ = last_sent_packet_;
}
MinRttFilter::MinRttFilter(QuicTime::Delta initial_min_rtt,
QuicTime initial_min_rtt_timestamp)
: min_rtt_(initial_min_rtt),
min_rtt_timestamp_(initial_min_rtt_timestamp) {}
void MinRttFilter::Update(QuicTime::Delta sample_rtt, QuicTime now) {
if (sample_rtt < min_rtt_ || min_rtt_timestamp_ == QuicTime::Zero()) {
min_rtt_ = sample_rtt;
min_rtt_timestamp_ = now;
}
}
void MinRttFilter::ForceUpdate(QuicTime::Delta sample_rtt, QuicTime now) {
min_rtt_ = sample_rtt;
min_rtt_timestamp_ = now;
}
const SendTimeState& SendStateOfLargestPacket(
const Bbr2CongestionEvent& congestion_event) {
const auto& last_acked_sample = congestion_event.last_acked_sample;
const auto& last_lost_sample = congestion_event.last_lost_sample;
if (!last_lost_sample.packet_number.IsInitialized()) {
return last_acked_sample.bandwidth_sample.state_at_send;
}
if (!last_acked_sample.packet_number.IsInitialized()) {
return last_lost_sample.send_time_state;
}
DCHECK_NE(last_acked_sample.packet_number, last_lost_sample.packet_number);
if (last_acked_sample.packet_number < last_lost_sample.packet_number) {
return last_lost_sample.send_time_state;
}
return last_acked_sample.bandwidth_sample.state_at_send;
}
Bbr2NetworkModel::Bbr2NetworkModel(const Bbr2Params* params,
QuicTime::Delta initial_rtt,
QuicTime initial_rtt_timestamp,
float cwnd_gain,
float pacing_gain)
: params_(params),
bandwidth_sampler_(nullptr, params->initial_max_ack_height_filter_window),
min_rtt_filter_(initial_rtt, initial_rtt_timestamp),
cwnd_gain_(cwnd_gain),
pacing_gain_(pacing_gain) {}
void Bbr2NetworkModel::OnPacketSent(QuicTime sent_time,
QuicByteCount bytes_in_flight,
QuicPacketNumber packet_number,
QuicByteCount bytes,
HasRetransmittableData is_retransmittable) {
round_trip_counter_.OnPacketSent(packet_number);
bandwidth_sampler_.OnPacketSent(sent_time, packet_number, bytes,
bytes_in_flight, is_retransmittable);
}
void Bbr2NetworkModel::OnCongestionEventStart(
QuicTime event_time,
const AckedPacketVector& acked_packets,
const LostPacketVector& lost_packets,
Bbr2CongestionEvent* congestion_event) {
const QuicByteCount prior_bytes_acked = total_bytes_acked();
const QuicByteCount prior_bytes_lost = total_bytes_lost();
congestion_event->event_time = event_time;
congestion_event->end_of_round_trip =
acked_packets.empty() ? false
: round_trip_counter_.OnPacketsAcked(
acked_packets.rbegin()->packet_number);
for (const auto& packet : acked_packets) {
const BandwidthSample bandwidth_sample =
bandwidth_sampler_.OnPacketAcknowledged(event_time,
packet.packet_number);
if (!bandwidth_sample.state_at_send.is_valid) {
// From the sampler's perspective, the packet has never been sent, or
// the packet has been acked or marked as lost previously.
continue;
}
congestion_event->last_sample_is_app_limited =
bandwidth_sample.state_at_send.is_app_limited;
if (!bandwidth_sample.rtt.IsZero()) {
congestion_event->sample_min_rtt =
std::min(congestion_event->sample_min_rtt, bandwidth_sample.rtt);
}
if (!bandwidth_sample.state_at_send.is_app_limited ||
bandwidth_sample.bandwidth > MaxBandwidth()) {
congestion_event->sample_max_bandwidth = std::max(
congestion_event->sample_max_bandwidth, bandwidth_sample.bandwidth);
}
if (bandwidth_sample.bandwidth > bandwidth_latest_) {
bandwidth_latest_ = bandwidth_sample.bandwidth;
}
// |inflight_sample| is the total bytes acked while |packet| is inflight.
QuicByteCount inflight_sample =
total_bytes_acked() - bandwidth_sample.state_at_send.total_bytes_acked;
if (inflight_sample > inflight_latest_) {
inflight_latest_ = inflight_sample;
}
congestion_event->last_acked_sample = {packet.packet_number,
bandwidth_sample, inflight_sample};
}
min_rtt_filter_.Update(congestion_event->sample_min_rtt, event_time);
if (!congestion_event->sample_max_bandwidth.IsZero()) {
max_bandwidth_filter_.Update(congestion_event->sample_max_bandwidth);
}
for (const LostPacket& packet : lost_packets) {
const SendTimeState send_time_state =
bandwidth_sampler_.OnPacketLost(packet.packet_number);
if (send_time_state.is_valid) {
congestion_event->last_lost_sample = {packet.packet_number,
send_time_state};
}
}
congestion_event->bytes_in_flight = bytes_in_flight();
congestion_event->bytes_acked = total_bytes_acked() - prior_bytes_acked;
congestion_event->bytes_lost = total_bytes_lost() - prior_bytes_lost;
bytes_lost_in_round_ += congestion_event->bytes_lost;
bandwidth_sampler_.OnAckEventEnd(BandwidthEstimate(), RoundTripCount());
if (!congestion_event->end_of_round_trip) {
return;
}
// Per round-trip updates.
AdaptLowerBounds(*congestion_event);
}
void Bbr2NetworkModel::AdaptLowerBounds(
const Bbr2CongestionEvent& congestion_event) {
if (!congestion_event.end_of_round_trip ||
congestion_event.is_probing_for_bandwidth) {
return;
}
if (bytes_lost_in_round_ > 0) {
if (bandwidth_lo_.IsInfinite()) {
bandwidth_lo_ = MaxBandwidth();
}
if (inflight_lo_ == inflight_lo_default()) {
inflight_lo_ = congestion_event.prior_cwnd;
}
bandwidth_lo_ = std::max(bandwidth_latest_, bandwidth_lo_ * (1.0 - kBeta));
QUIC_DVLOG(3) << "bandwidth_lo_ updated to " << bandwidth_lo_
<< ", bandwidth_latest_ is " << bandwidth_latest_;
inflight_lo_ =
std::max<QuicByteCount>(inflight_latest_, inflight_lo_ * (1.0 - kBeta));
}
}
void Bbr2NetworkModel::OnCongestionEventFinish(
QuicPacketNumber least_unacked_packet,
const Bbr2CongestionEvent& congestion_event) {
if (congestion_event.end_of_round_trip) {
const auto& last_acked_sample = congestion_event.last_acked_sample;
if (last_acked_sample.bandwidth_sample.state_at_send.is_valid) {
bandwidth_latest_ = last_acked_sample.bandwidth_sample.bandwidth;
inflight_latest_ = last_acked_sample.inflight_sample;
}
bytes_lost_in_round_ = 0;
}
bandwidth_sampler_.RemoveObsoletePackets(least_unacked_packet);
}
void Bbr2NetworkModel::UpdateNetworkParameters(QuicBandwidth bandwidth,
QuicTime::Delta rtt) {
if (!bandwidth.IsInfinite() && bandwidth > MaxBandwidth()) {
max_bandwidth_filter_.Update(bandwidth);
}
if (!rtt.IsZero()) {
min_rtt_filter_.Update(rtt, MinRttTimestamp());
}
}
bool Bbr2NetworkModel::MaybeExpireMinRtt(
const Bbr2CongestionEvent& congestion_event) {
if (congestion_event.event_time <
(MinRttTimestamp() + Params().probe_rtt_period)) {
return false;
}
if (congestion_event.sample_min_rtt.IsInfinite()) {
return false;
}
QUIC_DVLOG(3) << "Replacing expired min rtt of " << min_rtt_filter_.Get()
<< " by " << congestion_event.sample_min_rtt << " @ "
<< congestion_event.event_time;
min_rtt_filter_.ForceUpdate(congestion_event.sample_min_rtt,
congestion_event.event_time);
return true;
}
bool Bbr2NetworkModel::IsCongestionWindowLimited(
const Bbr2CongestionEvent& congestion_event) const {
QuicByteCount prior_bytes_in_flight = congestion_event.bytes_in_flight +
congestion_event.bytes_acked +
congestion_event.bytes_lost;
return prior_bytes_in_flight >= congestion_event.prior_cwnd;
}
bool Bbr2NetworkModel::IsInflightTooHigh(
const Bbr2CongestionEvent& congestion_event) const {
const SendTimeState& send_state = SendStateOfLargestPacket(congestion_event);
if (!send_state.is_valid) {
// Not enough information.
return false;
}
const QuicByteCount inflight_at_send = BytesInFlight(send_state);
// TODO(wub): Consider total_bytes_lost() - send_state.total_bytes_lost, which
// is the total bytes lost when the largest numbered packet was inflight.
// bytes_lost_in_round_, OTOH, is the total bytes lost in the "current" round.
const QuicByteCount bytes_lost_in_round = bytes_lost_in_round_;
QUIC_DVLOG(3) << "IsInflightTooHigh: bytes_lost_in_round:"
<< bytes_lost_in_round << ", lost_in_round_threshold:"
<< inflight_at_send * Params().loss_threshold;
if (inflight_at_send > 0 && bytes_lost_in_round > 0) {
QuicByteCount lost_in_round_threshold =
inflight_at_send * Params().loss_threshold;
if (bytes_lost_in_round > lost_in_round_threshold) {
return true;
}
}
return false;
}
void Bbr2NetworkModel::RestartRound() {
bytes_lost_in_round_ = 0;
round_trip_counter_.RestartRound();
}
QuicByteCount Bbr2NetworkModel::inflight_hi_with_headroom() const {
QuicByteCount headroom = inflight_hi_ * Params().inflight_hi_headroom;
return inflight_hi_ > headroom ? inflight_hi_ - headroom : 0;
}
} // namespace quic