quic/congestion_control/NewReno.cpp (156 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #include <quic/congestion_control/NewReno.h> #include <quic/congestion_control/CongestionControlFunctions.h> #include <quic/logging/QLoggerConstants.h> namespace quic { constexpr int kRenoLossReductionFactorShift = 1; NewReno::NewReno(QuicConnectionStateBase& conn) : conn_(conn), ssthresh_(std::numeric_limits<uint32_t>::max()), cwndBytes_(conn.transportSettings.initCwndInMss * conn.udpSendPacketLen) { cwndBytes_ = boundedCwnd( cwndBytes_, conn_.udpSendPacketLen, conn_.transportSettings.maxCwndInMss, conn_.transportSettings.minCwndInMss); } void NewReno::onRemoveBytesFromInflight(uint64_t bytes) { subtractAndCheckUnderflow(conn_.lossState.inflightBytes, bytes); VLOG(10) << __func__ << " writable=" << getWritableBytes() << " cwnd=" << cwndBytes_ << " inflight=" << conn_.lossState.inflightBytes << " " << conn_; if (conn_.qLogger) { conn_.qLogger->addCongestionMetricUpdate( conn_.lossState.inflightBytes, getCongestionWindow(), kRemoveInflight); } } void NewReno::onPacketSent(const OutstandingPacket& packet) { addAndCheckOverflow( conn_.lossState.inflightBytes, packet.metadata.encodedSize); VLOG(10) << __func__ << " writable=" << getWritableBytes() << " cwnd=" << cwndBytes_ << " inflight=" << conn_.lossState.inflightBytes << " packetNum=" << packet.packet.header.getPacketSequenceNum() << " " << conn_; if (conn_.qLogger) { conn_.qLogger->addCongestionMetricUpdate( conn_.lossState.inflightBytes, getCongestionWindow(), kCongestionPacketSent); } } void NewReno::onAckEvent(const AckEvent& ack) { DCHECK(ack.largestNewlyAckedPacket.has_value() && !ack.ackedPackets.empty()); subtractAndCheckUnderflow(conn_.lossState.inflightBytes, ack.ackedBytes); VLOG(10) << __func__ << " writable=" << getWritableBytes() << " cwnd=" << cwndBytes_ << " inflight=" << conn_.lossState.inflightBytes << " " << conn_; if (conn_.qLogger) { conn_.qLogger->addCongestionMetricUpdate( conn_.lossState.inflightBytes, getCongestionWindow(), kCongestionPacketAck); } for (const auto& packet : ack.ackedPackets) { onPacketAcked(packet); } cwndBytes_ = boundedCwnd( cwndBytes_, conn_.udpSendPacketLen, conn_.transportSettings.maxCwndInMss, conn_.transportSettings.minCwndInMss); } void NewReno::onPacketAcked( const CongestionController::AckEvent::AckPacket& packet) { if (endOfRecovery_ && packet.outstandingPacketMetadata.time < *endOfRecovery_) { return; } if (cwndBytes_ < ssthresh_) { addAndCheckOverflow( cwndBytes_, packet.outstandingPacketMetadata.encodedSize); } else { // TODO: I think this may be a bug in the specs. We should use // conn_.udpSendPacketLen for the cwnd calculation. But I need to // check how Linux handles this. uint64_t additionFactor = (kDefaultUDPSendPacketLen * packet.outstandingPacketMetadata.encodedSize) / cwndBytes_; addAndCheckOverflow(cwndBytes_, additionFactor); } } void NewReno::onPacketAckOrLoss( const AckEvent* FOLLY_NULLABLE ackEvent, const LossEvent* FOLLY_NULLABLE lossEvent) { if (lossEvent) { onPacketLoss(*lossEvent); // When we start to support pacing in NewReno, we need to call onPacketsLoss // on the pacer when there is loss. } if (ackEvent && ackEvent->largestNewlyAckedPacket.has_value()) { onAckEvent(*ackEvent); } // TODO: Pacing isn't supported with NewReno } void NewReno::onPacketLoss(const LossEvent& loss) { DCHECK( loss.largestLostPacketNum.has_value() && loss.largestLostSentTime.has_value()); subtractAndCheckUnderflow(conn_.lossState.inflightBytes, loss.lostBytes); if (!endOfRecovery_ || *endOfRecovery_ < *loss.largestLostSentTime) { endOfRecovery_ = Clock::now(); cwndBytes_ = (cwndBytes_ >> kRenoLossReductionFactorShift); cwndBytes_ = boundedCwnd( cwndBytes_, conn_.udpSendPacketLen, conn_.transportSettings.maxCwndInMss, conn_.transportSettings.minCwndInMss); // This causes us to exit slow start. ssthresh_ = cwndBytes_; VLOG(10) << __func__ << " exit slow start, ssthresh=" << ssthresh_ << " packetNum=" << *loss.largestLostPacketNum << " writable=" << getWritableBytes() << " cwnd=" << cwndBytes_ << " inflight=" << conn_.lossState.inflightBytes << " " << conn_; } else { VLOG(10) << __func__ << " packetNum=" << *loss.largestLostPacketNum << " writable=" << getWritableBytes() << " cwnd=" << cwndBytes_ << " inflight=" << conn_.lossState.inflightBytes << " " << conn_; } if (conn_.qLogger) { conn_.qLogger->addCongestionMetricUpdate( conn_.lossState.inflightBytes, getCongestionWindow(), kCongestionPacketLoss); } if (loss.persistentCongestion) { VLOG(10) << __func__ << " writable=" << getWritableBytes() << " cwnd=" << cwndBytes_ << " inflight=" << conn_.lossState.inflightBytes << " " << conn_; if (conn_.qLogger) { conn_.qLogger->addCongestionMetricUpdate( conn_.lossState.inflightBytes, getCongestionWindow(), kPersistentCongestion); } cwndBytes_ = conn_.transportSettings.minCwndInMss * conn_.udpSendPacketLen; } } uint64_t NewReno::getWritableBytes() const noexcept { if (conn_.lossState.inflightBytes > cwndBytes_) { return 0; } else { return cwndBytes_ - conn_.lossState.inflightBytes; } } uint64_t NewReno::getCongestionWindow() const noexcept { return cwndBytes_; } bool NewReno::inSlowStart() const noexcept { return cwndBytes_ < ssthresh_; } CongestionControlType NewReno::type() const noexcept { return CongestionControlType::NewReno; } uint64_t NewReno::getBytesInFlight() const noexcept { return conn_.lossState.inflightBytes; } void NewReno::setAppIdle(bool, TimePoint) noexcept { /* unsupported */ } void NewReno::setAppLimited() { /* unsupported */ } bool NewReno::isAppLimited() const noexcept { return false; // unsupported } } // namespace quic