blob: 2658443c385b0fcb83bbd4593b5d8159c579bade [file] [log] [blame]
/** @file
*
* A brief file description
*
* @section license License
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <tscore/Diags.h>
#include <QUICLossDetector.h>
#define QUICCCDebug(fmt, ...) \
Debug("quic_cc", \
"[%s] " \
"window: %" PRIu32 " bytes: %" PRIu32 " ssthresh: %" PRIu32 " extra: %" PRIu32 " " fmt, \
this->_context.connection_info()->cids().data(), this->_congestion_window, this->_bytes_in_flight, this->_ssthresh, \
this->_extra_packets_count, ##__VA_ARGS__)
#define QUICCCError(fmt, ...) \
Error("quic_cc", \
"[%s] " \
"window: %" PRIu32 " bytes: %" PRIu32 " ssthresh: %" PRIu32 " extra %" PRIu32 " " fmt, \
this->_context.connection_info()->cids().data(), this->_congestion_window, this->_bytes_in_flight, this->_ssthresh, \
this->_extra_packets_count, ##__VA_ARGS__)
QUICNewRenoCongestionController::QUICNewRenoCongestionController(QUICContext &context)
: _cc_mutex(new_ProxyMutex()), _context(context)
{
auto &cc_config = context.cc_config();
this->_k_max_datagram_size = cc_config.max_datagram_size();
this->_k_initial_window = cc_config.initial_window();
this->_k_minimum_window = cc_config.minimum_window();
this->_k_loss_reduction_factor = cc_config.loss_reduction_factor();
this->_k_persistent_congestion_threshold = cc_config.persistent_congestion_threshold();
this->reset();
}
void
QUICNewRenoCongestionController::on_packet_sent(size_t bytes_sent)
{
SCOPED_MUTEX_LOCK(lock, this->_cc_mutex, this_ethread());
if (this->_extra_packets_count > 0) {
--this->_extra_packets_count;
}
this->_bytes_in_flight += bytes_sent;
}
bool
QUICNewRenoCongestionController::_in_congestion_recovery(ink_hrtime sent_time) const
{
return sent_time <= this->_congestion_recovery_start_time;
}
bool
QUICNewRenoCongestionController::is_app_limited()
{
// FIXME : don't known how does app worked here
return false;
}
void
QUICNewRenoCongestionController::on_packet_acked(const QUICPacketInfo &acked_packet)
{
// Remove from bytes_in_flight.
SCOPED_MUTEX_LOCK(lock, this->_cc_mutex, this_ethread());
this->_bytes_in_flight -= acked_packet.sent_bytes;
if (this->_in_congestion_recovery(acked_packet.time_sent)) {
// Do not increase congestion window in recovery period.
return;
}
if (this->is_app_limited()) {
// Do not increase congestion_window if application
// limited.
return;
}
if (this->_congestion_window < this->_ssthresh) {
// Slow start.
this->_context.trigger(QUICContext::CallbackEvent::CONGESTION_STATE_CHANGED, QUICCongestionController::State::SLOW_START);
this->_congestion_window += acked_packet.sent_bytes;
QUICCCDebug("slow start window changed");
} else {
// Congestion avoidance.
this->_context.trigger(QUICContext::CallbackEvent::CONGESTION_STATE_CHANGED,
QUICCongestionController::State::CONGESTION_AVOIDANCE);
this->_congestion_window += this->_k_max_datagram_size * acked_packet.sent_bytes / this->_congestion_window;
QUICCCDebug("Congestion avoidance window changed");
}
}
// additional code
// the original one is:
// CongestionEvent(sent_time):
void
QUICNewRenoCongestionController::_congestion_event(ink_hrtime sent_time)
{
// Start a new congestion event if packet was sent after the
// start of the previous congestion recovery period.
if (!this->_in_congestion_recovery(sent_time)) {
this->_congestion_recovery_start_time = Thread::get_hrtime();
this->_congestion_window *= this->_k_loss_reduction_factor;
this->_congestion_window = std::max(this->_congestion_window, this->_k_minimum_window);
this->_ssthresh = this->_congestion_window;
this->_context.trigger(QUICContext::CallbackEvent::CONGESTION_STATE_CHANGED, QUICCongestionController::State::RECOVERY);
this->_context.trigger(QUICContext::CallbackEvent::METRICS_UPDATE, this->_congestion_window, this->_bytes_in_flight,
this->_ssthresh);
}
}
// additional code
// the original one is:
// ProcessECN(ack):
void
QUICNewRenoCongestionController::process_ecn(const QUICPacketInfo &acked_largest_packet,
const QUICAckFrame::EcnSection *ecn_section)
{
// If the ECN-CE counter reported by the peer has increased,
// this could be a new congestion event.
if (ecn_section->ecn_ce_count() > this->_ecn_ce_counter) {
this->_ecn_ce_counter = ecn_section->ecn_ce_count();
// Start a new congestion event if the last acknowledged
// packet was sent after the start of the previous
// recovery epoch.
this->_congestion_event(acked_largest_packet.time_sent);
}
}
bool
QUICNewRenoCongestionController::_in_persistent_congestion(const std::map<QUICPacketNumber, QUICPacketInfo *> &lost_packets,
QUICPacketInfo *largest_lost_packet)
{
ink_hrtime period = this->_context.rtt_provider()->congestion_period(this->_k_persistent_congestion_threshold);
// Determine if all packets in the window before the
// newest lost packet, including the edges, are marked
// lost
return this->_in_window_lost(lost_packets, largest_lost_packet, period);
}
// additional code
// the original one is:
// OnPacketsLost(lost_packets):
void
QUICNewRenoCongestionController::on_packets_lost(const std::map<QUICPacketNumber, QUICPacketInfo *> &lost_packets)
{
if (lost_packets.empty()) {
return;
}
SCOPED_MUTEX_LOCK(lock, this->_cc_mutex, this_ethread());
// Remove lost packets from bytes_in_flight.
for (auto &lost_packet : lost_packets) {
this->_bytes_in_flight -= lost_packet.second->sent_bytes;
}
QUICPacketInfo *largest_lost_packet = lost_packets.rbegin()->second;
this->_congestion_event(largest_lost_packet->time_sent);
// Collapse congestion window if persistent congestion
if (this->_in_persistent_congestion(lost_packets, largest_lost_packet)) {
this->_congestion_window = this->_k_minimum_window;
}
}
bool
QUICNewRenoCongestionController::check_credit() const
{
if (this->_bytes_in_flight >= this->_congestion_window) {
QUICCCDebug("Congestion control pending");
}
return this->_bytes_in_flight < this->_congestion_window;
}
uint32_t
QUICNewRenoCongestionController::credit() const
{
if (this->_extra_packets_count) {
return UINT32_MAX;
}
if (this->check_credit()) {
return this->_congestion_window - this->_bytes_in_flight;
} else {
return 0;
}
}
uint32_t
QUICNewRenoCongestionController::bytes_in_flight() const
{
return this->_bytes_in_flight;
}
uint32_t
QUICNewRenoCongestionController::congestion_window() const
{
return this->_congestion_window;
}
uint32_t
QUICNewRenoCongestionController::current_ssthresh() const
{
return this->_ssthresh;
}
// [draft-17 recovery] 7.9.3. Initialization
void
QUICNewRenoCongestionController::reset()
{
SCOPED_MUTEX_LOCK(lock, this->_cc_mutex, this_ethread());
this->_bytes_in_flight = 0;
this->_congestion_window = this->_k_initial_window;
this->_congestion_recovery_start_time = 0;
this->_ssthresh = UINT32_MAX;
}
bool
QUICNewRenoCongestionController::_in_window_lost(const std::map<QUICPacketNumber, QUICPacketInfo *> &lost_packets,
QUICPacketInfo *largest_lost_packet, ink_hrtime period) const
{
// check whether packets are continuous. return true if all continuous packets are in period
QUICPacketNumber next_expected = UINT64_MAX;
for (auto &it : lost_packets) {
if (it.second->time_sent >= largest_lost_packet->time_sent - period) {
if (next_expected == UINT64_MAX) {
next_expected = it.second->packet_number + 1;
continue;
}
if (next_expected != it.second->packet_number) {
return false;
}
next_expected = it.second->packet_number + 1;
}
}
return next_expected == UINT64_MAX ? false : true;
}
void
QUICNewRenoCongestionController::add_extra_credit()
{
++this->_extra_packets_count;
}