I/O Subsystem
The Linux I/O subsystem handles all storage operations. Understanding the block layer, I/O schedulers, and modern async I/O with io_uring is essential for building and debugging high-performance systems.Interview Focus: I/O schedulers, async I/O, io_uring
Time to Master: 4-5 hours
Block Layer Architecture
bio and request Structures
The bio Structure
bio Lifecycle
I/O Schedulers
Multi-Queue Architecture (blk-mq)
Scheduler Comparison
- mq-deadline
- bfq
- kyber
- none
- Read and write request deadlines
- Read priority over writes (reads typically blocking)
- Batch dispatch for efficiency
Asynchronous I/O
Traditional AIO (libaio)
- Only supports O_DIRECT
- Limited to block I/O
- System call per submit/complete
io_uring: Modern Async I/O
io_uring Architecture
io_uring Example
io_uring Advanced Features
io_uring Supported Operations
Direct I/O and O_DIRECT
When to Use O_DIRECT
I/O Profiling and Debugging
blktrace and blkparse
BPF-based Tools
iostat Analysis
Interview Deep Dives
Q: Explain the journey of a write() call from application to disk
Q: Explain the journey of a write() call from application to disk
-
Application:
write(fd, buf, len) -
VFS Layer:
- Find inode from fd
- Call filesystem’s write_iter
-
Page Cache (buffered write):
- Find/create page in cache
- Copy data from user space
- Mark page dirty
- Return to application (write “complete”)
-
Writeback (background or sync):
- pdflush/writeback worker wakes
- Allocates bio for dirty pages
- Submits bio to block layer
-
Block Layer:
- bio enters request queue
- Scheduler may merge/reorder
- Dispatch to driver
-
Device Driver:
- Translate to device commands
- DMA data to device
-
Hardware:
- Device writes to persistent storage
- Interrupt on completion
-
Completion:
- Driver handles interrupt
- bio completion callback
- Page marked clean
Q: What's the difference between sync, fsync, and fdatasync?
Q: What's the difference between sync, fsync, and fdatasync?
- Triggers writeback for ALL dirty data
- Doesn’t wait for completion
- System-wide operation
- Flushes data AND metadata for specific file
- Waits for completion
- Includes directory entry if new file
- Flushes data for specific file
- Only flushes metadata if required for data retrieval
- Skips non-essential metadata (atime, mtime)
Q: When would you use io_uring over regular syscalls?
Q: When would you use io_uring over regular syscalls?
-
High I/O rate: Thousands of IOPS
- Syscall overhead becomes significant
- Batching amortizes overhead
-
Network servers: Accept/read/write patterns
- Single interface for all I/O
- Async accept with multishot
-
Low latency requirements:
- SQPOLL avoids syscall entirely
- Registered files/buffers reduce overhead
-
Mixed I/O workloads:
- File + network in same ring
- Unified completion handling
- Simple applications (complexity not worth it)
- Few I/O operations (no benefit)
- Portability required (Linux-specific)
Q: How would you debug slow disk I/O?
Q: How would you debug slow disk I/O?
-
Identify the bottleneck:
-
Check for throttling:
-
Profile I/O patterns:
-
Check scheduler:
-
Application level:
NVMe Specifics
NVMe vs SATA SSD
Interview Deep-Dive
You are choosing an I/O scheduler for a fleet of NVMe-backed database servers. Compare mq-deadline, kyber, and 'none', and explain your recommendation.
You are choosing an I/O scheduler for a fleet of NVMe-backed database servers. Compare mq-deadline, kyber, and 'none', and explain your recommendation.
- For NVMe-backed database servers, my recommendation is
none(no scheduler), with the caveat that workload testing should validate this. mq-deadlinemaintains separate read and write queues with deadline guarantees: reads default to 500ms deadline, writes to 5000ms. It prioritizes reads over writes because reads are typically in the synchronous path. This is valuable for HDD where seeking is expensive and reordering requests by sector can save milliseconds. But NVMe devices have no seek time, so reordering adds latency without reducing device-side cost.kyberuses a token-based system with target latencies for reads and writes. When I/O latency exceeds the target, kyber reduces the number of in-flight requests (throttles) to reduce queueing. This is useful for shared environments where multiple workloads compete for NVMe bandwidth. However, for a dedicated database server, the database’s own I/O scheduler (InnoDB’s adaptive flushing, PostgreSQL’s bgwriter) already manages I/O prioritization.nonepasses I/O requests directly to the NVMe device with no kernel-side reordering or scheduling. NVMe devices have internal schedulers optimized for their flash topology (channel interleaving, die-level parallelism), and the device’s 64K queue depth per submission queue means it can handle massive parallelism. Adding a kernel scheduler on top adds latency (microseconds per request for lock acquisition and queue insertion) without improving throughput.- The exception: if multiple containers share the NVMe with different priority classes, I would use
kyberormq-deadlinewith I/O cgroup limits to prevent noisy neighbors.
- NVMe devices expose multiple hardware submission/completion queue pairs (typically one per CPU core). The blk-mq layer creates per-CPU software staging queues that map to these hardware queues. When a thread submits I/O, the request enters the software queue for the thread’s current CPU, is optionally processed by the I/O scheduler, and then dispatched to the corresponding hardware submission queue. This per-CPU design eliminates cross-CPU lock contention: each CPU has its own software queue feeding its own hardware queue. The mapping is configurable via
/sys/block/nvme0n1/queue/nr_requests(per-queue depth) andirq_affinity(which CPUs handle completion interrupts). For optimal performance, the completion interrupt for a hardware queue should be handled by the same CPU that submitted the I/O, keeping the data cache warm.
A production service is experiencing periodic I/O latency spikes of 50-100ms on an SSD that normally serves requests in under 1ms. How would you investigate this at the block layer level?
A production service is experiencing periodic I/O latency spikes of 50-100ms on an SSD that normally serves requests in under 1ms. How would you investigate this at the block layer level?
- First, I would capture the I/O latency distribution with
sudo biolatency-bpfcc -D 10(grouped by device) to confirm the bimodal pattern: most I/Os under 1ms with a tail at 50-100ms. Thensudo biosnoop-bpfcc -d nvme0n1to see individual slow I/Os with their PID, operation type, sector, and size. - Common causes of periodic I/O spikes on SSDs: First, garbage collection — SSD firmware periodically reclaims erased blocks, which can stall writes for 10-100ms. This manifests as periodic write latency spikes regardless of host activity. Check with
nvme smart-log /dev/nvme0for wear leveling counts. Second, journal commits — ext4/XFS periodically commit journal transactions (default every 5 seconds), which issues synchronous writes that can queue behind other I/O. Check withbpftrace -e 'kprobe:jbd2_journal_commit_transaction { printf("%s\n", comm); }'. Third, filesystem metadata operations —sync,fsync, or flusher threads writing dirty pages can cause queue depth spikes. - At the block layer, I would check queue depth using
bpftraceto traceblock_rq_issueandblock_rq_completeevents, computing the instantaneous queue depth. If the spike correlates with high queue depth, the device is saturated. If the spike happens at low queue depth, the device itself is stalling (firmware GC, thermal throttling, or defective NAND). - I would also check the I/O scheduler:
cat /sys/block/nvme0n1/queue/scheduler— if it is notnone, try switching to rule out scheduler-induced delays. And check I/O cgroup throttling:cat /sys/fs/cgroup/<path>/io.statfor the relevant device.
- I would trace both
block_rq_issue(when the kernel dispatches the request to the driver) andblock_rq_complete(when the device signals completion). The delta between issue and complete is purely device-side latency. Separately, I would traceblock_rq_insert(when the request enters the scheduler queue) andblock_rq_issue— this delta is kernel scheduler queueing time. If the device-side delta shows spikes, the SSD is stalling. If the scheduler delta shows spikes, the kernel is holding requests in the queue (possibly throttled by cgroup I/O limits or the scheduler’s admission control).
Next: Networking Stack →