The Qspinlock: How Linux Replaced Ticket Spinlocks to Scale Past 8 Cores

2026-08-18

Linux used ticket spinlocks from 2008 to 2014: each waiter atomically increments a "next ticket" counter, then spins reading a shared "now serving" counter until it matches. Fair, simple, and catastrophic at scale.

The problem is all waiters spin on the same cache line. When the lock holder releases (writes now_serving+1), that store invalidates the line in every waiter's L1. All N cores then re-read it, and the coherence traffic scales as O(N²). On a 4-core system this is invisible. On a 64-core box, releasing a hot lock triggers a storm of snoop invalidations across the ring bus, and lock throughput drops as you add cores.

Qspinlock (merged 2014, x86 default 2015) fixes this by making each waiter spin on its own cache line — a per-CPU node in an implicit MCS queue. The lock word itself is 32 bits, encoding three states:

Concrete example: the kernel's tasklist_lock (a rwlock, but same principle) was measured by Red Hat on a 4-socket Haswell (72 cores). Under a fork()-heavy workload, ticket spinlock throughput peaked at 16 cores then declined to 40% of peak at 72 cores. Qspinlock scaled monotonically, hitting 3.2× the peak ticket throughput.

Rule of thumb: if a lock is uncontended, qspinlock costs one cmpxchg (~20 cycles). If it's contended by N cores, release costs one cache-line transfer to the next waiter — independent of N. Compare to ticket locks: release costs N cache-line invalidations, so contention scales as N × latency-to-farthest-core (~150ns × 64 = ~10μs of coherence traffic per release on a big box).

The tail encoding is the clever bit: 14 bits for CPU index, 2 bits for context (task/softirq/hardirq/NMI), lets any code path acquire without dynamic allocation. When you nest an IRQ handler inside a task holding a qspinlock, the IRQ uses a different per-CPU node — no collision.

Key Takeaway: Qspinlock replaced ticket spinlocks because fairness that forces every waiter to spin on the same cache line stops scaling once cross-socket coherence traffic dominates lock latency.

All newsletters