# Linux preemption models

Preemption is the scheduler's decision about *when* it's allowed to interrupt a running thread and hand the CPU to another. Different workloads want different answers, so the Linux kernel exposes the choice as a build-time configuration.

## The three models

`PREEMPT_NONE` — the kernel almost never interrupts a running thread. It runs until it gives up the CPU itself: a syscall, blocking I/O, an explicit `sleep`. Few context switches, high throughput, predictable behavior under load. Traditional server default.

`PREEMPT_FULL` — the kernel can interrupt a running thread at almost any safe point, including inside the kernel itself. Lower latency, more context-switch overhead. Traditional desktop default, where responsiveness wins over throughput.

`PREEMPT_LAZY` — introduced in Linux 6.12 as a compromise. Preemption is allowed but the scheduler tries to wait for natural boundaries before cutting in. Designed as a drop-in replacement for `PREEMPT_NONE` on throughput workloads.

## What changed in Linux 7.0

Linux 7.0 removed `PREEMPT_NONE` on modern CPU architectures, leaving `PREEMPT_FULL` and `PREEMPT_LAZY`. Most server distros shifted their default to `PREEMPT_LAZY`. The transition is invisible for most software. But where it isn't, it really isn't — see [[linux-7-postgres-regression]] for a workload (PostgreSQL on a 96-vCPU box) where the change cut throughput in half because the scheduler can now preempt a thread that's holding a [[spinlock]] and is mid–page fault.

## Why "drop-in" isn't drop-in

`PREEMPT_NONE` was a free implicit guarantee for any code holding a spinlock: even if the holder did something the kernel had to handle (a fault, a slow path), it wouldn't get descheduled until it was done. Code written before `PREEMPT_LAZY` could quietly rely on that. Once removed, every "the holder will be gone in nanoseconds" assumption needs to actually be true — or be backed by something stronger like restartable sequences.

## What happens before any of this matters

The preemption model only takes effect after boot. During Phase 4 of [[linux-kernel-startup]] (`start_kernel`), interrupts are disabled for almost the entire run and the scheduler exists but isn't load-balancing yet. Real multitasking — the world where preemption decisions actually matter — starts in Phase 6 with `sched_init_smp()`. See [[linux-boot-phases]] for the broader ordering.
