Pressure Stall Information (PSI): How the Kernel Measures Whether Your System Is Actually Struggling

2026-08-18

Traditional metrics lie about resource pressure. A 100%-utilized CPU might be perfectly healthy (batch job doing useful work) or catastrophically overloaded (100 threads fighting for a runqueue slot). Load average conflates runnable and uninterruptible-sleep tasks and averages over minutes. Memory "free" tells you nothing about whether allocations are stalling on reclaim. PSI (Pressure Stall Information), added in kernel 4.20 by Johannes Weiner at Facebook, measures the thing you actually care about: time your tasks lost waiting for a resource that was contended.

PSI exposes three files: /proc/pressure/{cpu,memory,io}. Each has two lines:

Each line reports averages over 10s / 60s / 300s windows plus a monotonic total in microseconds. The kernel tracks stall time by hooking scheduler events: when a task goes to sleep waiting on a page fault, direct reclaim, a swap-in, or a block-io completion, that time gets attributed to memory or io pressure. When a task is runnable but not running, that's CPU pressure.

Concrete example. Facebook's oomd and systemd's systemd-oomd use PSI to make OOM decisions before the kernel's OOM killer fires. A rule like "if memory.full > 60% over 10s, kill the biggest cgroup in this slice" catches thrashing far earlier than the kernel does — the kernel OOM killer only triggers when allocation literally fails, which on a swap-enabled system can mean minutes of unusable thrashing first. PSI catches that state directly: 60% "full" means the cgroup spent 6 seconds of every 10 doing nothing but waiting on memory.

Cgroups v2 exposes per-cgroup PSI at /sys/fs/cgroup/<path>/{cpu,memory,io}.pressure, so you can attribute pressure to a specific container. You can also poll(2) on these files with a threshold — the kernel wakes you only when pressure crosses your line, no polling loop needed.

Rule of thumb. For a latency-sensitive service, treat some > 10% on any resource as a yellow flag and full > 10% on memory or io as a red flag worth paging on. CPU has no "full" — use some > 20% as the CPU equivalent (a fifth of your task-seconds spent waiting for a core).

Gotcha. PSI's numerator is task-seconds, not wall-time. On a 64-core box, a single stalled thread contributes to "some" but is a rounding error in the ratio. PSI is expressed as a fraction of the system's total capacity to do work, so small stalls on big machines look tiny even when they're user-visible.

Key Takeaway: PSI measures lost productive time due to resource contention — the metric you actually wanted every time you squinted at load average or "free -m" and couldn't tell if things were fine.

All newsletters