Implement L4S support and the Prague CCA - #2729
zoltanszatmary wants to merge 3 commits into
Conversation
4e00076 to
a53adec
Compare
|
Thanks for working on this! My review queue is a bit full so it might take me some time to give this a serious look. Also, I will be reviewing before getting to this, which also makes some changes to the congestion controller interface. Would be good if you can have a look at whether any changes you've made to the trait are incompatible (if so, maybe give feedback on that PR). |
|
Well, aside from that few parts of the code base where congestion control was touched by both branches (e.g., quinn-perf's I mean I extended it with 7 more methods in addition to the 3 new methods added in his patch. I've been thinking of better ways to achieve the same thing with less addition to the interface, but I couldn't come up with any so far. Or maybe one...? So currently, Prague relies on an inner
|
ee510aa to
1007ad2
Compare
a00daa6 to
57d8981
Compare
a774b02 to
ce843fe
Compare
ce843fe to
c2d8d9e
Compare
Extend the handling of ECN to support classic (RFC 3168) and L4S (RFC 9330) ECN validation, configuration, and congestion control algorithms (CCAs) that make use of ECN (e.g. scalable congestion control algorithms as defined in L4S). Changes: - Make ECN mode configurable via `TransportConfig` (Classic, L4S, or Disabled), enabling ECT(0) by default to preserve baseline CCA expectations. - Detect changes to ECT(0)/ECT(1) markings during validation, and drop the check preventing ECT(1) to support L4S. - Consult the active controller on ECN support, informing it of the pending ECN mode during querying, and pass ECN increments (rather than absolute counts) to decouple ECN processing from CCAs. - Add an external slow start threshold setter and fix CUBIC's cwnd manipulation to decouple the recovery boundary from epoch start and recompute W_max on cwnd changes. - Fix NewReno recovery clock-underflow by migrating `recovery_start_time` to `Option<Instant>`. - Add qlog event types for ECN-CE increments, L4S CCA alpha values, and recovery exits. - Expose RTT and jitter stats through send and receive streams. Co-Authored-By: Felician Nemeth <[email protected]>
TCP Prague emerged as the de facto implementation for scalable congestion control defined by the Low Latency, Low Loss, and Scalable Throughput (L4S) architecture (see RFC 9330 <https://datatracker.ietf.org/doc/html/rfc9330>). The need for L4S is motivated by the desire to prevent the build-up of large network queues that add significant delays to packets, while at the same time avoiding underutilization or severe losses (due to tail-drops or AQM-elicited early drops). In essence, L4S builds on three pillars: 1) support from the network: network queues in routers and other nodes along a given network path capable of isolating L4S-capable flows and treating their ECN marking logic according to the specification 2) support from the protocols: reinterpretation of ECN bits in the network- and transport-layer headers 3) support from the hosts (endpoints): use of so-called scalable congestion control algorithms that react to congestion signals (primarily ECN-CE marks on packets) according to the behavior defined in L4S (e.g., scaling back the congestion window in fractional amounts proportionally to the ratio of ECN-CE marked packets seen recently -- which, in a properly functional network queue, is proportional to the current queue length compared to the queue's capacity) Of course, Quinn is only concerned with the third point. Thus, this change implements the Prague congestion control algorithm (CCA) in Quinn to provide basic support for L4S, following the TCP Prague draft <https://datatracker.ietf.org/doc/draft-briscoe-iccrg-prague-congestion-control/04/>. Implementation notes: - NewReno is used as Prague's inner CCA, since its RTT-independent multiplicative-decrease behavior is a closer match for what the scalable congestion control response in L4S expects. - Congestion window reduction is paced on RTT, and general packet pacing for Prague-controlled flows is aligned with the draft's requirements. - Loss detection and ECN processing for the same incoming ACK can both fire `on_congestion_event` for what is really a single queue-overflow event (`detect_lost_packets` runs ahead of `process_ecn` in `Connection::on_ack_received`, and many AQMs drop and CE-mark off the same queue-occupancy signal). Without mitigation this compounds to a ~75% window reduction instead of the ~50% floor required by the TCP Prague draft §2.4.1. The ECN cooldown is now armed from the loss event itself, mirroring the existing handling of the reverse (ECN-then-loss) ordering. - Basic tracing for congestion window reduction and qlog emission of L4S-type events are added for observability. Co-Authored-By: Felician Nemeth <[email protected]>
Add ECN configuration, Prague support to `quinn-perf`. Changes: - Expose the `--ecn` CLI option to set the ECN mode. - Add `Prague` to the `--congestion` algorithm choices. - Track and report RTT and RTT variance metrics per stream to analyze latency characteristics under L4S similarly to iperf3. - Document L4S ECN requirements and configuration guidelines. Also includes two unrelated fixes surfaced while working on the above: - Close connections immediately once completed to prevent waiting on the 30-second idle timeout, which caused perf runs to be needlessly slow.
c2d8d9e to
9ee2daf
Compare
Summary
This change set introduces support for the Low Latency, Low Loss, and Scalable Throughput (L4S) architecture (RFC 9330) by exposing ECN configuration and implementing the Prague scalable congestion control algorithm.
Changes
The changes made are three-fold:
Classic,L4S, orDisabled) toTransportConfigand enablesECT(1)markings.quinn-perfSupport: Updatesquinn-perfto support Prague and configured ECN, tracks stream RTT/RTTvar, and optimizes client shutdown logic.Testing & Verification
To verify the implementation, I created a Mininet-based testbed (available here, though unfortunately, not everything is documented as of yet), the topology of which is seen in the figure below.
I measured six metrics in time (four endpoint-based, and one network-based).
Namely, one-way delay, RTT, CWND, network queue length, Prague's alpha value,
and the estimated throughput. The endpoint-based metrics were gathered and/or
derived from qlog entries (e.g., for calculating the one-way delay estimates,
the script paired up corresponding 1RTT packets -- assuming most packets were
not dropped). On the other hand, the queue length was monitored by a short
script running on the host taking the role of the router (configured with a
DualPI2 queue on its outbound interface facing the quinn-perf server host).
During the measurement scenario here, the quinn-perf client (on host h1) ran
for 60 seconds, continuously sending data to the quinn-perf server (on host h2)
through the network, with each round-trip experiencing a total delay of 24 ms,
and the network bandwidth being limited to 32 Mbps (with HTB).
The results of this run are depicted in the figure below.
Referenced Issues
Fixes #1498 #2487.