2026-06-27
Every TCP retransmit timeout, every schedule_timeout(), every keepalive — they all land in the same data structure. A modern server can have hundreds of thousands of timers pending. Yet adding, cancelling, and firing them must all be O(1). A balanced tree (O(log n)) would crater the network stack. The kernel's answer is the cascading timer wheel.
The wheel is an array of buckets indexed by when the timer expires. Imagine the simplest version: a 256-slot circular array indexed by expiry_jiffies % 256. Each tick, the kernel advances a cursor and fires every timer in that bucket. Adding a timer = one modulo + list insertion. O(1). Cancelling = list removal from a known bucket. O(1).
The catch: that only handles 256 ticks of range. Linux fixes this with cascading wheels — eight wheels of 64 buckets each, covering exponentially larger time ranges. Wheel 0 holds timers expiring in the next 64 ticks. Wheel 1 holds timers expiring 64–4095 ticks out (granularity: 64 ticks). Wheel 2: 4096–262143 ticks (granularity: 4096 ticks). And so on. When wheel 0 fully rotates, the kernel cascades the next bucket from wheel 1 down into wheel 0, re-bucketing those timers at finer granularity.
Why this design wins: nearly all timers in practice are short-lived — TCP retransmits at 200ms, network timeouts at 30s. They sit in the lower wheels and never cascade. The expensive cascade operation happens rarely and only for long-lived timers, where the user already accepted coarse granularity.
Concrete example: Set a 5-second timer on a 1000 Hz kernel (HZ=1000). That's 5000 jiffies. With 64-bucket wheels, it lands in wheel 1 at granularity 64 jiffies = 64ms. So your "5 second" timer may fire anywhere from 5.000s to 5.064s. This is why add_timer() docs say timers are imprecise — and why hrtimer (a separate red-black-tree-based subsystem) exists for sub-millisecond accuracy.
Rule of thumb: wheel timer granularity at level N is 64^N jiffies. Level 0 = 1 jiffy, level 1 = 64, level 2 = 4096. If you need precision better than your timer's level granularity, use hrtimer instead.
The 2015 rewrite (Thomas Gleixner) eliminated cascading entirely for timers that don't need precise expiry — they just fire up to the bucket granularity late, saving the cascade work for the common case of cancelled-before-expiry timers (TCP retransmits that never fire).
