Congestion avoidance
mb-ecn is a congestion-avoidance (CA) policy for the Ouroboros unicast
IPCP. It lives in the IPC Process: the flow-and-retransmission task
(FRCP) provides reliability, in-order delivery and flow control
end-to-end; congestion avoidance is orthogonal and runs per IPCP
layer. Within a layer, the IPCP controls aggregates, one loop
per (destination address, QoS cube), shared by every flow toward that
destination. The alternative policy is nop (no congestion
avoidance).
This document describes the algorithm: what the policy does and why. The implementation follows it (Section 12 maps the pieces to source files). It does not claim properties the algorithm does not have; Section 8 states the known gaps explicitly.
The keywords "MUST", "SHOULD", "MAY" etc. are used only where they appear in all capitals, per BCP 14 (RFC 2119, RFC 8174).
Notation
r- Paced send rate, bytes per second.
C- Bottleneck capacity, bytes per second.
S- Packet size in bytes.
q- Standing queue occupancy at the bottleneck, in packets.
Q- Marking quantum, packets per mark unit (4).
ecn- Per-packet congestion mark set by a forwarder, an unsigned integer
(on-wire field is 8-bit).
ece- Receiver-side congestion estimate, fixed point with LSB = 1/32 of an
ecnunit, i.e.ece = 32 × (time-mean ecn).
cap8- Path-capacity code carried per packet, quarter-log2:
C ≈ 2^(cap8 / 4) bytes/s, 0 = unknown.
W- Receiver averaging window, nanoseconds.
a- Additive-increase slope, bytes/s per second.
M- Full-congestion reference level of
ece(512). Δt- Elapsed wall-clock time between two events, nanoseconds.
τ- Path round-trip time (feedback delay). The controller does not
measure it.
Rate is the control variable throughout.
All concrete figures in this document — window bounds, horizons, rate thresholds — are instances of the parameter values in Section 11. The mechanisms and the relations between parameters are what the algorithm fixes; retuning the parameters moves the figures together.
Overview
mb-ecn is a rate-based, ECN-driven controller with three moving parts, one at each point of the loop:
sender forwarder
+--------------------------------+ +--------------------------+
| SFQ virtual-clock pacer | | ecn = MAX(ecn, queue/Q) |
| rate r, paced per packet | data | (deepest queue wins) |
| AIMD + PD control on r |------>| cap8 = MIN(cap8, link) |
+--------------------------------+ +--------------------------+
^ |
| ece + cap8 feedback | marked data
| v
+--------------------------------+ +--------------------------+
| reads downstream ece, |<------| receiver |
| updates the rate on feedback, | ece | time-mean of ecn over |
| scales floor/slope to cap8 | cap8 | an adaptive window |
+--------------------------------+ +--------------------------+
- The forwarder marks each packet with a multi-bit magnitude
proportional to its standing queue, and MIN-stamps its measured outgoing-link capacity into the same PCI.
- The receiver turns the stream of marks into a time-averaged
congestion estimate ece and feeds it back to the sender,
together with the window minimum of the path-capacity codes.
- The sender paces its rate with a start-time fair-queuing (SFQ)
virtual clock, and adjusts that rate with additive-increase /
multiplicative-decrease (AIMD) driven by ece; the rate floor
and the additive slope derive from the fed-back path capacity.
The controller runs on packet sends and on feedback arrivals. All increase/decrease steps are scaled by elapsed wall-clock time, which is what makes the allocation independent of RTT (see Section 6).
The unit of control is the aggregate, kept in the IPCP per
(destination address, QoS cube). Every flow toward that
destination at that QoS shares one controller and one rate: a new
flow joins the aggregate at its current rate, and a departing flow
leaves the rate to the others. The pacer divides the aggregate rate
fairly across the member flows (SFQ), so per-flow fairness within an
aggregate is a scheduler property, distinct from the rate law
described here.
2. Forwarder marking
A forwarder marks a packet as a function of the packets already queued on the outgoing link:
mark = queued / Q (Q = 4 packets) ecn = MAX(ecn, mark) (saturating, 8-bit field)
The quantum Q is a layer-wide constant: the marks it
quantizes are combined by MAX across hops, so all forwarders in a
layer MUST use the same quantum for the signal to be comparable.
Two properties:
- Multi-bit magnitude. The mark is an integer proportional to
queue depth, quantized in steps of Q packets: a coarse
readout of the standing queue.
- MAX along the path. A packet crossing several forwarders carries
the maximum mark, i.e. the deepest queue on its path. MAX keeps the signal range-stable and monotone across hops.
There is a dead zone: for queued < Q the mark is 0.
Capacity stamping
Each forwarder also measures the capacity of its outgoing link and
stamps it into the packet. The estimate is a busy-period drain rate:
the queue is observed at most once per millisecond, and a measurement
window stays open until 16 packets have drained, so its length
self-scales with the link rate — the millisecond cadence
bounds it on fast links, the 16-packet drain time on slow ones
(~19 ms at 10 Mbit/s). A window in which more than 1/8 of
the arrivals found the queue empty is discarded as unsaturated;
occasional empty observations (token-bucket shapers grazing zero) are
tolerated. The drain rate of a saturated queue is the link rate; a max
filter with slow decay (1/16 per window) converges on it from below. A
window that opened or closed on an empty queue may have drained into
buffers below at above-wire rate, so it may lower the estimate but
never raise it. The code on the wire is quarter-log2
(cap8 = 4 log2 C, ~19% per step, 0 = unknown) and each hop
MIN-combines its own code into the byte, so a packet arrives carrying
the bottleneck's rate. A hop that has never been backlogged stamps
nothing; since only backlogged hops matter, the signal exists exactly
when it is needed.
3. Receiver estimate
The receiver converts marks into a smoothed estimate ece over a
time window W that adapts to the incoming byte rate. It is
a boxcar (rectangular) time-integral mean. Per packet, with
Δt the gap since the previous packet:
A += ecn * MIN(Δt, W) dwell-weighted; one packet
weighs at most one window
B += S accumulate bytes this window
...
at window close (elapsed >= W, or B >= 2 * B_target with at
least the window floor elapsed):
ece = 32 * A / elapsed mean over the actual window
W += (B_target * elapsed / B - W) / 4 nudge toward the size
that holds the target
W = clamp(W, ~1.05 ms, ~4.3 s)
A = 0 ; B = 0 hard reset
At each close the next window is nudged by a quarter-weight moving
average toward the size that would hold about 16 packets at the
measured byte rate, from an initial ~67 ms. The target is byte-based (B_target = 16,000 bytes), so
"16 packets" is exact only at ~1000-byte packets, and the window
converges to it over several closes. The mean always divides by the
actual elapsed window, so a rate step leaves the current
estimate exact and only re-sizes the next window. The effect is a roughly constant sample
count — ~16 packets — from about 30 kbit/s to 122 Mbit/s;
above that the window floors at ~1 ms (a cadence still carrying
thousands of samples at 10–100 GbE), and below ~30 kbit/s it
saturates at the ~4.3 s ceiling. The averaging clock stretches with
the flow the way TCP's ACK clock stretches with the RTT. A speed-up
escapes a stretched window early: once twice the target bytes arrive
the window closes anyway (the window floor keeps that cadence bounded
at high rate). The fed-back ece is thus a piecewise-constant
staircase whose step period tracks the rate. Two edge cases:
- Onset (first mark after an idle estimate) emits the
instantaneous mark undiluted, so a starting queue is reported without a full window of delay.
- Gap longer than 4 windows resets the window and emits the raw
sample. The threshold is rate-relative: idle means a few current windows of silence, so a slow flow's normal inter-packet gap never reads as idle, while the in-window dwell clamp above bounds what a genuine pause can add to the mean before the restart fires.
Alongside the mark integral, the receiver keeps the MIN of the nonzero
capacity codes seen in the current window; each emitted ece
carries that minimum (the raw packet's code on onset/gap restarts) and
the fold then resets, so a reroute to a faster path can raise the
fed-back capacity within one window.
The estimate is fed back to the sender on the reverse flow.
4. Sender pacer
The sender paces with an SFQ virtual clock. The context keeps a
virtual time V; each flow keeps a finish tag F:
V += r * Δt virtual time advances by bytes served s = MAX(F, V) a packet behind the clock starts now F = s + S the one after it queues behind wait = (s - V) / r time until this packet may go
The wait becomes the flow's scheduling deadline: a packet
already behind the virtual clock is sent immediately
(wait = 0); one ahead waits, deferring only its own flow.
A clock advance across an idle gap longer than 50 ms credits at most
the calling flow's owed lead plus one burst (50 ms of service, at
least one packet): a paced flow slower than one packet per 50 ms
receives its true elapsed service — a time-capped credit
would decay such a flow without bound — while an idle flow still
cannot bank an unbounded burst.
The virtual time V is shared by every flow in the context;
the finish tags are per flow. Because every flow computes its start
tag s against the same V, a flow that has just sent
carries a finish tag ahead of V and waits, while an idle flow
(finish tag at or behind V) starts immediately — this is
what fairly divides the aggregate rate r across them.
5. Rate control (AIMD + PD)
The rate law runs on packet sends, at most once per millisecond, and also on feedback arrivals so that a sender with no traffic of its own still reacts. Each increase term and the proportional decrease are scaled by elapsed wall-clock time, each by its own clock: the increase terms use time banked and capped at 50 ms (an idle gap cannot bank an unbounded ramp), while the proportional decrease uses the uncapped elapsed time (a starved sender still cuts by the right amount). The one-sided derivative (Section 5.3) is a per-delta term.
5.1. Slow start
Before the first congestion signal the rate ramps exponentially with a 20 ms time constant:
r += r * Δt / T_ss (T_ss = 20 ms)
Slow start ends the first time a congestion signal arrives — fed back from the receiver or observed at the sender's own first hop — and never resumes.
Slow start belongs to the aggregate, so it runs once per (destination, QoS cube), on the first flow. A flow arriving at an established aggregate rides the existing estimates: it enters at the aggregate's current rate and receives its fair share of it through the pacer (Section 4), with no probing of its own.
5.2. Increase: additive plus proportional probe (always on)
Past slow start, two increase terms are applied every step, both regardless of congestion:
r += a * Δt additive; a defaults to 2^16 B/s^2 r += r * Δt / T_probe proportional; T_probe = 8 s
The additive term is a fixed per-context slope (bytes/s per second),
scaled to the path capacity (Section 5.5). The proportional probe
grows the rate by a fixed fraction per unit time (e-folds over
T_probe): a flow recovers in the same number of steps at any
link rate; the fixed additive term's effect vanishes relative to a
fast link. Both are the "probing"
pressure the multiplicative decrease balances at equilibrium; the
probe's cost is a rate-independent standing-queue floor (Section 7).
5.3. Multiplicative decrease (proportional + derivative)
With congestion level m (the fed-back ece, or a local
first-hop mark as fallback), the decrease is the sum of a proportional
term and a one-sided derivative term — a PD controller on the
congestion signal:
mark = MIN(m, M) M = 512 rise = MAX(m - m_prev, 0), capped at M one-sided derivative
cut = r * rise / (4 * M) derivative, gain 1/4 cut += r * mark * Δt_ms / (1000 * M) proportional cut = MIN(cut, r / 2) at most a halving
r -= cut m_prev = m
- The proportional term is scaled by honest elapsed time in
milliseconds, so a starved sender that has not run the controller for a while still cuts by the right amount.
- The derivative term (
rise) reacts to an increase in
the congestion level between samples; it is one-sided and per-delta. It sharpens the reaction at the onset of congestion.
- The cut is clamped to
r/2per step, bounding the decrease to
a halving.
M = 512 = 32 × 16 defines "full congestion" as a mean
ecn of 16, i.e. a mean queue of 16 × Q = 64 packets.
5.4. Staleness
Feedback arrives once per receiver window, and the window stretches
with the flow's byte rate (Section 3) — so the staleness horizon
must stretch with it, or a slow flow's mark would age out between
feedbacks and the rate would ramp against a congested path. The
sender mirrors the receiver: the horizon is four target windows of
bytes at the current rate, floored at ~268 ms so fast flows keep a
fixed horizon; at the default rate floor it is ~8 s. A signal older
than the horizon is aged out (both the fed-back estimate and the
local mark), freeing the rate to ramp once congestion clears. The
rate is clamped to
[r_min, r_max] on every step.
5.5. Capacity-derived floor and slope
The fed-back capacity code sets the two rate-scale constants of the controller, per context:
target = decode(cap8) / 32 clamped to [2^13, 2^32] B/s r_min += (target - r_min) / 2 per feedback (moving average) a = r_min
The defaults are fixed constants (r_min = 2^13 B/s,
a = 2^16 B/s^2), used on paths that never report a capacity;
with the estimator live the fairness floor and the post-cut recovery
slope scale with the bottleneck, and capacity engages wherever
C/32 clears the default floor, i.e. above ~2 Mbit/s. A
capacity older than 16 staleness horizons (~4.3 s for fast flows)
reverts both to the defaults. That horizon deliberately outlives the
feedback horizon: feedback stops the moment the marks clear, which is
exactly when the recovery slope is needed; the onset-fresh capacity
(Section 3) re-seeds it on the first mark of the next episode either
way.
5.6. Ramp and recovery
Three mechanisms set how fast an aggregate reaches a fast link's rate, each owning one regime:
- From scratch: slow start. The exponential ramp reaches any
physical link rate in well under a second (~200 ms from the seed to 10 Gbit/s; each further doubling of link speed costs one more ~14 ms doubling time). On an uncongested path nothing marks, so slow start runs until the aggregate arrives at its bottleneck.
- After a cut: the probe. A decrease is at most a halving per
step, and the mark ages out on the rate-relative horizon once
congestion clears (Section 5.4); the proportional probe then heals
a halving in T_probe · ln 2 ≈ 5.5 s at any
link rate.
- At the bottom: the capacity floor. The rate sits at most a
factor 32 below a measured bottleneck, and the additive slope
refills C/32 per second, so the deepest hole is bounded:
floor to full capacity in tens of seconds, a halving in seconds.
These compose because a deep cut requires sustained congestion, sustained congestion backlogs the bottleneck queue, and a backlogged queue is exactly when its capacity is measured and stamped (Section 2): whenever the controller is cut deep, the scaled floor is live. The default floor only governs paths that never congested the sender — where there is nothing to recover from. This is why slow start can end for good: the floor and the probe cover every later recovery.
5.7. Regimes
+-------------+ first mark seen +----------------+
| slow start |--------------------->| congestion |
| r *= e^(t/T)| | avoidance |
+-------------+ | |
| m == 0 -> AI |
| m > 0 -> AIPD |
+----------------+
m = fed-back ece, or the local first-hop mark when ece == 0
6. RTT behaviour
Because every increase and decrease is scaled by wall-clock time, two flows of different RTT that share a bottleneck obey the same rate law and converge to the same rate. The steady-state allocation is RTT-independent.
This is a property of the equilibrium, not of the dynamics. The
feedback delay in the loop is the path RTT τ, and the
controller does not measure or compensate for it. At low rate the
receiver window (up to its ~4.3 s ceiling) is the dominant lag —
the loop is slow there in proportion to how slow the flow is, exactly
as a long-RTT TCP is — while as the rate rises the window shrinks
toward its ~1 ms floor, so the window's contribution to the loop delay
falls with capacity and the path RTT τ becomes the
irreducible term. Two mechanisms keep the loop damped: the adaptive
window removes the delay-dominated corner at low τ, and a
bucketed rate-velocity damping (a washout: each ~31 ms bucket
pulls the rate a quarter of the way back toward its value at the
previous bucket) supplies the damping the otherwise
near-double-integrator lacks, which is what holds the loop together at
high capacity and moderate τ. What remains is the loop gain
√G ∝ √C: at very high capacity and large
τ the √G · τ phase budget is the
binding constraint, and no window or damping trick removes it (see
Section 8). A precise statement is therefore:
RTT-fair in equilibrium, and dynamically stable up to a
capacity-dependent RTT limit set by √G · τ.
7. Fairness
At equilibrium the two increase terms balance the proportional
decrease, a + r/T_probe = r · m*/M, which pins the
congestion level to m* = M · (a/r + 1/T_probe) and, since
ece = 8q (one mark unit per Q = 4 packets, 32 fixed-point
steps per unit), a standing queue of
q* = 64 * a / r + 64 / T_probe (packets, r in bytes/s)
The additive part (64 · a / r) vanishes with rate under
the default slope; with the capacity-derived slope (a = C/32,
Section 5.5) it becomes 2 C / r — about two packets per
competing flow, rate-independent. The proportional-probe part
(64 / T_probe) is a rate-independent floor (~8 packets at
8 s). That floor is the cost of rate-independent convergence, and it
keeps q* above the marking threshold at high rate.
Two flows at the same bottleneck see the same m*, so each
solves r_i = a / (m*/M - 1/T_probe) to the same rate. Under
the network-utility-maximization view this corresponds to
proportional fairness (equal weights), which is max-min fair at
a single bottleneck.
Measurement fidelity at low rate comes from the estimator itself:
the averaging window stretches with the flow
(Section 3), so a CA-limited flow keeps ~16 packets per window at any
rate down to ~30 kbit/s, and the rate-relative gap threshold keeps its
inter-packet spacing from reading as idle. The default floor
(2^13 B/s) only bounds the extremes (window ceiling,
staleness horizon, pacer arithmetic). The floor scales with the
fed-back path capacity (r_min = C/32, Section 5.5), so
roughly 32 CA-limited flows fit above it at any link class. Flows
whose paths differ in bottleneck class derive different slopes, so
fairness across them is capacity-weighted; see
Section 8.
9. Architectural fit
Each property the preceding sections build toward attaches to a structural feature of the recursive architecture. This section makes the mapping explicit.
- Congestion avoidance is fully orthogonal to ARQ and to flow
control. Three concerns, three mechanisms, two scopes: FRCP provides retransmission (ARQ) and flow control (the peer pacing the sender) end-to-end, per flow; congestion avoidance runs in the IPCP, per aggregate. Each signal means one thing. A loss triggers a retransmission and nothing else — a lossy link reads as lossy, with the congestion verdict left to the marks (Section 8's "no loss response" is this orthogonality stated from the other side). The peer's flow-control window paces the endpoint and nothing else — back-pressure from a slow receiver stays distinct from congestion in the network. A retransmitted packet is ordinary traffic to the pacer. TCP folds all three into one window machine, where the receive window bounds the congestion window and loss serves as both reliability trigger and congestion signal; here each mechanism can be reasoned about, tested and evolved alone.
- Congestion avoidance sits below application choice. Every
flow in the layer is paced by the same rate law, whatever its QoS: a greedy raw sender shares a bottleneck fairly with a reliable stream because the control is a property of the layer, on the aggregate, rather than a courtesy of the endpoint transport.
- A layer defines its own PCI, so the signal can be rich.
Every member of a layer is enrolled into it: the layer is a single administrative domain by construction, at whatever scope it spans, and its header is layer-internal. Forwarders therefore write a multi-bit queue magnitude and a capacity byte directly into the packet. The datacenter schemes of Section 10 require exactly such a domain and find it only inside one operator's fabric; a recursive layer supplies it everywhere.
- Layer-internal addressing makes the aggregate well-defined.
Flows in a layer run between layer addresses, so the (destination, QoS cube) aggregate — RFC 3124's macroflow — falls out of the naming structure. Controller state scales with destinations, and each QoS cube keeps its own loop, so service classes keep separate fates.
- Flows are allocated, so feedback has a channel. Every flow
has state at both ends and a reverse direction; the receiver's
ece and the path capacity ride the flow allocator's
existing exchange.
- There is no transport ack clock, and none is relied on. The
layer must control raw flows, which carry no acknowledgements at all, so the controller paces on wall-clock time — and that constraint yields the RTT-independent allocation of Section 6. An architectural restriction becomes the controller's distinguishing property.
- Recursion scales the scheme. Each layer controls congestion
over its own scope and timescale; many N-flows ride one N-1 flow, so aggregation compounds down the stack. The capacity a layer measures at its egress queue is the rate the layer below actually provides — a paced, shared lower flow rather than a nominal wire speed — so the signal stays meaningful at every level, and pushback cascades layer by layer.
The fit has a boundary: at a shim over legacy media the layer below neither enrolls nor marks, which is the deployment edge behind Section 8's no-loss-response limitation.
10. Heritage and positioning
mb-ecn is best understood as a rate-based ECN controller in the DECbit / QCN line, adapted to an acknowledgement-less recursive layer.
The core of the scheme dates to the first Ouroboros
congestion-avoidance policy (2020): a multi-bit queue-depth mark
relayed by forwarders to the receiver, a smoothed multi-bit
ece fed back on the reverse flow at the same 1/32 fixed
point used today, and a sender that paced packets from a time budget
and ran slow start, additive increase and multiplicative decrease on
wall-clock time slots — rate-based and acknowledgement-free from
the start, with RTT-independent allocation already an objective. The
present algorithm keeps that skeleton and reworks every estimator
around it: per-path aggregation with SFQ pacing, the rate-adaptive
boxcar receiver, elapsed-time-scaled AIMD with a derivative term and
rate-velocity damping, and the in-band capacity signal.
- DECbit (Ramakrishnan & Jain, 1988; ToCS 1990) established
binary congestion feedback set by the switch on queue occupancy. mb-ecn keeps the switch-sets-on-queue idea and carries a magnitude.
- The Congestion Manager (Balakrishnan et al., SIGCOMM 1999;
RFC 3124, 2001) aggregates congestion state at the end host: one controller per macroflow, a scheduler apportioning its rate across the member streams, and new streams joining at the ensemble's current state instead of probing from scratch. mb-ecn's (destination, QoS cube) aggregate is the same idea placed inside the IPCP: every layer manages its own macroflows, recursively, the SFQ pacer plays the CM scheduler's role (Section 4), and a joining flow rides the aggregate's estimates (Section 5.1). The CM gathers its signal from transport feedback at the edge; mb-ecn reads it off the wire from the forwarders.
- DCTCP (Alizadeh et al., SIGCOMM 2010) recovers a queue
magnitude at the endpoint by averaging a single threshold bit. mb-ecn instead reads the magnitude off the wire, then time-averages it at the receiver. The additive-increase / proportional-decrease structure is shared.
- QCN (IEEE 802.1Qau, 2010) and DCQCN (Zhu et al., SIGCOMM
2015) contribute the rate-based (not window-based) ECN control skeleton. mb-ecn's proportional decrease plus a probing increase mirrors that skeleton; the increase here is wall-clock scaled rather than byte-counter/timer based.
- HPCC (Li et al., SIGCOMM 2019) and PowerTCP (Addanki et
al., NSDI 2022) combine a queue term and a rate term (via in-band telemetry or delay). mb-ecn now carries a coarse in-band rate (the quarter-log2 path MIN, Section 2) but uses it only to scale the controller's gains; the congestion signal remains queue-only, which is still the main functional difference (Section 8).
- L4S / TCP Prague (RFC 9330–9332, 2023) make
RTT-independence a requirement and use a high-frequency single-bit signal. mb-ecn shares the RTT-independent allocation goal but reaches it by wall-clock-scaled rate control, with a low-frequency multi-bit signal.
The novel combination is the multi-bit magnitude on the wire fused with a wall-clock-scaled rate pacer in a setting with no per-flow ack clock. The trade-off, made explicit above, is a queue-only signal and a reactivity capped by the averaging window.
11. Parameters
These are the values every figure in this document instantiates. They are coupled: the mechanisms compose only while certain relations hold — the gap-restart horizon at the rate floor must exceed a packet's service time (or a floor-rate flow restart-loops at onset), a floor-rate flow's target window must fit under the window ceiling, the control interval must sit under the damping bucket under the idle credit cap, the rate floor must not exceed the slow-start seed, and the derived-floor ceiling must stay under the rate ceiling. Retune them as a set, against those invariants.
| Parameter | Value | Meaning |
|---|---|---|
Marking quantum Q |
4 packets | Mark onset and step; layer-wide |
| Feedback resolution | 1/32 ecn unit |
Fixed-point LSB of ece
|
Full-congestion reference M |
512 | Mean queue of 64 packets |
| Receiver window target | 16 packets (16,000 bytes) | Samples per window |
| Initial receiver window | ~67 ms | Before rate is known |
| Receiver window bounds | ~1.05 ms – ~4.3 s | Clamp on W
|
| Window resize weight | 1/4 | Moving average toward target |
| Early window close | 2 × target bytes | Speed-up escape |
| Gap restart | 4 windows | Idle horizon (rate-relative) |
Additive slope a (default) |
2^16 B/s^2 | Lower clamp of derived slope |
Probe time constant T_probe |
8 s | Rate-proportional recovery |
| Derivative gain | 1/4 | Onset sharpening |
Rate floor r_min (default) |
2^13 B/s | Lower clamp of derived floor |
| Slow-start seed rate | 2^16 B/s | Initial rate |
| Slow-start time constant | 20 ms | Exponential ramp |
Rate ceiling r_max |
2^37 B/s | Upper clamp |
| Capacity scale | C / 32 |
Floor and slope from bottleneck |
| Capacity smoothing | 1/2 | Moving average per feedback |
| Capacity staleness | 16 × feedback horizon | Revert to defaults |
| Derived floor ceiling | 2^32 B/s | Upper clamp on derived floor |
| Control interval | 1 ms | Minimum spacing of rate updates |
| Idle credit cap | 50 ms | Burst allowance / increase banking |
| Feedback staleness floor | ~268 ms | Floor of rate-relative horizon |
| Damping bucket | ~31 ms | Rate-velocity washout cadence |
| Damping fraction | 1/4 | Pullback per bucket |
| Estimator fold spacing | 1 ms | Minimum measurement cadence |
| Estimator window closure | 16 packets drained | Self-scaling window |
| Estimator window staleness | ~134 ms | Discard across traffic gaps |
| Estimator idle tolerance | 1/8 of arrivals | Unsaturated-window rejection |
| Estimator decay | 1/16 per window | Max-filter convergence from below |
12. References
12.1. Source
The implementation follows the algorithm above:
src/ipcpd/unicast/ca/mb-ecn.c,mb-ecn.h—
receiver estimator, pacer and rate control.
src/ipcpd/unicast/ca/ops.h— CA policy interface.src/ipcpd/unicast/cap.c,cap.h— link-capacity
estimator and quarter-log2 codec.
src/ipcpd/unicast/dt.c,fa.c— per-hop marking
and stamping, and the feedback carryingece+cap8.
src/ipcpd/unicast/psched.c— consumes the pacing deadline.
12.2. Literature
- K. K. Ramakrishnan, R. Jain, "A Binary Feedback Scheme for Congestion
Avoidance in Computer Networks" (DECbit), ACM ToCS, 1990.
- H. Balakrishnan, H. S. Rahul, S. Seshan, "An Integrated Congestion
Management Architecture for Internet Hosts", SIGCOMM 1999; H. Balakrishnan, S. Seshan, "The Congestion Manager", RFC 3124, 2001.
- M. Alizadeh et al., "Data Center TCP (DCTCP)", SIGCOMM 2010.
- Y. Zhu et al., "Congestion Control for Large-Scale RDMA Deployments"
(DCQCN), SIGCOMM 2015.
- R. Mittal et al., "TIMELY: RTT-based Congestion Control", SIGCOMM 2015.
- Y. Li et al., "HPCC: High Precision Congestion Control", SIGCOMM 2019.
- V. Addanki et al., "PowerTCP", NSDI 2022.
- K. De Schepper, B. Briscoe (eds.), "Low Latency, Low Loss, and
Scalable Throughput (L4S)", RFC 9330/9331/9332.
- F. Kelly, "Charging and Rate Control for Elastic Traffic", 1997;
R. Srikant, "The Mathematics of Internet Congestion Control", 2004.