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