Skip to content

CONCEPT Cited by 1 source

CUBIC epoch

Definition

The epoch (epoch_start in the Linux-kernel TCP CUBIC implementation; congestion_recovery_start_time in quiche) is CUBIC's reference timestamp for its growth curve. The CUBIC window function W_cubic(delta_t) = C·(delta_t − K)^3 + W_max is parameterised on delta_t = now − epoch_start; the epoch is reset whenever CUBIC restarts its growth function — most notably after a loss event reduces cwnd. Between resets, delta_t grows monotonically with wall-clock time, and cwnd traces the cubic curve anchored at the epoch (Source: sources/2026-05-12-cloudflare-when-idle-isnt-idle-how-a-linux-kernel-optimization-became-a-quic-bug).

Why the epoch is load-bearing

The epoch is not just a timestamp — it is the load-bearing state variable that decides how aggressively CUBIC grows cwnd at any moment. Two structural failure modes hinge on it:

  1. Epoch stale during application idleness. If the application stops sending for a while, delta_t = now − epoch_start grows while no data moves. The next packet sent after idleness reads delta_t as huge — and the cubic function computes an enormous bic_target, which CUBIC would try to satisfy by inflating cwnd to an unreasonable value. This is the Jana Iyengar 2017 problem that Linux kernel commit 30927520dbae addressed with "CUBIC after idle: shift epoch_start forward by the idle duration, preserving the growth-curve shape." This was the correct fix — resetting would instead collapse the growth curve and trigger aggressive cwnd growth, as Neal Cardwell's review caught.
  2. Epoch set into the future. If the idle-shift adjustment sets epoch_start > now, then in_congestion_recovery() returns true on every incoming ACK (because recovery is nominally still ahead of us in time), cwnd growth is skipped, and CUBIC locks the connection into congestion- recovery state. This is the 1-week-later Linux-kernel follow-up fix (commit c2e7204d180f) and the six-year-later quiche fix (Cloudflare 2026-05-12) — both addressing the same invariant violation.

The invariant

epoch_start ≤ now must hold at all times. Any adjustment to the epoch that can push it forward past wall-clock time violates the CUBIC state machine and causes the growth function to behave pathologically. The 2017 Linux follow-up commit named this explicitly in its message: "Let's simply not set epoch_start in the future, otherwise bictcp_update() could overflow and CUBIC would again grow cwnd too fast." The 2026-05-12 quiche fix enforces the same invariant via if idle_start < now guard before advancing congestion_recovery_start_time.

Naming difference: Linux TCP vs quiche

  • Linux TCP CUBIC calls this state variable epoch_start and updates it in both the ACK-processing path (bictcp_acked) and — via the 2017 fix — a dedicated CA_EVENT_TX_START callback for the idle-period shift.
  • quiche (Rust) names the corresponding state variable congestion_recovery_start_time. Because QUIC's CCA runs in user space inside on_packet_sent(), there is no kernel CA_EVENT_TX_START callback; the idle detection and the epoch shift are fused into the send path. That structural difference is the fault-line along which the 2026-05-12 bug hid for five years — see patterns/userspace-port-of-kernel-primitive-risk.

Seen in

Last updated · 542 distilled / 1,571 read