# 帳票設計書 19-ctlstat - CAMターゲットレイヤー統計レポート

## 概要

本ドキュメントは、FreeBSD の `ctlstat` コマンドが出力するCAMターゲットレイヤー（CTL）統計レポートの設計仕様を記述する。

### 本帳票の処理概要

ctlstat は、CAM Target Layer（CTL）のI/O統計情報をレポートするユーティリティである。CTLデバイスからioctlでLUN/ポート別のI/O統計データを取得し、スループット、転送レート、レイテンシなどを計算して出力する。4つの出力モード（標準、ダンプ、JSON、Prometheus）をサポートする。

**業務上の目的・背景**：iSCSIやファイバーチャネルなどのストレージターゲットの運用管理において、I/Oパフォーマンスの監視、ボトルネック特定、SLA遵守の確認のために使用される。

**帳票の利用シーン**：ストレージターゲットのパフォーマンス監視、LUN別/ポート別I/O統計の確認、Prometheus連携によるメトリクス収集、トラブルシューティング時のI/Oパターン分析などの場面で利用される。

**主要な出力内容**：
1. 標準モード: KB/t（転送あたりKB）、tps/dps（秒あたり転送/DMA数）、MB/s（スループット）、ms（レイテンシ）
2. ダンプモード: LUN/ポート別のバイト数、操作数、DMA数、I/O時間、DMA時間の生データ
3. JSONモード: ダンプモードと同じデータをJSON形式で出力
4. Prometheusモード: Prometheusスクレイプ対応のメトリクス出力（HTTP応答形式）

**帳票の出力タイミング**：管理者がコマンドラインから `ctlstat` コマンドを実行した際に標準出力に出力される。-w オプションで定期的な更新出力も可能。

**帳票の利用者**：ストレージ管理者、iSCSI/FC運用管理者

## 帳票種別

集計表（ストレージI/O統計）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| N/A | コマンドラインインターフェース | N/A | `ctlstat` コマンド実行 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | テキスト / JSON / Prometheus exposition format |
| 用紙サイズ | N/A |
| 向き | N/A |
| ファイル名 | N/A（標準出力） |
| 出力方法 | 標準出力（stdout） |
| 文字コード | ASCII |

## 帳票レイアウト

### レイアウト概要（標準モード）

CPU使用率、LUN/ポート別またはRead/Write/Total集計のI/O統計を表形式で繰り返し出力する。

```
┌──────────────────────────────────────────────────────────────┐
│ CPU      lun0           lun1           lun2                    │
│      ms  KB/t   tps MB/s  ms  KB/t  tps MB/s  ms  KB/t  tps MB│
├──────────────────────────────────────────────────────────────┤
│ xx%  x.x  xxx   xxx  xxx  x.x  xxx  xxx  xxx  x.x  xxx  xxx │
│ xx%  x.x  xxx   xxx  xxx  x.x  xxx  xxx  xxx  x.x  xxx  xxx │
└──────────────────────────────────────────────────────────────┘
```

### 標準モード明細部（LUN/ポート別）

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | CPU% | CPU使用率 | getcpu() / kern.cp_time | "%3.0Lf%%" |
| 2 | ms | 転送あたりミリ秒 | compute_stats() | "%5.1Lf" |
| 3 | KB/t | 転送あたりKB | compute_stats() | "%4.0Lf" |
| 4 | tps/dps | 秒あたり転送/DMA数 | compute_stats() | "%5.0Lf" |
| 5 | MB/s | 秒あたりMB | compute_stats() | "%4.0Lf" |

### 標準モード明細部（トータルモード -t）

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | Read | 全LUN/ポートのRead集計 | cur_total_stats[0] | 上記と同じ |
| 2 | Write | 全LUN/ポートのWrite集計 | cur_total_stats[1] | 上記と同じ |
| 3 | Total | 全LUN/ポートの合計 | cur_total_stats[2] | 上記と同じ |

### ダンプモード明細部（-D）

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | item | LUN/ポート番号 | stats[i].item | "lun %d" / "port %d" |
| 2 | io type | I/O種別（NO IO/READ/WRITE） | iotypes[] | 文字列 |
| 3 | bytes | バイト数 | stats[i].bytes[iotype] | "%ju" |
| 4 | operations | 操作数 | stats[i].operations[iotype] | "%ju" |
| 5 | dmas | DMA数 | stats[i].dmas[iotype] | "%ju" |
| 6 | io time | I/O時間 | stats[i].time[iotype] | bintime形式 |
| 7 | dma time | DMA時間 | stats[i].dma_time[iotype] | bintime形式 |

### JSONモード出力部（-j）

```json
{"luns":[{"num":0,"io":[{"type":"NO IO","bytes":0,"operations":0,"dmas":0,"io time":0.000000,"dma time":0.000000},{"type":"READ",...},{"type":"WRITE",...}]},...]
```

### Prometheusモード出力部（-P）

```
HTTP/1.1 200 OK
Connection: close
Content-Type: text/plain; version=0.0.4

# HELP iscsi_lun_bytes Number of bytes
# TYPE iscsi_lun_bytes counter
iscsi_lun_bytes{lun="0",target="",type="NO IO"} 0
iscsi_lun_bytes{lun="0",target="",type="READ"} 12345
...
# HELP iscsi_lun_operations Number of operations
# TYPE iscsi_lun_operations counter
...
```

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| CTLデバイスアクセス | CTL_DEFAULT_DEVが開けること | Yes（行971-972） |
| LUN/ポートフィルタ | -l/-p オプションでLUN/ポート番号を指定 | No |
| -l と -p は排他的 | 同時指定不可 | 制約（行949-950） |

### ソート順

LUN/ポート番号の昇順（CTLから返された順序）

## データベース参照仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| CTL I/O統計 | LUN/ポート別I/O統計取得 | ioctl(CTL_GET_LUN_STATS / CTL_GET_PORT_STATS) |
| CPU統計 | CPU使用率取得 | sysctlbyname("kern.cp_time") |
| CTLポートリスト | Prometheusモードでターゲット名取得 | ioctl(CTL_PORT_LIST) + XMLパース |

### テーブル別参照項目詳細

#### ctl_io_stats構造体

| 参照項目（カラム名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| item | LUN/ポート番号 | ioctl() | |
| bytes[CTL_STATS_NUM_TYPES] | バイト数（IO種別別） | ioctl() | NO IO/READ/WRITE |
| operations[CTL_STATS_NUM_TYPES] | 操作数（IO種別別） | ioctl() | |
| dmas[CTL_STATS_NUM_TYPES] | DMA数（IO種別別） | ioctl() | |
| time[CTL_STATS_NUM_TYPES] | I/O時間（IO種別別） | ioctl() | bintime型 |
| dma_time[CTL_STATS_NUM_TYPES] | DMA時間（IO種別別） | ioctl() | bintime型 |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| MB/s | total_bytes / (1024*1024) / etime | 整数 | 行276-281 |
| KB/t | total_bytes / 1024 / total_operations | 整数 | 行282-287 |
| tps | total_operations / etime | 整数 | 行288-296 |
| dps | total_dmas / etime | 整数 | 行289-296 |
| ms/transfer | (time_sec*1000 + time_nsec/1000000) / total_operations | 小数1位 | 行300-308 |
| ms/dma | (dma_time_sec*1000 + dma_time_nsec/1000000) / total_dmas | 小数1位 | 行310-318 |
| CPU% | (delta_jiffies - delta_idle) / delta_jiffies * 100 | 整数 | 行643-645 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[ctlstatコマンド実行] --> B[オプション解析]
    B --> C{Prometheusモード?}
    C -->|Yes| D[HTTPヘッダー出力]
    C -->|No| E[CTLデバイスオープン]
    D --> E
    E --> F[メインループ開始]
    F --> G{モード判定}
    G -->|STANDARD| H[getstats → ctlstat_standard]
    G -->|DUMP| I[getstats → ctlstat_dump]
    G -->|JSON| J[getstats → ctlstat_json]
    G -->|PROMETHEUS| K[getstats(LUN) + getstats(PORT) → ctlstat_prometheus]
    H --> L[sleep waittime]
    I --> L
    J --> L
    K --> L
    L --> M{count == 0?}
    M -->|No| F
    M -->|Yes| N[終了]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| CTLデバイスオープン失敗 | open(CTL_DEFAULT_DEV)失敗 | "cannot open %s" | err(1, ...) |
| ioctl失敗 | CTL_GET_*_STATS ioctl失敗 | "CTL_GET_*_STATS ioctl returned error" | err(1, ...) |
| ステータスエラー | get_stats.status == CTL_SS_ERROR | "CTL_GET_*_STATS ioctl returned CTL_SS_ERROR" | err(1, ...) |
| バッファ不足 | CTL_SS_NEED_MORE_SPACE (2回以上) | "CTL_GET_*_STATS returned NEED_MORE_SPACE again" | errx(1, ...) |
| -l/-p排他エラー | 両オプション同時指定 | "Options -p and -l are exclusive." | errx(1, ...) |
| -P排他エラー | -Pと-p/-c/-w/-tの同時指定 | "Option -P is exclusive with -p, -c, -w, and -t" | errx(1, ...) |
| CPU統計取得失敗 | sysctlbyname失敗 | "sysctlbyname(kern.cp_time...) failed" | warn() |
| XMLパースエラー | ポートリストXMLパース失敗 | "Unable to parse XML: Error %d" | errx(1, ...) |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | CTL_STAT_NUM_ITEMS=256（デフォルト、動的拡張） |
| 目標出力時間 | ioctl応答速度に依存 |
| 同時出力数上限 | 制限なし |

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

- CTLデバイス（/dev/cam/ctl）へのO_RDWRアクセスが必要
- 通常root権限が必要
- Prometheusモードでは外部からのHTTPアクセスを想定（適切なアクセス制御が必要）

## 備考

- 4つの出力モード: STANDARD（行93）, DUMP（行94）, JSON（行95）, PROMETHEUS（行96）
- デフォルトはSTANDARDモードで表示デバイス数3（行859）
- -t オプションでトータルモード（Read/Write/Total集計、行935-936）
- -d オプションでDMA時間表示（-dなしはI/O時間表示）
- -C オプションでCPU使用率表示を無効化
- -h オプションでヘッダー表示を無効化
- ヘッダーは20行間隔で再表示（行690）
- Prometheusモードではlunとportの両方の統計を出力（行989-991）
- Prometheusモードではターゲット名をCTL_PORT_LISTのXMLからパース（行576-588）
- バッファ不足時は自動的に5/4倍に拡張してリトライ（行200）
- kern.cam.ctl.max_lunsのsysctlでビットマスクサイズを取得（行866-870）
- iotypes配列: {"NO IO", "READ", "WRITE"}（行329）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ctl_ioctl.h | `cam/ctl/ctl_ioctl.h` | ctl_io_stats構造体、ctl_get_io_stats構造体、CTL_GET_LUN_STATS/CTL_GET_PORT_STATS ioctl定義 |
| 1-2 | ctlstat.c | `usr.bin/ctlstat/ctlstat.c` | ctlstat_context構造体（行119-133）、ctlstat_mode_types列挙型（行93-98）、フラグ定義（行100-117） |

**読解のコツ**: ctl_io_stats構造体はCTL_STATS_NUM_TYPES（3）の配列を持ち、NO IO/READ/WRITEの3種別でバイト数・操作数・DMA数・時間を保持する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ctlstat.c | `usr.bin/ctlstat/ctlstat.c` | main()（行845-1007）：オプション解析、モード設定、メインループ |

**主要処理フロー**:
1. **行854-863**: デフォルト値設定（numdevs=3、STANDARD、CPU/FIRST_RUN/HEADER有効）
2. **行865-870**: kern.cam.ctl.max_lunsからビットマスクサイズ取得
3. **行875-947**: getoptによるオプション解析
4. **行949-968**: -l/-p排他チェック、-P排他チェック
5. **行971-972**: CTLデバイスオープン
6. **行986-1004**: メインループ（get_and_print_stats → sleep → count判定）

#### Step 3: 統計取得と計算を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ctlstat.c | `usr.bin/ctlstat/ctlstat.c` | getstats()（行169-221）：ioctl呼び出しとバッファ管理 |
| 3-2 | ctlstat.c | `usr.bin/ctlstat/ctlstat.c` | compute_stats()（行245-319）：MB/s, KB/t, tps, ms計算 |
| 3-3 | ctlstat.c | `usr.bin/ctlstat/ctlstat.c` | getcpu()（行223-243）：CPU統計取得 |

#### Step 4: 各出力モードを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ctlstat.c | `usr.bin/ctlstat/ctlstat.c` | ctlstat_standard()（行616-803）：標準表形式出力 |
| 4-2 | ctlstat.c | `usr.bin/ctlstat/ctlstat.c` | ctlstat_dump()（行331-359）：生データダンプ出力 |
| 4-3 | ctlstat.c | `usr.bin/ctlstat/ctlstat.c` | ctlstat_json()（行361-396）：JSON形式出力 |
| 4-4 | ctlstat.c | `usr.bin/ctlstat/ctlstat.c` | ctlstat_prometheus()（行546-614）：Prometheus形式出力 |

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

```
main() [ctlstat.c:845]
    |
    +-- sysctlbyname("kern.cam.ctl.max_luns") [ビットマスクサイズ]
    +-- getopt() [オプション解析]
    +-- open(CTL_DEFAULT_DEV) [CTLデバイスオープン]
    |
    +-- [メインループ]
        +-- get_and_print_stats() [ctlstat.c:806]
            |
            +-- getstats() [ctlstat.c:169]
            |   +-- ioctl(CTL_GET_LUN_STATS / CTL_GET_PORT_STATS)
            |
            +-- [モード分岐]
                +-- ctlstat_standard() [ctlstat.c:616]
                |   +-- getcpu() [ctlstat.c:223]
                |   +-- compute_stats() [ctlstat.c:245]
                |   +-- fprintf() [表形式出力]
                |
                +-- ctlstat_dump() [ctlstat.c:331]
                |   +-- printf() [生データ出力]
                |
                +-- ctlstat_json() [ctlstat.c:361]
                |   +-- printf() [JSON出力]
                |
                +-- ctlstat_prometheus() [ctlstat.c:546]
                    +-- ioctl(CTL_PORT_LIST) [ポートリスト取得]
                    +-- XML_Parse() [XMLパース]
                    +-- printf() [Prometheus出力]
```

### データフロー図

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

CTLデバイス ────────> ioctl(CTL_GET_*_STATS) ──────> モード別出力
(/dev/cam/ctl)        [ctl_io_stats配列取得]
                            |
                      compute_stats()
                      [MB/s, KB/t, tps,       ──> STANDARD: 表形式
                       ms計算]                ──> DUMP: 生データ
                                              ──> JSON: JSON形式

kern.cp_time ────────> getcpu()
(sysctl)               [CPU統計]    ──────────> CPU%表示

CTL_PORT_LIST ───────> XML_Parse()
(ioctl)                [ターゲット名取得] ────> PROMETHEUS: メトリクス
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ctlstat.c | `usr.bin/ctlstat/ctlstat.c` | ソース | メインプログラム（1012行） |
| ctl_ioctl.h | `sys/cam/ctl/ctl_ioctl.h` | ヘッダ | CTL ioctl定義、ctl_io_stats構造体 |
| ctl.h | `sys/cam/ctl/ctl.h` | ヘッダ | CTL共通定義 |
| Makefile | `usr.bin/ctlstat/Makefile` | ビルド | ビルド設定 |
