Congestion avoidance: Difference between revisions

From Ouroboros
Jump to navigation Jump to search
No edit summary
Line 22: Line 22:
== Notation ==
== Notation ==


;<code>r</code>
{| class="wikitable"
:Paced send rate, bytes per second.
! Symbol !! Units !! Meaning
;<code>C</code>
|-
:Bottleneck capacity, bytes per second.
| <code>r</code> || B/s || Paced send rate
;<code>S</code>
|-
:Packet size in bytes.
| <code>C</code> || B/s || Bottleneck capacity
;<code>q</code>
|-
:Standing queue occupancy at the bottleneck, in packets.
| <code>S</code> || bytes || Packet size
;<code>Q</code>
|-
:Marking quantum, packets per mark unit (4).
| <code>q</code> || packets || Standing queue occupancy at the bottleneck
;<code>ecn</code>
|-
:Per-packet congestion mark set by a forwarder, an unsigned integer
| <code>Q</code> || packets || Marking quantum: packets per mark unit (4)
(on-wire field is 8-bit).
|-
;<code>ece</code>
| <code>ecn</code> || 8-bit code || Per-packet congestion mark set by a forwarder
:Receiver-side congestion estimate, fixed point with LSB = 1/32 of an
|-
<code>ecn</code> unit, i.e. <code>ece = 32 &times; (time-mean ecn)</code>.
| <code>ece</code> || 32 &times; ecn || Receiver-side congestion estimate (time-mean of <code>ecn</code>)
;<code>cap8</code>
|-
:Path-capacity code carried per packet, quarter-log2:
| <code>cap8</code> || 8-bit code || Path capacity, quarter-log2: <code>C &asymp; 2^(cap8 / 4)</code> B/s, 0 = unknown
<code>C &asymp; 2^(cap8 / 4)</code> bytes/s, 0 = unknown.
|-
;<code>W</code>
| <code>W</code> || ns || Receiver averaging window
:Receiver averaging window, nanoseconds.
|-
;<code>a</code>
| <code>a</code> || B/s^2 || Additive-increase slope
:Additive-increase slope, bytes/s per second.
|-
;<code>M</code>
| <code>M</code> || <code>ece</code> || Full-congestion reference level (512)
:Full-congestion reference level of <code>ece</code> (512).
|-
;<code>&Delta;t</code>
| <code>&Delta;t</code> || ns || Elapsed wall-clock time between two events
:Elapsed wall-clock time between two events, nanoseconds.
|-
;<code>&tau;</code>
| <code>&tau;</code> || &mdash; || Path round-trip time (feedback delay); the controller does not measure it
:Path round-trip time (feedback delay). The controller does not
|}
measure it.


Rate is the control variable throughout.
Rate is the control variable throughout.

Revision as of 21:25, 11 July 2026


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

Symbol Units Meaning
r B/s Paced send rate
C B/s Bottleneck capacity
S bytes Packet size
q packets Standing queue occupancy at the bottleneck
Q packets Marking quantum: packets per mark unit (4)
ecn 8-bit code Per-packet congestion mark set by a forwarder
ece 32 × ecn Receiver-side congestion estimate (time-mean of ecn)
cap8 8-bit code Path capacity, quarter-log2: C ≈ 2^(cap8 / 4) B/s, 0 = unknown
W ns Receiver averaging window
a B/s^2 Additive-increase slope
M ece Full-congestion reference level (512)
Δt ns Elapsed wall-clock time between two events
τ 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      |
      +--------------------------------+       +--------------------------+
  1. 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.
  1. 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.
  1. 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.

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/2 per 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 carrying ece + 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.