# 通知設計書 40-NVMe controller event

## 概要

本ドキュメントは、FreeBSDカーネルのNVMe（Non-Volatile Memory Express）サブシステムにおけるNVMeコントローライベント通知（NVMe controller event）の設計を記述する。NVMeコントローラドライバ（nvme_ctrlr.c）が、コントローラのリセット、SMARTエラー、非同期イベント（AEN）などの各種イベントを検出した際にdevctl経由でユーザランドへ通知を発行する仕組みを対象とする。

### 本通知の処理概要

本通知は、NVMeコントローラドライバが各種コントローラレベルのイベント（リセット、SMARTエラー、非同期イベント通知）を検出した際に発行される複数種類のカーネルレベル通知の集合体である。すべての通知はnvme_ctrlr_devctl/nvme_ctrlr_devctl_logヘルパー関数を経由して発行される。

**業務上の目的・背景**：NVMeストレージデバイスはサーバー・ワークステーションにおける主要なストレージメディアであり、コントローラレベルのイベント（特にSMARTエラーやリセット）は、デバイス障害の重要な前兆指標である。これらのイベントをリアルタイムでユーザランドに通知することで、ストレージ監視システムが予防的な対応（デバイス交換、データバックアップ、冗長構成の再構築等）を迅速に実施できる。

**通知の送信タイミング**：以下の3つのタイミングで通知が発行される。(1) コントローラリセットの開始/成功/タイムアウト時（nvme_ctrlr_reset_task）、(2) SMART健康状態異常検出時（nvme_ctrlr_log_critical_warnings）、(3) 非同期イベント通知（AEN）の受信時（nvme_ctrlr_aer_task）。

**通知の受信者**：devd(8)デーモンおよびdevctl(4)インターフェースを監視するユーザランドプロセス。

**通知内容の概要**：システム名「nvme」、サブシステム名「controller」、タイプは「RESET」「SMART_ERROR」「aen」のいずれか。データにはコントローラのデバイス名とイベント固有情報が含まれる。

**期待されるアクション**：RESET時はI/Oの一時停止/再開管理、SMART_ERROR時はデバイス交換の判断とデータバックアップ、AEN時はイベントタイプに応じた個別対応。

## 通知種別

カーネルdevctl通知（devctl_notify経由）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（タスクキューから発行） |
| 優先度 | 高（コントローラレベルイベント） |
| リトライ | 無し |

### 送信先決定ロジック

devctl_notifyによるブロードキャスト。

## 通知テンプレート

### devctl通知の場合

| 項目 | 内容 |
|-----|------|
| システム名 | nvme |
| サブシステム名 | controller |
| タイプ | RESET / SMART_ERROR / aen |
| データ | name="nvmeN" [イベント固有データ] |

### 本文テンプレート

```
!system=nvme subsystem=controller type=RESET name="nvme0" event="start"
!system=nvme subsystem=controller type=RESET name="nvme0" event="success"
!system=nvme subsystem=controller type=RESET name="nvme0" event="timed_out"
!system=nvme subsystem=controller type=SMART_ERROR name="nvme0" state=0x04
!system=nvme subsystem=controller type=aen name="nvme0" type=0x1 info=0x00 page=0x02
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| system | 通知のシステム名 | ハードコード "nvme" | Yes |
| subsystem | サブシステム名 | ハードコード "controller" | Yes |
| type | イベントタイプ | "RESET" / "SMART_ERROR" / "aen" | Yes |
| name | コントローラデバイス名 | device_get_nameunit(ctrlr->dev) | Yes |
| event | リセットイベント種別 | "start" / "success" / "timed_out" (RESET時) | No |
| state | SMARTクリティカル警告状態 | NVME_CRIT_WARN_STビットマスク (SMART_ERROR時) | No |
| type (data) | AENイベントタイプ | NVME_ASYNC_EVENT_TYPE (AEN時) | No |
| info | AENイベント情報 | NVME_ASYNC_EVENT_INFO (AEN時) | No |
| page | AENログページID | cpl->cdw0のビット23:16 (AEN時) | No |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| タスクキュー | nvme_ctrlr_reset_task | 常時 | コントローラリセット開始・成功・タイムアウト |
| SMART監視 | nvme_ctrlr_log_critical_warnings | SMARTクリティカル警告ビットが非ゼロ | SMART健康状態異常 |
| AENコールバック | nvme_ctrlr_aer_task | ログページIDが有効 | 非同期イベント通知受信 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| sbuf確保失敗 | sbuf_new(SBUF_NOWAIT)がNULL返却 |
| sbuf_finish失敗 | sbuf_finishがエラーを返した場合 |
| コントローラリセット中（AEN時） | ctrlr->is_resettingが真の場合、AENタスクは即座にreturn |
| ログページID無効（AEN時） | is_log_page_id_validが偽の場合、devctl通知をスキップ |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[NVMeコントローライベント発生] --> B{イベント種別}
    B -->|リセット| C[nvme_ctrlr_reset_task]
    B -->|SMART警告| D[nvme_ctrlr_log_critical_warnings]
    B -->|AEN| E[nvme_ctrlr_aer_task]
    C --> F["nvme_ctrlr_devctl_log(RESET, event=...)"]
    D --> G["nvme_ctrlr_devctl(SMART_ERROR, state=...)"]
    E --> H{ログページID有効?}
    H -->|Yes| I["nvme_ctrlr_devctl(aen, type=... info=... page=...)"]
    H -->|No| J[通知スキップ]
    F --> K[nvme_ctrlr_devctl_va]
    G --> K
    I --> K
    K --> L["devctl_notify(nvme, controller, type, sbuf_data)"]
```

## データベース参照・更新仕様

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| sbuf確保失敗 | sbuf_new(SBUF_NOWAIT)がNULL | return で通知をスキップ |
| sbuf_finish失敗 | データ構築中のエラー | devctl_notifyを呼ばずにスキップ |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0 |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし

## セキュリティ考慮事項

/dev/devctlへのアクセスはrootのみ。通知にはコントローラのデバイス名と状態情報が含まれるが、ユーザデータは含まれない。

## 備考

- nvme_ctrlr_devctl_logはdevctl_notifyに加えてdevice_printfによるカーネルログ出力も行う
- RESET通知はstart/success/timed_outの3段階で発行され、リセットの進行状況を追跡可能
- SMART_ERRORのstateビットは以下を含む：温度超過、信頼性劣化、読取専用、揮発性メモリバックアップ障害、永続メモリ異常
- AENはNVMe仕様のAsynchronous Event Notification機能で、コントローラが自発的に発行するイベント
- nvme_ctrlr_devctl_vaはすべての通知の共通基盤で、name=デバイス名を自動付加する

---

## コードリーディングガイド

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | nvme_private.h | `sys/dev/nvme/nvme_private.h` | nvme_controller構造体、nvme_async_event_request構造体 |
| 1-2 | nvme.h | `sys/dev/nvme/nvme.h` | NVME_ASYNC_EVENT_TYPE, NVME_CRIT_WARN_ST定数 |

**読解のコツ**: NVMeの非同期イベントはcompletion entry（cpl）のcdw0フィールドにイベントタイプ、情報、ログページIDがエンコードされている。NVMEVマクロでビットフィールドを抽出する。

#### Step 2: 共通通知ヘルパーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | nvme_ctrlr.c | `sys/dev/nvme/nvme_ctrlr.c` | nvme_ctrlr_devctl_va関数（65-79行目）- 通知の共通基盤 |
| 2-2 | nvme_ctrlr.c | `sys/dev/nvme/nvme_ctrlr.c` | nvme_ctrlr_devctl関数（82-89行目）- 可変引数ラッパー |
| 2-3 | nvme_ctrlr.c | `sys/dev/nvme/nvme_ctrlr.c` | nvme_ctrlr_devctl_log関数（92-110行目）- ログ出力付きラッパー |

**主要処理フロー**:
- **71-72行目**: sbufでNOWAITバッファ確保
- **73行目**: name=デバイス名を先頭に追加
- **74行目**: 可変引数メッセージを追加
- **77行目**: devctl_notify("nvme", "controller", type, sbuf_data) 呼び出し

#### Step 3: 各イベントタイプの処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | nvme_ctrlr.c | `sys/dev/nvme/nvme_ctrlr.c` | nvme_ctrlr_reset_task関数（1148-1164行目）- RESET通知 |
| 3-2 | nvme_ctrlr.c | `sys/dev/nvme/nvme_ctrlr.c` | nvme_ctrlr_log_critical_warnings関数（650-683行目）- SMART_ERROR通知 |
| 3-3 | nvme_ctrlr.c | `sys/dev/nvme/nvme_ctrlr.c` | nvme_ctrlr_aer_task関数（1182-1219行目）- AEN通知 |

**主要処理フロー（RESET）**:
- **1153行目**: nvme_ctrlr_devctl_log(ctrlr, "RESET", "event=\"start\"")
- **1156行目**: 成功時 nvme_ctrlr_devctl_log(ctrlr, "RESET", "event=\"success\"")
- **1159行目**: タイムアウト時 nvme_ctrlr_devctl_log(ctrlr, "RESET", "event=\"timed_out\"")

**主要処理フロー（SMART_ERROR）**:
- **682行目**: nvme_ctrlr_devctl(ctrlr, "SMART_ERROR", "state=0x%02x", state)

**主要処理フロー（AEN）**:
- **1204-1206行目**: nvme_ctrlr_devctl(ctrlr, "aen", "type=0x%x info=0x%x page=0x%x", ...)

### プログラム呼び出し階層図

```
NVMeコントローライベント
    |
    +-- nvme_ctrlr_reset_task()                 [nvme_ctrlr.c:1148]
    |       +-- nvme_ctrlr_devctl_log("RESET", ...) [1153/1156/1159]
    |               +-- nvme_ctrlr_devctl_va()  [65]
    |                       +-- devctl_notify("nvme","controller","RESET",...) [77]
    |
    +-- nvme_ctrlr_log_critical_warnings()      [650]
    |       +-- nvme_ctrlr_devctl("SMART_ERROR", ...) [682]
    |               +-- nvme_ctrlr_devctl_va()  [87]
    |                       +-- devctl_notify("nvme","controller","SMART_ERROR",...) [77]
    |
    +-- nvme_ctrlr_aer_task()                   [1182]
            +-- nvme_ctrlr_devctl("aen", ...)   [1204]
                    +-- nvme_ctrlr_devctl_va()  [87]
                            +-- devctl_notify("nvme","controller","aen",...) [77]
```

### データフロー図

```
[入力]                     [処理]                        [出力]

NVMeコントローラ         nvme_ctrlr_devctl_va()     /dev/devctl
(リセット/SMART/AEN) --> sbuf構築 -----------------> devctl_notify()
completion entry         name + イベントデータ       devdデーモン
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| nvme_ctrlr.c | `sys/dev/nvme/nvme_ctrlr.c` | ソース | NVMeコントローラドライバ本体、devctl_notify発行 |
| nvme_private.h | `sys/dev/nvme/nvme_private.h` | ヘッダ | NVMeドライバ内部構造体定義 |
| nvme.h | `sys/dev/nvme/nvme.h` | ヘッダ | NVMe仕様関連定数定義 |
| subr_bus.c | `sys/kern/subr_bus.c` | ソース | devctl_notify関数の実装 |
