このページの目次
ブロックデバイス層
カバー範囲: bio → request → plug/unplug → blk-mq (multi-queue) → IO scheduler (mq-deadline/kyber/bfq) → blktrace/blkparse デバッグ カーネルバージョン: 2.6 ~ 6.x。blk-mq 再構築 (3.13~5.x) に重点注記
概要
ブロック層は、ファイルシステムとデバイスドライバの間のトランスレーション層です。入力としてファイルシステムから提出された bio(「N番目のセクタを読み書きし、結果をこれらのページに配置し、完了時に通知してください」)を受け取り、出力としてデバイスドライバへ request を送信します。この間、ブロック層は結合、ソート、スケジューリングを行い、IO レイテンシを最小化しつつスループットを最大化することを目指します。
2.6 の単一リクエストキューから 5.x のマルチキュー (blk-mq) へ、Linux ブロック層は完全な書き換えを経験しました。ドライバモデルは「1デバイス:1キュー:1ロック」から「per-CPU 送信キュー + 複数のハードウェアキュー」へと変化しました。
bio: ブロック IO の原子単位
// include/linux/blk_types.h
;
bio_vec: メモリページ上のIOの記述
// include/linux/bvec.h
;
1つのbioは複数の bio_vec を含むことができます。これが scatter-gather (分散-結合) IO です。ファイルシステムは連続しないメモリページを1つのIOにパックでき、ブロック層とドライバはそれをデバイスが許容するサイズに分割します。
bio 送信パス
// block/blk-core.c
void
blk-mq: マルチキューアーキテクチャ
blk-sq から blk-mq への移行理由
blk-sq (単一キュー, 2.6 ~ 3.x):
struct request_queue *q; ← 1つの大きなロックがすべての送信を保護
全CPUが q->queue_lock で競合 → マルチコアでのスケーラビリティのボトルネック
IO schedulerのシングルスレッドモデル → 高IOPSデバイス (NVMe) で頭打ち
blk-mq (3.13+, 5.x で blk-sq を完全に置換):
2層のキュー:
ソフトウェアキュー (ctx): per-CPU または per-cgroup → ロック競合ゼロ
ハードウェアキュー (hctx): デバイスのハードウェアキューにマッピング (NVMe SQ, SCSI tag)
Tags: ハードウェアキューの並列リクエスト数を管理するために使用
→ 各ハードウェアキューには固定のtagプールがあり、自然なレート制限となる
データ構造
// include/linux/blk-mq.h
;
;
送信パス: plug → scheduler → dispatch → driver
Plug: バッチ送信
// block/blk-mq.c + block/blk-plug.c
// plug は per-task の bio 蓄積メカニズムである:
// blk_start_plug(current) // plug を開始
// submit_bio() × N // N 個の bio を蓄積し、即時ディスパッチしない
// blk_finish_plug(current) // すべての bio を一括でフラッシュ
// 設計原理:
// 1. ロック競合の削減 (1回のロック取得で N 個のリクエストを挿入)
// 2. マージ機会の増加 (plug 期間中に front/back merge 可能)
// 3. ファイルシステムは通常、書き込みパスで plug を利用する:
// ext4_writepages() → blk_start_plug() → 複数回 submit_bio → blk_finish_plug()
IO スケジューラ
マルチキュー IO スケジューラと旧単一キュースケジューラの根本的な違い:スケジューラはグローバルではなく、per-hctx(ハードウェアキューごと)である。各ハードウェアキューは独立してスケジューリングされ、異なるキューは異なるCPUからの送信に対応しているためです。
mq-deadline
// block/mq-deadline.c
// 読み書きそれぞれで赤黒木 (sector順ソート) + FIFO (時計順ソート) を維持
//
// 主要パラメータ:
// read_expire: 読みリクエストの最大待機時間 (デフォルト 500ms)
// write_expire: 書きリクエストの最大待機時間 (デフォルト 5000ms)
// writes_starved: 書きリクエストの割り当て (何バッチの読みを優先処理した後に書きを処理するか)
//
// スケジューリングロジック:
// 1. FIFOを先に確認: 期限切れのリクエストがある場合 → 赤黒木からそのsector近辺のリクエストを取得 (一括送信)
// 2. それ以外: directionごとのbatchで送信 (読みを優先するが、writes_starvedにより書きが飢えるのを防止)
// 3. 1回のdispatchで最大16リクエストまで (他のキューの飢えを防ぐため)
//
// 適しているもの: NVMe, 汎用SSD
kyber
// block/kyber-iosched.c
// トークンバケットに基づく適応型スケジューリング
// 目標: 読み/書きキューの深さを制御し、レイテンシを目標値以内に維持
//
// 動作原理:
// 読み/書きのレイテンシヒストグラムを維持
// レイテンシに基づき、その方向のトークンバケットレートを動的に調整
// レイテンシが低い → バケットを増やす → より多くのIO並列実行
// レイテンシが高い → バケットを減らす → IO並列実行を減らす (デバイスキュー深さを低減)
//
// 適しているもの: レイテンシに敏感なSSD負荷 (例: データベース)
bfq (Budget Fair Queuing)
// block/bfq-iosched.c
// cgroup/プロセスごとにIO帯域幅を割り当て
// 各プロセスには「バジェット」(送信可能なセクタ数)が1つある
// B-WF2Q+ アルゴリズムに基づく (CFSの公平スケジューリングに類似)
//
// 特徴:
// - インタラクティブなプロセスは自動的に高いウェイトを得る (think timeを検出)
// - cgroup blkioコントローラをサポート
// 順序読みが検出される → より多くのバジェットを付与 → 高スループット
//
// 適しているもの: デスクトップシステム (バックグラウンドのバックアップがフォアグラウンドアプリの応答性を妨げないよう確保)
スケジューラの切り替え
# [mq-deadline] kyber bfq none
キューパラメータのチューニング
# ハードウェアキュー数 (通常はCPU数に等しい)
# max_sectors_kb: 1回のIOの最大サイズ (デフォルト 512KB = 1024セクタ)
# nr_requests: 各ハードウェアキューの最大リクエスト数
# スケジューラパラメータ
デバッグと観測
# blktrace: 各IOの完全なライフサイクルを追跡
# 出力: time, CPU, action (Q=キューイング, G=取得, I=挿入, D=ディスパッチ, C=完了)
# iostat: デバイスレベルの統計
# r/s, w/s, r_await, w_await, aqu-sz (平均キュー深さ), %util
# 単一デバイスの生カウント
# フィールド: read_ios, read_merges, read_sectors, read_ticks, write_ios, ...
# blk-mq デバッグ
# 各ハードウェアキューのtag使用状況
# ftrace でブロック層イベントを追跡
主要な設定とコンパイル
# カーネル起動パラメータ
scsi_mod.use_blk_mq=1 # SCSI に blk-mq を強制使用 (5.0+ でデフォルト)
elevator=mq-deadline # デフォルト IO スケジューラ
elevator=bfq # デスクトップ推奨
# カーネルコンパイルオプション
CONFIG_BLK_MQ=y # blk-mq を有効化
CONFIG_IOSCHED_BFQ=y # BFQ サポート
CONFIG_BLK_DEV_THROTTLING=y # cgroup blkio
参考と拡張
- ソースコード:
block/blk-mq.c(~4000行、blk-mq コア)、block/blk-merge.c、block/mq-deadline.c、block/bfq-iosched.c、block/kyber-iosched.c - カーネルドキュメント:
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/)
キーワード: bio, bio_vec, blk-mq, software queue, hardware queue, plug, mq-deadline, kyber, bfq, blktrace, iostat, IO scheduler