← Start of tutorial

WIUNIX ACADEMY

Linux I/O latency: connect iostat, PSI and service pressure

Page 2 of 3. Review the earlier steps and prerequisites before continuing.

YOUR SYSTEM. YOUR STEPS.

Where are you working?

Choose your OS and version. The prerequisites, commands and troubleshooting follow your environment.

Ubuntu 24.04 LTSReviewed against official documentation ·

USE YOUR OWN VALUES

Configure this tutorial

Set the example values for your environment; related explanations and copy panels update together. Never enter passwords, private keys or tokens. Values stay in this tab and are not sent to the server or share links. Check actual DNS, certificates and network policy before running commands.

Different IP addresses, domains or ports? Open the settings below, change the values and select “Apply to tutorial”, then copy the updated commands.

Set values for my environment4 editable values
06

LET’S BUILD

Separate paging and blocked work from CPU usage

Use vmstat for the same incident period. Rate and CPU columns in its first data row are averages since boot; read the following rows for interval comparisons. Process and memory fields are instantaneous in every row. Look at blocked tasks, swap-in/swap-out and CPU fields together. The wa column is CPU idle time associated with outstanding I/O, not the percentage of application time lost to I/O.

A workload may stall on I/O while other tasks keep CPUs busy, leaving wa small. Memory reclaim and swapping can create storage traffic without a new database read surge. These signals support a hypothesis; they do not identify an individual disk or authorize changing swap, cache or kernel writeback settings.

bash

Authorized terminal on the affected Linux host

date -u +%FT%TZ
LC_ALL=C vmstat 1 6
How do I know it worked?

The five interval rows after the initial summary are interpreted alongside pressure and device reports; paging is kept distinct from application I/O.

07

LET’S BUILD

Find candidate processes without exposing their arguments

pidstat samples task-accounted reads and writes. This panel uses process names rather than full command-line arguments, which may contain private values. A process with high write accounting is a candidate owner, not proof that every device request completed synchronously for that PID: buffered writeback and shared filesystems complicate attribution.

Zero or absent iodelay is inconclusive when task delay accounting is unavailable or disabled. The sysstat FAQ documents the kernel accounting requirements. Do not enable delay accounting or broaden procfs access as an incident shortcut. Resolve visibility with the host owner and keep the documented blind spot in the incident record.

bash

Authorized terminal on the affected Linux host

date -u +%FT%TZ
LC_ALL=C pidstat -d -p ALL 1 5
How do I know it worked?

A bounded list of candidate PIDs is available within current permissions. Missing accounting and buffered-write attribution limits remain explicit.

08

LET’S BUILD

Optional: locate the existing service cgroup

Set example to the existing systemd service basename, without the .service suffix. Inspect its identity and ControlGroup before using a cgroup path. This optional branch requires cgroup v2 and host-level access; it does not create or move cgroups. A container’s private cgroup namespace may not expose the host path.

Combine the actual cgroup2 mount point with the returned ControlGroup and put that existing absolute directory into /sys/fs/cgroup/system.slice/example.service. The initial example is intentionally not a discovered service. A missing unit or empty ControlGroup means stop this branch, not copy a guessed directory.

bash

Authorized terminal on the affected Linux host

systemctl show 'example.service' -p Id -p LoadState -p ActiveState -p MainPID -p ControlGroup
findmnt -t cgroup2 -o TARGET,FSTYPE,OPTIONS
How do I know it worked?

An existing unit and its actual cgroup v2 directory are identified before cgroup counters are read; unavailable scopes are skipped.

09

LET’S BUILD

Compare service pressure, bytes and configured limits

Read the discovered service directory twice. io.stat counts bytes and operations by major:minor; compare each device’s delta over the same interval and map it back to lsblk. io.max can reveal a configured ceiling. A max entry does not prove that the service reached that ceiling, and absence of a local cap does not exclude a limit at a parent cgroup or storage provider.

Service-level pressure can be high while the host looks comfortable. Inspect the relevant ancestor policy with the service owner if needed; do not change io.max, io.weight or controller delegation to make numbers improve. Some kernels or cgroup arrangements do not expose every file; missing interfaces are not zeros.

bash

Authorized terminal on the affected Linux host

(
set -eu
cg='/sys/fs/cgroup/system.slice/example.service'
test -d "$cg"
test "$(findmnt -n -o FSTYPE --target "$cg")" = cgroup2
for sample in 1 2; do
  date -u +%FT%TZ
  for name in io.pressure io.stat io.max memory.pressure; do
    printf '\n%s\n' "$name"
    if test -r "$cg/$name"; then cat "$cg/$name"; else printf 'unavailable or unreadable\n' >&2; fi
  done
  if test "$sample" = 1; then sleep 5; fi
done
)
How do I know it worked?

Two samples preserve device counters, service stalls and visible policy. Parent/provider limits and missing interfaces remain open questions.

Choose a next action from a consistent explanation

If application latency, I/O pressure and device queue/await rise together under comparable demand, investigate that storage path and backend limits or errors. If pressure is concentrated in one service with comfortable host devices, investigate its cgroup hierarchy and workload pattern. If memory pressure and swap rise first, investigate working-set growth and memory limits before buying faster disks.

If request latency worsens but these measurements stay normal, preserve the negative finding and investigate application locks, remote dependencies or CPU scheduling instead. Short samples can miss bursts; repeat a bounded window at the actual failure time rather than declaring an unrelated idle period a pass.

Choose one owner-approved mitigation at a time: for example reduce a restartable batch job’s concurrency through its own scheduler, or reschedule it outside the foreground peak. Preserve the previous application setting and backlog/replay plan. Throttling, pausing or retrying work can change completion time and data freshness; it is a workload decision, not a generic shell repair.

When the measurements do not line up

Use the matching diagnostic below and retain errors. Do not install a load generator, clear caches, change the I/O scheduler, disable MAC protection or reboot a production host to make an observation available.

PSI is missing or unreadable

Check the running kernel and visible interface. A container mount, kernel configuration or boot policy can hide/disable PSI. Record unavailable coverage and ask the host owner; do not treat this as zero pressure.

bash
uname -r
ls -ld /proc/pressure
ls -l /proc/pressure/io /proc/pressure/memory
iostat does not show the expected device

Recheck the actual filesystem and block stack. Remote filesystems, overlays or a different namespace do not necessarily map to the device you guessed; kernel statistics can also be unavailable. Do not infer a healthy device from an absent row.

bash
findmnt --target '/var/lib' -o TARGET,SOURCE,FSTYPE
lsblk -o NAME,KNAME,MAJ:MIN,TYPE,MOUNTPOINTS
head -n 12 /proc/diskstats
pidstat reports little or no I/O

Processes may have exited, visibility may be restricted, accounting may be unavailable, or buffered/remote I/O may not match the device interval. Verify a candidate PID immediately; PIDs are reused. comm avoids dumping private arguments.

bash
ps -p '1234' -o pid=,ppid=,user=,comm=,lstart=
cat '/proc/1234/cgroup'
The service cgroup vanished or changed

Units can stop or move as processes restart. Rediscover ControlGroup and the cgroup2 mount; update the public example only after confirming scope. Never create a directory under cgroupfs to make the read succeed.

bash
systemctl show 'example.service' -p LoadState -p ActiveState -p MainPID -p ControlGroup
findmnt -t cgroup2 -o TARGET,FSTYPE
Explore all tutorials ↗