# 通知設計書 45-ZFSイベント通知

## 概要

本ドキュメントは、FreeBSD カーネルの ZFS サブシステムが、ZFS 内部イベント（zevent）を devctl 通知として転送する「ZFS / ZFS / {type}」通知の設計を記述する。

### 本通知の処理概要

OpenZFS のイベントシステム（zevent）で発生した各種イベント（プールインポート、エラー検知、スクラブ完了など）を、FreeBSD の devctl 通知インターフェースに変換して送信する。ZFS の全イベントクラスがこの単一の通知経路を通じて配信される。

**業務上の目的・背景**：ZFS はエンタープライズレベルのファイルシステムであり、プールの状態変化、データ整合性エラー、デバイス障害などの重要なイベントを迅速に検知することが運用上不可欠である。この通知により、devd(8) 経由で ZFS イベントをリアルタイムに監視し、自動アラート、障害対応、監査ログの記録を実現できる。zpool events コマンドと並行して、プッシュ型の通知メカニズムを提供する。

**通知の送信タイミング**：OpenZFS カーネルモジュール内で zevent が生成されるたびに、sysevent_worker カーネルスレッドが zevent キューからイベントを取り出し、devctl 通知に変換して送信する。

**通知の受信者**：devd(8) デーモンを経由して、ZFS 監視ツール（zed 等）やシステム管理スクリプトが受信する。

**通知内容の概要**：ZFS イベントの全 nvlist 属性（イベントクラス、プール名、GUID、エラー情報など）がキー=値形式で含まれる。

**期待されるアクション**：受信者はイベントタイプに応じて、アラート発報、メール通知、自動修復（resilver開始）、ログ記録などのアクションを実行する。

## 通知種別

devctl カーネル通知（devd(8) 経由のシステムイベント通知）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（カーネル内 devctl_notify、専用ワーカースレッド） |
| 優先度 | 高 |
| リトライ | 無（zevent キューからの取り出しは自動） |

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

専用カーネルスレッド（sysevent_worker）が zevent キューを監視し、イベント取得後に devctl_notify() で配信する。

## 通知テンプレート

### devctl 通知フォーマット

| 項目 | 内容 |
|-----|------|
| system | ZFS |
| subsystem | ZFS |
| type | イベントクラス文字列（ESC_ZFS_* の場合は misc.fs.zfs.* に変換） |
| data | nvlist の全属性をスペース区切りのキー=値形式 |

### 本文テンプレート

```
!system=ZFS subsystem=ZFS type=misc.fs.zfs.vdev_statechange
 class=ESC_ZFS_vdev_statechange pool_name=mypool pool_guid=12345678 vdev_guid=87654321 vdev_state=ONLINE
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| class | イベントクラス（FM_CLASS 属性） | nvlist の FM_CLASS エントリ | Yes |
| (動的) | ZFS イベント固有の属性群 | nvlist の全 nvpair | No |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| カーネルイベント | ZFS zevent 生成 | sysevent_worker スレッドがイベントを取得 | ZFS サブシステム内の全イベント |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| sbuf メモリ割り当て失敗 | sbuf_new_auto() が NULL を返した場合 |
| sbuf_finish 失敗 | sbuf_finish() が非ゼロを返した場合 |
| type が NULL | イベントクラスが取得できなかった場合、空文字列として処理 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[ZFS 内部で zevent 生成] --> B[sysevent_worker スレッドで受信]
    B --> C[log_sysevent 呼び出し]
    C --> D[sbuf_new_auto でバッファ確保]
    D --> E{確保成功?}
    E -->|No| F[ENOMEM 返却]
    E -->|Yes| G[nvlist の全属性を sbuf に展開]
    G --> H{sbuf_finish 成功?}
    H -->|No| I[sbuf 解放、ENOMEM 返却]
    H -->|Yes| J{type が ESC_ZFS_ で始まる?}
    J -->|Yes| K[type を misc.fs.zfs.* に変換]
    J -->|No| L[type をそのまま使用]
    K --> M[devctl_notify ZFS/ZFS/type 発行]
    L --> M
    M --> N[sbuf 解放]
    F --> O[終了]
    I --> O
    N --> O
```

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

### 参照テーブル一覧

| 構造体 | 用途 | 備考 |
|-----------|------|------|
| nvlist_t (zevent) | ZFS イベントの全属性 | nvpair_t を走査して全属性を抽出 |
| zfs_zevent_t | zevent キュー管理構造体 | sysevent_worker で使用 |

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| メモリ不足 | sbuf_new_auto() が NULL | ENOMEM を返し、次のイベントの処理に進む |
| sbuf 構築失敗 | sbuf_finish() が非ゼロ | sbuf を解放し、ENOMEM を返す |
| zevent 待機中断 | zfs_zevent_wait() が ESHUTDOWN | ワーカースレッド終了 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（個別イベントのリトライなし） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A（エラー発生時は次のイベントに進む） |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 明示的な制限なし（zevent の発生頻度に依存） |
| 1日あたり上限 | 明示的な制限なし |

### 配信時間帯

制限なし

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

- ZFS イベントにはプール名、GUID、デバイスパスなどのインフラ情報が含まれる
- /dev/devctl の読み取りには root 権限が必要
- 大量の zevent が発生した場合、devctl キューの圧迫やバッファの肥大化に注意

## 備考

- sysevent_worker は zfskern プロセスの sysevent カーネルスレッドとして動作する
- dst_size は 131072 バイト（128KB）に設定されており、大きなイベントにも対応
- nvlist の NVLIST 型（ネストされた nvlist）は現在未実装で、スキップされる
- イベントクラスが ESC_ZFS_ で始まる場合は misc.fs.zfs.* に自動変換される
- ZFS Event Daemon (zed) は通常、zevent を直接監視するが、devctl 経由でも受信可能

---

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

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

### 推奨読解順序

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

ZFS イベント（zevent）の nvlist データ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | spl_sysevent.c | `sys/contrib/openzfs/module/os/freebsd/spl/spl_sysevent.c` | log_sysevent()（行46-223）で nvlist の各型の変換処理を理解 |

**読解のコツ**: nvlist は ZFS/Solaris 由来のキー値ペアデータ構造。nvpair_type() で型を判定し、型ごとに異なる nvpair_value_*() 関数で値を取得する。

#### Step 2: エントリーポイントを理解する

sysevent_worker スレッドの動作を追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | spl_sysevent.c | `sys/contrib/openzfs/module/os/freebsd/spl/spl_sysevent.c` | sysevent_worker()（行226-263）がメインループ |

**主要処理フロー**:
1. **行234**: zfs_zevent_init() で zevent リスナー初期化
2. **行235**: メインループ開始
3. **行239-240**: zfs_zevent_next() でイベント取得
4. **行242**: イベントなしの場合は zfs_zevent_wait() で待機
5. **行247**: log_sysevent() でイベントを devctl 通知に変換
6. **行248**: nvlist_free() でイベント解放

#### Step 3: イベントの devctl 変換を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | spl_sysevent.c | `sys/contrib/openzfs/module/os/freebsd/spl/spl_sysevent.c` | log_sysevent()（行46-223）の変換処理 |

**主要処理フロー**:
- **行53**: sbuf_new_auto() でバッファ自動拡張
- **行58-206**: nvlist 走査。各 nvpair の型に応じて sbuf_printf で文字列化
- **行119**: FM_CLASS 属性を見つけたら type 変数に格納
- **行215-218**: ESC_ZFS_ プレフィックスを misc.fs.zfs. に変換
- **行219**: devctl_notify("ZFS", "ZFS", type, sbuf_data(sb)) で通知発行

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

```
ddi_sysevent_init()                        [行266: ワーカースレッド起動]
    |
    +-- sysevent_worker()                  [行226: メインループ]
            |
            +-- zfs_zevent_next()          [行239: イベント取得]
            +-- zfs_zevent_wait()          [行242: イベント待機]
            +-- log_sysevent()             [行247: devctl 変換]
                    |
                    +-- nvlist_next_nvpair() [行58: nvlist 走査]
                    +-- devctl_notify()      [行219: 通知発行]
```

### データフロー図

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

ZFS 内部イベント -------> zevent キュー -------> sysevent_worker
                                                      |
nvlist_t (zevent) -----> log_sysevent()           --> devctl キュー
  FM_CLASS                   |                          |
  pool_name              nvpair 走査              devd(8) 受信
  vdev_guid              sbuf 構築
  ...                    型別変換
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| spl_sysevent.c | `sys/contrib/openzfs/module/os/freebsd/spl/spl_sysevent.c` | ソース | ZFS イベントの devctl 変換・通知発行 |
| protocol.h | `sys/contrib/openzfs/include/sys/fm/protocol.h` | ヘッダー | FM_CLASS 等の定数定義 |
| sysevent.h | `sys/contrib/openzfs/include/sys/sysevent.h` | ヘッダー | sysevent インターフェース |
| subr_bus.c | `sys/kern/subr_bus.c` | ソース | devctl_notify() 実装 |
