Skip to content

Commit

Permalink
Update Congestion Control logic to draft-29
Browse files Browse the repository at this point in the history
  • Loading branch information
maskit committed Jul 22, 2020
1 parent 53da240 commit b4a0c8c
Show file tree
Hide file tree
Showing 11 changed files with 103 additions and 130 deletions.
4 changes: 2 additions & 2 deletions doc/admin-guide/files/records.config.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4102,13 +4102,13 @@ removed in the future without prior notice.
This is just for debugging. Do not change it from the default value unless
you really understand what this is.

.. ts:cv:: CONFIG proxy.config.quic.congestion_control.initial_window_scale INT 10
.. ts:cv:: CONFIG proxy.config.quic.congestion_control.initial_window INT 12000
:reloadable:

This is just for debugging. Do not change it from the default value unless
you really understand what this is.

.. ts:cv:: CONFIG proxy.config.quic.congestion_control.minimum_window_scale INT 2
.. ts:cv:: CONFIG proxy.config.quic.congestion_control.minimum_window_scale INT 2400
:reloadable:

This is just for debugging. Do not change it from the default value unless
Expand Down
7 changes: 1 addition & 6 deletions iocore/net/quic/Mock.h
Original file line number Diff line number Diff line change
Expand Up @@ -534,11 +534,6 @@ class MockQUICCongestionController : public QUICCongestionController
public:
MockQUICCongestionController() {}
// Override
void
on_packets_acked(const std::vector<QUICSentPacketInfoUPtr> &packets) override
{
}

virtual void
on_packets_lost(const std::map<QUICPacketNumber, QUICSentPacketInfoUPtr> &packets) override
{
Expand All @@ -552,7 +547,7 @@ class MockQUICCongestionController : public QUICCongestionController
{
}
virtual void
on_packet_acked(const QUICSentPacketInfo &acked_packet) override
on_packets_acked(const std::vector<QUICSentPacketInfoUPtr> &packets) override
{
}
void
Expand Down
20 changes: 4 additions & 16 deletions iocore/net/quic/QUICConfig.cc
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,8 @@ QUICConfigParams::initialize()
this->_ld_initial_rtt = HRTIME_MSECONDS(timeout);

// Congestion Control
REC_EstablishStaticConfigInt32U(this->_cc_max_datagram_size, "proxy.config.quic.congestion_control.max_datagram_size");
REC_EstablishStaticConfigInt32U(this->_cc_initial_window_scale, "proxy.config.quic.congestion_control.initial_window_scale");
REC_EstablishStaticConfigInt32U(this->_cc_minimum_window_scale, "proxy.config.quic.congestion_control.minimum_window_scale");
REC_EstablishStaticConfigInt32U(this->_cc_initial_window, "proxy.config.quic.congestion_control.initial_window");
REC_EstablishStaticConfigInt32U(this->_cc_minimum_window, "proxy.config.quic.congestion_control.minimum_window");
REC_EstablishStaticConfigFloat(this->_cc_loss_reduction_factor, "proxy.config.quic.congestion_control.loss_reduction_factor");
REC_EstablishStaticConfigInt32U(this->_cc_persistent_congestion_threshold,
"proxy.config.quic.congestion_control.persistent_congestion_threshold");
Expand Down Expand Up @@ -419,27 +418,16 @@ QUICConfigParams::ld_initial_rtt() const
return _ld_initial_rtt;
}

uint32_t
QUICConfigParams::cc_max_datagram_size() const
{
return _cc_max_datagram_size;
}

uint32_t
QUICConfigParams::cc_initial_window() const
{
// kInitialWindow: Default limit on the initial amount of data in
// flight, in bytes. Taken from [RFC6928]. The RECOMMENDED value is
// the minimum of 10 * kMaxDatagramSize and max(2* kMaxDatagramSize,
// 14600)).
return std::min(_cc_initial_window_scale * _cc_max_datagram_size,
std::max(2 * _cc_max_datagram_size, static_cast<uint32_t>(14600)));
return _cc_initial_window;
}

uint32_t
QUICConfigParams::cc_minimum_window() const
{
return _cc_minimum_window_scale * _cc_max_datagram_size;
return _cc_minimum_window;
}

float
Expand Down
5 changes: 2 additions & 3 deletions iocore/net/quic/QUICConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,8 @@ class QUICConfigParams : public ConfigInfo
ink_hrtime _ld_initial_rtt = HRTIME_MSECONDS(500);

// [draft-11 recovery] 4.7.1. Constants of interest
uint32_t _cc_max_datagram_size = 1200;
uint32_t _cc_initial_window_scale = 10; // Actual initial window size is this value multiplied by the _cc_default_mss
uint32_t _cc_minimum_window_scale = 2; // Actual minimum window size is this value multiplied by the _cc_default_mss
uint32_t _cc_initial_window = 1200 * 10;
uint32_t _cc_minimum_window = 1200 * 2;
float _cc_loss_reduction_factor = 0.5;
uint32_t _cc_persistent_congestion_threshold = 3;
};
Expand Down
14 changes: 8 additions & 6 deletions iocore/net/quic/QUICCongestionController.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,19 @@ class QUICCongestionController
};

virtual ~QUICCongestionController() {}
// Appendix B. Congestion Control Pseudocode
virtual void on_packet_sent(size_t bytes_sent) = 0;
virtual void on_packet_acked(const QUICSentPacketInfo &acked_packet) = 0;
virtual void process_ecn(const QUICAckFrame &ack, QUICPacketNumberSpace pn_space, ink_hrtime largest_acked_packet_time_sent) = 0;
virtual void on_packets_acked(const std::vector<QUICSentPacketInfoUPtr> &packets) = 0;
virtual void process_ecn(const QUICAckFrame &ack, QUICPacketNumberSpace pn_space, ink_hrtime largest_acked_packet_time_sent) = 0;
virtual void on_packets_lost(const std::map<QUICPacketNumber, QUICSentPacketInfoUPtr> &packets) = 0;
// The function signature is different from the pseudo code because LD takes care of most of the processes
virtual void on_packet_number_space_discarded(size_t bytes_in_flight) = 0;
virtual void add_extra_credit() = 0;
virtual void reset() = 0;
virtual uint32_t credit() const = 0;
virtual uint32_t bytes_in_flight() const = 0;

// These are additional and not on the spec
virtual void add_extra_credit() = 0;
virtual void reset() = 0;
virtual uint32_t credit() const = 0;
virtual uint32_t bytes_in_flight() const = 0;

// Debug
virtual uint32_t congestion_window() const = 0;
Expand Down
8 changes: 1 addition & 7 deletions iocore/net/quic/QUICContext.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,6 @@ class QUICCCConfigQCP : public QUICCCConfig
virtual ~QUICCCConfigQCP() {}
QUICCCConfigQCP(const QUICConfigParams *params) : _params(params) {}

uint32_t
max_datagram_size() const override
{
return this->_params->cc_max_datagram_size();
}

uint32_t
initial_window() const override
{
Expand Down Expand Up @@ -151,4 +145,4 @@ QUICPathManager *
QUICContext::path_manager() const
{
return _path_manager;
}
}
6 changes: 2 additions & 4 deletions iocore/net/quic/QUICLossDetector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -269,10 +269,12 @@ QUICLossDetector::_on_ack_received(const QUICAckFrame &ack_frame, QUICPacketNumb
this->_cc->process_ecn(ack_frame, pn_space, largest_acked->time_sent);
}

// ADDITIONAL CODE
// Find all newly acked packets.
for (const auto &info : newly_acked_packets) {
this->_on_packet_acked(*info);
}
// END OF ADDITIONAL CODE

auto lost_packets = this->_detect_and_remove_lost_packets(pn_space);
if (!lost_packets.empty()) {
Expand All @@ -297,10 +299,6 @@ QUICLossDetector::_on_packet_acked(const QUICSentPacketInfo &acked_packet)
QUICLDVDebug("[%s] Packet number %" PRIu64 " has been acked", QUICDebugNames::pn_space(acked_packet.pn_space),
acked_packet.packet_number);

if (acked_packet.in_flight) {
this->_cc->on_packet_acked(acked_packet);
}

for (const QUICSentPacketInfo::FrameInfo &frame_info : acked_packet.frames) {
auto reactor = frame_info.generated_by();
if (reactor == nullptr) {
Expand Down
137 changes: 67 additions & 70 deletions iocore/net/quic/QUICNewRenoCongestionController.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,28 +28,27 @@
#define QUICCCDebug(fmt, ...) \
Debug("quic_cc", \
"[%s] " \
"window: %" PRIu32 " bytes: %" PRIu32 " ssthresh: %" PRIu32 " extra: %" PRIu32 " " fmt, \
"window:%" PRIu32 " in-flight:%" 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 QUICCCVDebug(fmt, ...) \
Debug("v_quic_cc", \
"[%s] " \
"window: %" PRIu32 " bytes: %" PRIu32 " ssthresh: %" PRIu32 " extra: %" PRIu32 " " fmt, \
"window:%" PRIu32 " in-flight:%" 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, \
"window:%" PRIu32 " in-flight:%" 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();
Expand All @@ -76,46 +75,42 @@ QUICNewRenoCongestionController::_in_congestion_recovery(ink_hrtime sent_time)
}

bool
QUICNewRenoCongestionController::_is_app_limited()
QUICNewRenoCongestionController::_is_app_or_flow_control_limited()
{
// FIXME : don't known how does app worked here
return false;
}

void
QUICNewRenoCongestionController::on_packet_acked(const QUICSentPacketInfo &acked_packet)
QUICNewRenoCongestionController::_maybe_send_one_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;
}
// TODO Implement _maybe_send_one_packet
}

if (this->_is_app_limited()) {
// Do not increase congestion_window if application
// limited.
return;
}
bool
QUICNewRenoCongestionController::_are_all_packets_lost(const std::map<QUICPacketNumber, QUICSentPacketInfoUPtr> &lost_packets,
const QUICSentPacketInfoUPtr &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 (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;
QUICCCVDebug("slow start window chaged");
} 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;
QUICCCVDebug("Congestion avoidance window changed");
if (next_expected != it.second->packet_number) {
return false;
}

next_expected = it.second->packet_number + 1;
}
}

return next_expected == UINT64_MAX ? false : true;
}

// addtional code
// the original one is:
// CongestionEvent(sent_time):
void
QUICNewRenoCongestionController::_congestion_event(ink_hrtime sent_time)
{
Expand All @@ -129,20 +124,19 @@ QUICNewRenoCongestionController::_congestion_event(ink_hrtime sent_time)
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);
// A packet can be sent to speed up loss recovery.
this->_maybe_send_one_packet();
}
}

// additional code
// the original one is:
// ProcessECN(ack):
void
QUICNewRenoCongestionController::process_ecn(const QUICAckFrame &ack_frame, QUICPacketNumberSpace pn_space,
ink_hrtime largest_acked_time_sent)
{
// If the ECN-CE counter reported by the peer has increased,
// this could be a new congestion event.
if (ack_frame.ecn_section()->ecn_ce_count() > this->_ecn_ce_counter) {
this->_ecn_ce_counter = ack_frame.ecn_section()->ecn_ce_count();
if (ack_frame.ecn_section()->ecn_ce_count() > this->_ecn_ce_counters[static_cast<int>(pn_space)]) {
this->_ecn_ce_counters[static_cast<int>(pn_space)] = ack_frame.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.
Expand All @@ -154,16 +148,43 @@ bool
QUICNewRenoCongestionController::_in_persistent_congestion(const std::map<QUICPacketNumber, QUICSentPacketInfoUPtr> &lost_packets,
const QUICSentPacketInfoUPtr &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);
ink_hrtime congestion_period = this->_context.rtt_provider()->congestion_period(this->_k_persistent_congestion_threshold);
// Determine if all packets in the time period before the
// largest newly lost packet, including the edges, are
// marked lost
return this->_are_all_packets_lost(lost_packets, largest_lost_packet, congestion_period);
}

void
QUICNewRenoCongestionController::on_packets_acked(const std::vector<QUICSentPacketInfoUPtr> &packets)
{
SCOPED_MUTEX_LOCK(lock, this->_cc_mutex, this_ethread());

for (auto &packet : packets) {
// Remove from bytes_in_flight.
this->_bytes_in_flight -= packet->sent_bytes;
if (this->_in_congestion_recovery(packet->time_sent)) {
// Do not increase congestion window in recovery period.
continue;
}
if (this->_is_app_or_flow_control_limited()) {
// Do not increase congestion_window if application
// limited or flow control limited.
continue;
}
if (this->_congestion_window < this->_ssthresh) {
// Slow start.
this->_context.trigger(QUICContext::CallbackEvent::CONGESTION_STATE_CHANGED, QUICCongestionController::State::SLOW_START);
this->_congestion_window += packet->sent_bytes;
QUICCCVDebug("slow start window changed");
continue;
}
// Congestion avoidance.
this->_context.trigger(QUICContext::CallbackEvent::CONGESTION_STATE_CHANGED,
QUICCongestionController::State::CONGESTION_AVOIDANCE);
this->_congestion_window += this->_max_datagram_size * static_cast<double>(packet->sent_bytes) / this->_congestion_window;
QUICCCVDebug("Congestion avoidance window changed");
}
}

// additional code
Expand All @@ -172,11 +193,8 @@ QUICNewRenoCongestionController::on_packets_acked(const std::vector<QUICSentPack
void
QUICNewRenoCongestionController::on_packets_lost(const std::map<QUICPacketNumber, QUICSentPacketInfoUPtr> &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;
Expand Down Expand Up @@ -244,34 +262,13 @@ QUICNewRenoCongestionController::reset()
{
SCOPED_MUTEX_LOCK(lock, this->_cc_mutex, this_ethread());

this->_bytes_in_flight = 0;
this->_congestion_window = this->_k_initial_window;
this->_bytes_in_flight = 0;
this->_congestion_recovery_start_time = 0;
this->_ssthresh = UINT32_MAX;
}

bool
QUICNewRenoCongestionController::_in_window_lost(const std::map<QUICPacketNumber, QUICSentPacketInfoUPtr> &lost_packets,
const QUICSentPacketInfoUPtr &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;
}
for (int i = 0; i < QUIC_N_PACKET_SPACES; ++i) {
this->_ecn_ce_counters[i] = 0;
}

return next_expected == UINT64_MAX ? false : true;
}

void
Expand Down

0 comments on commit b4a0c8c

Please sign in to comment.