Repository navigation
quest(quic): split ECN into the uring fix, a study, and L4S #3820
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| # [S] Measure ECN on the backbone | ||
|
|
||
| ## Goal | ||
|
|
||
| A written verdict on classic ECN between relays: whether a marking | ||
| bottleneck reduces the rate before its queue overflows instead of after, | ||
| with the numbers, and whether Linode's and OVH's networks preserve ECT(0) | ||
| and CE between relays. No code lands; the verdict is recorded in | ||
| [L4S on the backbone](/quest/m2/quic/ecn.md), which acts on it. | ||
|
|
||
| ## Plan | ||
|
|
||
| A manual procedure on Linux, run as root, with the commands and what to | ||
| record written here so the dualpi2 run and any later provider re-check | ||
| repeat it. | ||
|
|
||
| - Bottleneck: two relays on the io_uring runtime across a netem link with a | ||
| rate limit and delay, once with `fq_codel` marking (`ecn` on) and once | ||
| with the same qdisc dropping (`noecn`). Measure queueing delay at the | ||
| bottleneck, goodput, and loss under a moq-bench media profile. The | ||
| question is whether the marking run holds a shorter queue at the same | ||
| goodput. | ||
|
Comment on lines
+17
to
+22
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift Measure the sender response over time. The stated question is whether marking reduces the rate before queue overflow. Queueing delay, aggregate goodput, and aggregate loss do not establish that ordering. Record the sending rate and CE or queue-depth time series, or define another observable that proves the rate reduction precedes overflow. 🤖 Prompt for AI Agents |
||
| - Providers: between a Linode relay and an OVH relay, `tcpdump -v` on each | ||
| end shows the TOS byte of arriving packets. Record whether ECT(0) arrives | ||
| intact in both directions and whether any CE appears; ECN is only alive | ||
| when both directions preserve the marks, since a stripped return path | ||
| means ACKs without counts. Repeat the capture for cross-provider and | ||
| same-provider pairs. | ||
| - Record the qdisc parameters, the moq-bench profile, the kernel version, | ||
| and the tcpdump summaries beside the numbers in the L4S quest's Plan. | ||
| If neither provider preserves the marks, say so there: L4S stays off and | ||
| the marking response is only a lab result. | ||
|
|
||
| ## Required | ||
|
|
||
| - [ECN on the io_uring UDP path](/quest/m2/quic/ecn-uring.md) - the relays | ||
| under test must mark | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| # [S] ECN on the io_uring UDP path | ||
|
|
||
| ## Goal | ||
|
|
||
| A relay on the io_uring runtime keeps ECN negotiated the way it already does | ||
| on the tokio runtime: its packets leave marked ECT(0), and marks on arriving | ||
| packets reach the QUIC stack, so the peer's ACKs carry ECN counts and noq | ||
| never disables ECN on a uring-to-uring session. A viewer's session is | ||
| unaffected. | ||
|
|
||
| ## Plan | ||
|
|
||
| noq-proto already does classic ECN end to end: every path starts with | ||
| `sending_ecn` on and marks ECT(0), `process_ecn` validates the peer's ACK | ||
| ECN counts, and an ACK without counts disables it. `noq-udp` carries the | ||
| mark through `IP_TOS` / `IPV6_TCLASS` on send and reads it back on receive | ||
| (`unix.rs:607-620`). | ||
|
|
||
| The gap is ours. `rs/moq-uring/src/udp.rs` builds its own control messages | ||
| and carries only `UDP_SEGMENT` on send and `UDP_GRO` on receive: no TOS or | ||
| TCLASS out, no `IP_RECVTOS` / `IPV6_RECVTCLASS` in. Nothing in `moq-uring` | ||
| mentions ECN, and `udp::TxBuf::send(len, to, segment)` has no slot for the | ||
| codepoint, so on the io_uring path the peer never sees a mark, its ACKs | ||
| carry no counts, and noq turns ECN off within the first ACK. | ||
|
|
||
| - Send: `TxBuf::send` takes the codepoint beside the segment size and writes | ||
| `IP_TOS` (v4) or `IPV6_TCLASS` (v6) into the same control buffer as | ||
| `UDP_SEGMENT`, so a GSO train carries the mark on every segment. The noq | ||
| and quinn adapter passes `Transmit::ecn` at both call sites, | ||
| `rs/moq-uring/src/quic/quinn/endpoint.rs:377` and | ||
| `rs/moq-uring/src/quic/quinn/connection.rs:797`. quiche has no ECN send | ||
| API, so its two callers (`rs/moq-uring/src/quic/quiche/endpoint.rs:476` | ||
| and `rs/moq-uring/src/quic/quiche/connection.rs:843`) pass no codepoint | ||
| and change only to match the signature. | ||
| - Receive: enable `IP_RECVTOS` and `IPV6_RECVTCLASS` on the socket and parse | ||
| the TOS or TCLASS control message next to `UDP_GRO`. `udp::Packet` gains | ||
| an `ecn` accessor (one mark per completion; a GRO batch shares it), and | ||
| the noq and quinn adapter threads it into both `Endpoint::handle` calls at | ||
| `quinn/endpoint.rs:240-252`, which pass `None` today. | ||
| - `moq-uring` is 0.0.1 and unpublished, so the `udp` module's signature | ||
| changes on `main`. No config, wire, or doc changes. | ||
| - Regression, in `rs/moq-uring/tests`: a socket pair through the worker | ||
| sends with ECT(0) and the receiving `Packet::ecn` reads it back, over v4 | ||
| and v6, for a single datagram and for a GSO train; and a datagram sent | ||
| with CE arrives as CE. Fails today. noq-proto 1.2 exposes no | ||
| ECN state, so the session-level check waits for the fork; the `rs uring` | ||
| nightly lane already runs this crate and gates on the kernel floor. | ||
|
|
||
| ## Related | ||
|
|
||
| - [Measure ECN on the backbone](/quest/m2/quic/ecn-measure.md) - the study | ||
| this fix unblocks | ||
| - [L4S on the backbone](/quest/m2/quic/ecn.md) - the fork-side half |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,45 +1,42 @@ | ||
| # [M] ECN on the backbone | ||
| # [M] L4S on the backbone | ||
|
|
||
| ## Goal | ||
|
|
||
| Relay-to-relay sessions keep ECN negotiated on the io_uring runtime the way | ||
| they already do on the tokio runtime, and a marking network reduces the rate | ||
| before a queue overflows instead of after. L4S (ECT(1) with a scalable | ||
| response) is measured behind an option. Paths that strip or mangle the marks | ||
| fall back to no ECN, and a viewer's session is unaffected. | ||
| Relay-to-relay sessions can mark ECT(1) with a scalable response, measured | ||
| behind an option that is off by default, and a deployment whose network | ||
| mangles marks can turn ECN off explicitly. Paths that strip or mangle the | ||
| marks fall back to no ECN, and a viewer's session is unaffected. | ||
|
Comment on lines
+5
to
+8
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Clarify the default ECN mode. The Goal says the option is off by default, but the Plan specifies Also applies to: 22-24 🤖 Prompt for AI Agents |
||
|
|
||
| ## Plan | ||
|
|
||
| noq-proto already does classic ECN end to end: every path starts with | ||
| `sending_ecn` on and marks ECT(0) (`connection/paths.rs:305`, | ||
| `connection/mod.rs:1260`), `process_ecn` validates the peer's ACK ECN counts | ||
| and an ACK without counts disables it (`:3146`, `:3101`), and a CE increase | ||
| reaches `Controller::on_congestion_event(is_ecn = true)`, which Cubic and | ||
| BBR3 both handle. `noq-udp` carries the mark through `IP_TOS` / | ||
| `IPV6_TCLASS` on send and reads it back on receive (`unix.rs:607-620`). | ||
| Classic ECN is already end to end once the | ||
| [io_uring path marks](/quest/m2/quic/ecn-uring.md); this quest is the | ||
| fork-side half. noq-proto has no ECN knob: `sending_ecn` is hardcoded on | ||
| per path and only an ACK without counts turns it off, so both `off` and | ||
| `ect1` need the fork. | ||
|
|
||
| The gap is ours. `rs/moq-uring/src/udp.rs` builds its own cmsgs and carries | ||
| no TOS or TCLASS on send and no `IP_RECVTOS` / `IPV6_RECVTCLASS` on receive, | ||
| so on the io_uring path the peer never sees a mark, its ACKs carry no counts, | ||
| and noq turns ECN off within the first ACK. | ||
|
|
||
| - moq-uring: set the codepoint from `Transmit::ecn` in the send cmsg beside | ||
| the GSO segment size, enable the receive-side TOS/TCLASS cmsg, and fill | ||
| `RecvMeta::ecn`, so the io_uring path matches `noq-udp`. Regression: a | ||
| uring-to-uring session reports ECN still enabled after the handshake. | ||
| - In the fork: an `Ect1` marking option and the accounting to keep the two | ||
| codepoints apart, so an L4S response (proportional to the CE fraction per | ||
| RTT, per RFC 9330 to 9332) can be tried as a `Controller` change without a | ||
| transport change. Off by default. | ||
| - `moq-tokio`'s `[quic]` section gains `ecn = off | ect0 | ect1`, ect0 by | ||
| default (today's behavior on tokio), so a deployment whose network mangles | ||
| marks can turn it off explicitly. | ||
| - Measure on a netem bottleneck with a marking qdisc (`fq_codel`, then | ||
| `dualpi2` for L4S) against the same bottleneck dropping: queueing delay, | ||
| goodput, loss. Record whether Linode's and OVH's networks preserve the | ||
| marks between relays; if neither does, L4S stays off and the result is | ||
| written down. | ||
| default (today's behavior on tokio), and `doc/bin/relay/config.md` | ||
| documents it in the same PR. | ||
| - Measure on the netem bottleneck from the | ||
| [ECN study](/quest/m2/quic/ecn-measure.md) with `dualpi2` marking against | ||
| the same bottleneck dropping: queueing delay, goodput, loss. The study's | ||
| provider verdict decides whether the result matters outside the lab; | ||
| if neither Linode nor OVH preserves the marks, L4S stays off and the | ||
| result is written down here. | ||
| - ECN visibility stays on the wire: the study observes marks with tcpdump, | ||
| and exposing per-path ECN state in stats is a later quest if operators | ||
| need it. | ||
|
|
||
| ## Required | ||
|
|
||
| - [Fork noq](/quest/m2/quic/fork.md) - the `Ect1` option lives there | ||
| - [Fork noq](/quest/m2/quic/fork.md) - the `Ect1` option and the `off` knob | ||
| live there | ||
| - [ECN on the io_uring UDP path](/quest/m2/quic/ecn-uring.md) - the relays | ||
| must mark before a response can be measured | ||
| - [Measure ECN on the backbone](/quest/m2/quic/ecn-measure.md) - the | ||
| provider verdict this quest acts on | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
Add executable commands to the study procedure.
The document says that it contains the commands, but it only names
netem,fq_codel,moq-bench, andtcpdump. Add the setup, capture, workload, and teardown commands so another operator can reproduce both runs.🤖 Prompt for AI Agents