NVMe SSDにfault injectionしてみる
この記事は
Linux kernelのfalult injection機能の(特にNVMe SSDに関連する部分について)
ソースを読んだり、実際に使ってみたりする記事のうちの一つ
記事一覧
その1:fault injectionってなあに
その2:fault injection機能が埋まっている箇所のソースを読む
その3:NVMe SSDにfault injectionしてみる ★今回はこれ
NVMe SSDにfault injectionしてみる
環境準備
OSインストール
なんでもいいんですが
私はVirtualBox上にUbuntu22.04.3をインストールした
SATA(AHCI)コントローラーに接続されているHDDがシステムディスク
NVMeコントローラに接続されているSSDがデータディスク
システムディスクにfalult injectionするのはちょっと不安なので、
NVMe SSDはデータディスクとして準備するのがおすすめ
fault injection機能の有効化
Ubuntu22.04.3では、fault injectionは無効になっているため
機能を有効化するためにKernelをビルドする
Linux Kernelのソースを入手する
手順はUbuntuの公式Wiki (と自分の記憶)などを参考にしました
/etc/apt/source.listのdeb-srcの行のコメントをすべて外す
deb-src hogehoge
パッケージのリストを更新
apt update
適当な作業用ディレクトリに移動し、ソースのパッケージをダウンロード
apt-get source linux-image-$(uname -r)
以下三つのファイルが落ちてくるので
以下をたたいてUbuntu向けの修正が反映されたソースを生成する
dpkg-source -x linux_hoge.dsc
Linux Kernelをビルドする
kernelのビルドに必要なパッケージをインストールする
apt install make gcc flex bison libncurses-dev libelf-dev libssl-dev
ソースのルートディレクトリに移動
デフォルトのconfigファイルをKernelビルドに再利用するためコピー
cp /boot/config-hoge ./.config
fault injectionを有効にするよう.configを編集
CONFIG_FAULT_INJECTION=y
make oldconfigたたいてfault injection関係と思われるものはyを選択
Kernelのビルドとdpkgの作成
make -$(nproc) deb-pkg
ビルドしたKernelの適用
apt install linux-headers-hoge_amd64.deb apt install linux-headers-hoge_amd64.deb
再起動する(ビルドしたKernelで起動しなおす)
/sys/kernel/debug/nvme*が存在することを確認する
root@nodoguro-VirtualBox:/home/nodoguro# ls /sys/kernel/debug/nvme0 nvme0/ nvme0n1/
実践編
ターゲットの確認
ターゲットとなるディスクを確認しておく
今回は/dev/nvme0n1
root@nodoguro-VirtualBox:/home/nodoguro# parted -l Model: ATA VBOX HARDDISK (scsi) Disk /dev/sda: 107GB Sector size (logical/physical): 512B/512B Partition Table: gpt Disk Flags: Number Start End Size File system Name Flags 1 1049kB 538MB 537MB fat32 EFI System Partition boot, esp 2 538MB 107GB 107GB ext4 Error: /dev/nvme0n1: unrecognised disk label Model: ORCL-VBOX-NVME-VER12 (nvme) Disk /dev/nvme0n1: 26.8GB Sector size (logical/physical): 512B/512B Partition Table: unknown Disk Flags:
fault injectionしてみる
今回は公式ドキュメントのnvme-fault-injectionに記載のある以下3種のエラー注入を試してみる
Example 1:Inject default status code with no retry
Example 2: Inject default status code with retry
Example 3: Inject an error into the 10th admin command
今回は、ddコマンドでランダムなデータを書き込んでテストする
正常に終了した場合は以下のようになる
root@nodoguro-VirtualBox:/home/nodoguro# dd if=/dev/urandom of=/dev/nvme0n1 count=10000 10000+0 records in 10000+0 records out 5120000 bytes (5.1 MB, 4.9 MiB) copied, 0.526876 s, 9.7 MB/s
Example 1:Inject default status code with no retry
100%の確率で1度だけfault injectionする
リトライしない
設定
echo 1 > /sys/kernel/debug/nvme0n1/fault_inject/times echo 100 > /sys/kernel/debug/nvme0n1/fault_inject/probability
結果
root@nodoguro-VirtualBox:/home/nodoguro/source# dd if=/dev/urandom of=/dev/nvme0n1 count=10000 dd: writing to '/dev/nvme0n1': Input/output error 1+0 records in 0+0 records out 0 bytes copied, 0.00110659 s, 0.0 kB/s
エラーでこけていて期待通り
Example 2: Inject default status code with retry
100%の確率で1度だけfault injectionする
リトライする
設定
echo 1 > /sys/kernel/debug/nvme0n1/fault_inject/times echo 100 > /sys/kernel/debug/nvme0n1/fault_inject/probability echo 0 > /sys/kernel/debug/nvme0n1/fault_inject/dont_retry
結果
root@nodoguro-VirtualBox:/home/nodoguro/source# dd if=/dev/urandom of=/dev/nvme0n1 count=10000 10000+0 records in 10000+0 records out 5120000 bytes (5.1 MB, 4.9 MiB) copied, 0.600538 s, 8.5 MB/s
リトライするのでエラー見えないで期待通り
Example 3: Inject an error into the 10th admin command
しばらくしてから100%の確率で1度だけfault injectionする
※admin commandと書いてあるが、他と合わせてddで試す
設定
echo 100 > /sys/kernel/debug/nvme0n1/fault_inject/probability echo 10 > /sys/kernel/debug/nvme0n1/fault_inject/space echo 1 > /sys/kernel/debug/nvme0n1/fault_inject/times
結果
root@nodoguro-VirtualBox:/home/nodoguro/source# dd if=/dev/urandom of=/dev/nvme0n1 count=10000 dd: writing to '/dev/nvme0n1': Input/output error 73+0 records in 72+0 records out 36864 bytes (37 kB, 36 KiB) copied, 0.00613897 s, 6.0 MB/s
最初はIO成功、途中でIOが失敗しており期待通り
最後に
NVMe SSDへのfault injectionを実際に使ってみるところまでお試しできた
当初の予定通り、使ってみるところまでできて良かった
何か調べたいときに適したツールでサクッと調査できるしぐさに常に憧れがあるが
こういった、Linux Kernelに埋まっている、今はまだ知らない機能たちを触っていくのがそれに近づく一歩な気がしている
(これに関しては使うにはKernelのビルドが必要で超時間がかかっちゃうけどね..)
fault injection機能が埋まっている箇所のソースを読む
この記事は
Linux kernelのfalult injection機能の(特にNVMe SSDに関連する部分について)
ソースを読んだり、実際に使ってみたりする記事のうちの一つ
記事一覧
その1:fault injectionってなあに
その2:fault injection機能が埋まっている箇所のソースを読む ★今回はこれ
その3:NVMe SSDにfault injectionしてみる
fault injection機能が埋まっている箇所のソースを読む
なお、ここで読んでいくソースはUbuntu 22.04.3(6.2.0-39-generic)のものです
お目当ての処理をどうやって探そうか?
Linux Kernelのドキュメントのfault-injectionのページによると、
fault injection機能を新規に追加したい開発者向けに、以下のマクロが準備されている
- DECLARE_FAULT_ATTR()
また、関数レベルでのfault injectionを新規に追加*1 したい開発者向けに、以下のマクロが準備されている
- ALLOW_ERROR_INJECTION()
と、いうことで、
このあたりにエラー注入できたりしないかな/エラー注入は具体的に何をどうしているのかな、と探すときは
以下くらいでgrepするとよさそう
- DECLARE_FAULT_ATTR
- ALLOW_ERROR_INJECTION
*1 fail_function機能の一部として、関数の戻り値の差し替えを実装するということ
NVMe SSDのfault injection機能が埋まっている箇所を探す
DECLARE_FAULT_ATTRでgrepしてみると、以下が見つかった
drivers/nvme/host/fault_inject.c
static DECLARE_FAULT_ATTR(fail_default_attr);
ファイル名からして、まさにこれです、私が欲しかったのは、という感じがする
NVMe SSDのfault injection機能周りを読んでみる
Makefile
drivers/nvme/host/fault_inject.c と同じ階層にあるMakefileを見てみると
CONFIG_FAULT_INJECTION_DEBUG_FSがy以外の場合には
fault_inject.oは使用しない設定になっている
(Ubuntu 22.04.3では使用しない設定)
drivers/nvme/host/Makefile
nvme-core-$(CONFIG_FAULT_INJECTION_DEBUG_FS) += fault_inject.o
実際に使ってみるにはKernelのビルドが必要
エラーの注入処理
drivers/nvme/host/fault_inject.cにある
nvme_should_fail()がエラー注入の箇所そのものの様子
リクエストのステータスとしてNVME_SC_INVALID_OPCODE | NVME_SC_DNR
を設定する
(初期化時に、fault_inject->statusにはNVME_SC_INVALID_OPCODEが設定されるため)
void nvme_should_fail(struct request *req) { struct gendisk *disk = req->q->disk; struct nvme_fault_inject *fault_inject = NULL; u16 status; if (disk) { struct nvme_ns *ns = disk->private_data; if (ns) fault_inject = &ns->fault_inject; else WARN_ONCE(1, "No namespace found for request\n"); } else { fault_inject = &nvme_req(req)->ctrl->fault_inject; } if (fault_inject && should_fail(&fault_inject->attr, 1)) { /* inject status code and DNR bit */ status = fault_inject->status; if (fault_inject->dont_retry) status |= NVME_SC_DNR; nvme_req(req)->status = status; } }
このnvme_should_fail()は、nvme_try_complete_req()からのみコールされており
nvme_try_complete_req()は名前からして、ストレージに書き込みが完了した後にコールされるような雰囲気
ストレージにはIO発行しているけど、ステータスとしては失敗したように見せるのか
(NVMeのIO周りは土地勘がなくて確実なところは言えないけど)
追記:NVMe SSDにfault injectionしてみるで見えたCall stackより、
ストレージ書き込み完了後のHw割り込み延長でエラー注入で間違いなさそう
12月 26 01:47:47 nodoguro-VirtualBox kernel: CPU: 4 PID: 0 Comm: swapper/4 Tainted: G W OE 6.2.16 #1 12月 26 01:47:47 nodoguro-VirtualBox kernel: Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 12月 26 01:47:47 nodoguro-VirtualBox kernel: Call Trace: 12月 26 01:47:47 nodoguro-VirtualBox kernel: <IRQ> 12月 26 01:47:47 nodoguro-VirtualBox kernel: dump_stack_lvl+0x48/0x70 12月 26 01:47:47 nodoguro-VirtualBox kernel: dump_stack+0x10/0x20 12月 26 01:47:47 nodoguro-VirtualBox kernel: should_fail_ex+0x1ab/0x1b0 12月 26 01:47:47 nodoguro-VirtualBox kernel: should_fail+0xb/0x20 12月 26 01:47:47 nodoguro-VirtualBox kernel: nvme_should_fail+0x46/0xd0 [nvme_core] 12月 26 01:47:47 nodoguro-VirtualBox kernel: nvme_poll_cq+0x16e/0x390 [nvme] 12月 26 01:47:47 nodoguro-VirtualBox kernel: ? srso_alias_return_thunk+0x5/0x7f 12月 26 01:47:47 nodoguro-VirtualBox kernel: nvme_irq+0x40/0x90 [nvme] 12月 26 01:47:47 nodoguro-VirtualBox kernel: __handle_irq_event_percpu+0x4f/0x1b0 12月 26 01:47:47 nodoguro-VirtualBox kernel: handle_irq_event+0x39/0x80 12月 26 01:47:47 nodoguro-VirtualBox kernel: handle_fasteoi_irq+0x7d/0x1d0 12月 26 01:47:47 nodoguro-VirtualBox kernel: __common_interrupt+0x52/0x110 12月 26 01:47:47 nodoguro-VirtualBox kernel: common_interrupt+0x9f/0xb0 12月 26 01:47:47 nodoguro-VirtualBox kernel: </IRQ> 12月 26 01:47:47 nodoguro-VirtualBox kernel: <TASK> 12月 26 01:47:47 nodoguro-VirtualBox kernel: asm_common_interrupt+0x27/0x40
fault injectionってなあに
この記事は
Linux kernelのfalult injection機能の(特にNVMe SSDに関連する部分について)
ソースを読んだり、実際に使ってみたりする記事のうちの一つ
記事一覧
その1:fault injectionってなあに ★今回はこれ
その2:fault injection機能が埋まっている箇所のソースを読む
その3:NVMe SSDにfault injectionしてみる
(順次追加予定)
事の始まり
Kernel/VM探検隊@北陸 Part 6 - connpassへ参加してきた
satさんのセッションでは、タイトルである
「device mapperによるディスクI/O障害のエミュレーション」のほか、
それ以外のディスクI/O障害のエミュレーションの手法についても簡単な紹介があった
そもそも、kernel/driverに自分でデバッグコード埋め込まなくてもそんなことできるって初めて知ったぞ…
私はHwよりのところのドライバを触っている人なので、
紹介のあった手法の中で、よりHwに近い領域で障害をエミュレーションする、
NVMe SSDへのfault injection機能について気になった
fault injectionってなあに
意図的にエラーを発生させるための機能
通常、エラー処理は正常処理と比較するとあまり実行されない
fault injectionにより意図的にエラーを発生させ、コードのカバレッジを高めていきたいということらしい
公式のドキュメント*1 を読みながらもう少し詳細なところを確認していく
*1 Fault injection capabilities infrastructure — The Linux Kernel documentation
どこに対してfault injectionできるの
現在injection可能なのは以下
- slab allocation failures
- page allocation failures
- failures in user memory access functions
- futex deadlock and uaddr fault errors
- kernel RPC client and server failures
- disk IO errors
- MMC data errors on devices permitted
- return on specific functions
- NVMe status code and retry flag
- IO timeouts
この項目からはわからないが、 PMのPrepareの段階の失敗をinjectnionすることもできる様子、気になる
まだ棚ぼた的に使ってみたい箇所の発見もあるかもしれないという気がする
次回以降の記事で、いい感じのキーワードでソース上をgrepしていい感じに見ていきたい
動作のチューニング
injectnionする失敗について、以下の項目がチューニングが可能
- likelihood of failure injection
- interval between failure
- how many times failures may happen at most
- initial resource "budget"
- verbosity of the messages
- filtering by process
- specifies the range of virtual addresses
- stacktrace depth
- inject failures into highmem/user allocations
- inject failures into allocations that can sleep
- minimum page allocation order
- disconnect injection on the RPC client
- disconnect injection on the RPC server
- cache wait injection on the RPC
- target function
- shows error injectable functions(read only)
- "error" return value
かなり沢山項目がある
使ってみながら見ていく感じかなあ
最後に
興味をくすぐるテーマに出会わせてくれたsatさん、
その機会を提供をしていただいたイベントの運営の皆さん、ありがとうございました
…もうすべての記事を書き終わったかのような口ぶりだが
どこかで力尽きたときのための念のための保険で、一応書ききるつもりはある
ちなみに、satさんがセッションと同等の内容を
Youtubeにも挙げていらっしゃるのでそちらも要チェックだ
その63 device mapperによるディスクI/O障害のエミュレーション 既存ターゲット編 - YouTube
その64 分散ストレージCephのデータ破壊検知修復機能は本当に動作するのか - YouTube
その65 device mapperによるディスクI/O障害のエミュレーション カーネルモジュール自作編 - YouTube