On this page
Block Device Layer
Coverage: bio → request → plug/unplug → blk-mq (multi-queue) → IO scheduler (mq-deadline/kyber/bfq) → blktrace/blkparse debugging Kernel versions: 2.6 ~ 6.x, with a focus on the blk-mq refactoring (3.13~5.x)
Overview
The block layer is a translation layer between the file system and device drivers. Its input is the bio submitted by the file system ("read sector N, place it in these pages, notify me when done"), and its output is the request sent to the device driver. In between, the block layer performs merging, sorting, and scheduling, aiming to minimize IO latency while maximizing throughput.
From the single-request queue in 2.6 to the multi-queue architecture (blk-mq) in 5.x, the Linux block layer underwent a complete rewrite—the driver model shifted from "one queue and one lock per device" to "per-CPU submit queues + multiple hardware queues."
bio: The Atomic Unit of Block IO
// include/linux/blk_types.h
;
bio_vec: Describing IO on a Memory Page
// include/linux/bvec.h
;
A bio can contain multiple bio_vecs—this is scatter-gather IO. The file system can pack non-contiguous memory pages into a single IO, and the block layer and driver will then split them into sizes acceptable to the device.
Bio Submission Path
// block/blk-core.c
void
blk-mq: Multi-Queue Architecture
Why Migrate from blk-sq to blk-mq
blk-sq (Single Queue, 2.6 ~ 3.x):
struct request_queue *q; ← One large lock protects all submissions
All CPUs compete for q->queue_lock → Multi-core scalability bottleneck
Single-threaded model of IO scheduler → Peaks for high IOPS devices (NVMe)
blk-mq (3.13+, fully replaces blk-sq in 5.x):
Two-layer queue structure:
Software Queues (ctx): per-CPU or per-cgroup → Zero-lock contention
Hardware Queues (hctx): Mapped to device hardware queues (NVMe SQ, SCSI tag)
Tags: Used to manage the number of concurrent requests in hardware queues
→ Each hardware queue has a fixed tag pool → Natural throttling
Data Structures
// include/linux/blk-mq.h
;
;
Submission Path: Plug → Scheduler → Dispatch → Driver
Plug: Batch Submission
// block/blk-mq.c + block/blk-plug.c
// Plug is a per-task bio accumulation mechanism:
// blk_start_plug(current) // Start plug
// submit_bio() × N // Accumulate N bios, do not dispatch immediately
// blk_finish_plug(current) // Flush all bios at once
// Design rationale:
// 1. Reduce lock contention (acquire lock once, insert N requests)
// 2. Increase merge opportunities (front/back merge possible during plug)
// 3. File systems typically utilize plug in the write path:
// ext4_writepages() → blk_start_plug() → multiple submit_bio → blk_finish_plug()
IO Scheduler
The fundamental difference between multi-queue IO schedulers and the old single-queue schedulers is: the scheduler is not global, but per-hctx. Each hardware queue is scheduled independently because different queues correspond to submissions from different CPUs.
mq-deadline
// block/mq-deadline.c
// Maintains separate Red-Black trees (sorted by sector) + FIFO (sorted by clock time) for reads and writes
//
// Core parameters:
// read_expire: Max wait time for read requests (default 500ms)
// write_expire: Max wait time for write requests (default 5000ms)
// writes_starved: Write request quota (how many batches of reads to process before handling a batch of writes)
//
// Scheduling logic:
// 1. Check FIFO first: if there are timed-out requests → fetch requests near that sector from the RB tree (batch dispatch)
// 2. Otherwise: dispatch by direction batch (prefer reads, but writes_starved ensures writes are not starved)
// 3. Each dispatch sends no more than 16 requests (to avoid starving other queues)
//
// Suitable for: NVMe, general-purpose SSDs
kyber
// block/kyber-iosched.c
// Adaptive scheduling based on token bucket
// Goal: Control read/write queue depth to keep latency within target bounds
//
// Working principle:
// Maintains latency histograms for reads and writes
// Dynamically adjusts the token bucket rate for each direction based on latency
// Low latency → increase bucket → more IO concurrency
// High latency → decrease bucket → reduce IO concurrency (lower device queue depth)
//
// Suitable for: Latency-sensitive SSD workloads (e.g., databases)
bfq (Budget Fair Queuing)
// block/bfq-iosched.c
// Allocates IO bandwidth per cgroup/process
// Each process has a "budget" (number of sectors it can issue)
// Based on B-WF2Q+ algorithm (similar to CFS fair scheduling)
//
// Features:
// - Interactive processes automatically get higher weight (detects think time)
// - Supports cgroup blkio controller
// - Sequential reads detected → given more budget → high throughput
//
// Suitable for: Desktop systems (ensures background backups do not affect foreground application responsiveness)
Scheduler Switching
# [mq-deadline] kyber bfq none
Queue Parameter Tuning
# Number of hardware queues (usually equal to CPU count)
# max_sectors_kb: Maximum size of a single IO (default 512KB = 1024 sectors)
# nr_requests: Maximum number of requests per hardware queue
# Scheduler parameters
Debugging and Observation
# blktrace: Trace the full lifecycle of each IO
# Output: time, CPU, action (Q=queued, G=get, I=inserted, D=dispatch, C=complete)
# iostat: Device-level statistics
# r/s, w/s, r_await, w_await, aqu-sz (average queue depth), %util
# Raw counters for a single device
# Fields: read_ios, read_merges, read_sectors, read_ticks, write_ios, ...
# blk-mq debugging
# Tag usage for each hardware queue
# ftrace to trace block layer events
Key Configurations and Compilation
# Kernel boot parameters
scsi_mod.use_blk_mq=1 # Force SCSI to use blk-mq (default in 5.0+)
elevator=mq-deadline # Default IO scheduler
elevator=bfq # Recommended for desktops
# Kernel compilation options
CONFIG_BLK_MQ=y # Enable blk-mq
CONFIG_IOSCHED_BFQ=y # BFQ support
CONFIG_BLK_DEV_THROTTLING=y # cgroup blkio
References and Further Reading
- Source Code:
block/blk-mq.c(~4000 lines, blk-mq core),block/blk-merge.c,block/mq-deadline.c,block/bfq-iosched.c,block/kyber-iosched.c - Kernel Documentation:
Documentation/block/,Documentation/block/bfq-iosched.rst - LWN:
- "The multi-queue block layer" (lwn.net/Articles/552904/)
- "blk-mq and the I/O schedulers" (lwn.net/Articles/738449/)
Keywords: bio, bio_vec, blk-mq, software queue, hardware queue, plug, mq-deadline, kyber, bfq, blktrace, iostat, IO scheduler