KVM の PCI Passthrough を有効化する

## 概要
KVM で PCI passthrough をするために必要な host 側の設定について説明する。要点は次の2つ：
- IOMMU を有効化し、PCI device からホストへの Direct Memory Access を VM 上のメモリアクセスにマップできるようにする
- 仮想ドライバ VFIO を有効化し、ホスト上の PCI device driver ではなく、ゲスト上の PCI device driver にマップできるようにする

![pci-passthrough.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261727/d3e49e6e-5ce8-3b8e-cb21-872b85fafecd.png)


- 図左：設定前。デフォルト、PCI Device がホスト側 driver にバインドされた状態
- 図右：設定後。IOMMU および VFIO の設定が完了した状態

本記事では、ホスト OS を Rocky Linux 9.4 を使用する。対象の PCI デバイス Intel Iris Xe (GPU) を例として説明する。

:::note warn
本記事の例では、host GPU の PCI Passthrough 設定を行う。この場合、ホスト側から GPU へのアクセスが遮断されるため、設定が成功するとディスプレイに何も映らなくなる可能性がある。したがって、事前にホストにリモートログインできるようにしておき、別マシンから SSH 経由で設定を行うようにする。
:::

## PCI Passthrough の有効化

### Prerequisites
ホスト側に次のソフトウェアをインストールしておく：
```
# sudo dnf install pciutils driverctl
```

- `pciutils` ... PCI 情報の閲覧のため
- `driverctl` ... VFIO に関する設定のため


### IOMMU の有効化
Intel VT-d (IOMMU) を有効化する： 
```
# grubby --args="intel_iommu=on iommu_pt" --update-kernel DEFAULT
```

Kernel Option に `intel_iommu=on` および `iommu_pt` が追加されたことを確認する：
```
# grubby --info=DEFAULT | grep iommu
```

ホストを再起動して設定を反映する：
```
# reboot
```

### VFIO の有効化
PCI Device の情報から PCI device address (ex: `0000:00:02.0`) を確認する：
```
# lspci -Dnnk | grep -A 4 -E "Iris"
0000:00:02.0 VGA compatible controller [0300]: Intel Corporation Raptor Lake-P [Iris Xe Graphics] [8086:a7a0] (rev 04)
        DeviceName: Onboard - Video
        Subsystem: Intel Corporation Device [8086:3037]
        Kernel driver in use: i915
        Kernel modules: i915
```

Intel Iris Xe `0000:00:02.0` にバインドされるドライバが `i915` (Interl GUI Driver) ではなく `vfio-pci` になるよう設定する：
```
# driverctl set-override 0000:00:02.0 vfio-pci
```

再度 PCI Device の情報を確認し、`kernel driver in use` key の値が `vfio-pci` になっていることを確認する：
```
# lspci -Dnnk | grep -A 4 -E "Iris"
0000:00:02.0 VGA compatible controller [0300]: Intel Corporation Raptor Lake-P [Iris Xe Graphics] [8086:a7a0] (rev 04)
        DeviceName: Onboard - Video
        Subsystem: Intel Corporation Device [8086:3037]
        Kernel driver in use: vfio-pci
        Kernel modules: i915
```


### 仮想マシンに VFIO デバイスを割り当てる

:::note
PCIe デバイスを passthrough する場合、qemu machine type は `q35` である必要がある。
:::

PCI device 情報の xml を記述し、ドメインに割り当てる。`<address>` タグの各属性は PCI device address の形式 `<domain>:<bus>:<slot>.<function>` から読み取る。例えば、さきほど `lspci` コマンドで確認した PCI address `0000:00:02.0` の場合は次のようになる：

```xml:
virsh attach-device <domain> --persistent /dev/stdin <<EOF
<hostdev mode='subsystem' type='pci' managed='yes'>
 <driver name='vfio'/>
 <source>
  <address domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
 </source>
</hostdev>
EOF
```

### Guest 側から PCI デバイスを確認する
Guest にログインし、Guest 側にも `pciutils` をインストール：
```
# sudo dnf install pciutils
```

Guest 側から　PCI device が認識されていることを確認する：

```
# lspci -Dnnk | grep -A 4 -E "Iris"
0000:09:00.0 VGA compatible controller [0300]: Intel Corporation Raptor Lake-P [Iris Xe Graphics] [8086:a7a0] (rev 04)
        Subsystem: Intel Corporation Device [8086:3037]
        Kernel driver in use: i915
        Kernel modules: i915
```

## もとの設定の復元
上記の設定を完了すると、ホストから PCI device を利用することはできなくなる。もとに戻したい場合、以下の手順を実行する。

Kernel option を削除する：
```
# grubby --remove-args="intel_iommu=on iommu_pt --update-kernel DEFAULT
```

本記事で設定した kernel options `intel_iommu=on`, `iommu_pt`, `pci-stub.ids` が削除されていることを確認する：
```
# grubby --info=DEFAULT
```

再起動して設定を反映する：

```
# reboot
```

デフォルトのドライバを元に戻す (例: `i915`) ：
```
# driverctl set-override 0000:00:02.0 i915
```


## Trouboleshooting
今回の例では Intel Iris Xe の PCI Passthrough を試みたが、GPU 特有の問題に当たったので Trouble shooting を残す。

### PCI デバイスの割り当て後、マシンを起動するとすぐにエラーで落ちる
`virsh start <domain>` 実行後、すぐに起動に失敗する場合は、OOM (Out Of Memory) Killer によって VM が kill されている可能性がある。これを確認するには、失敗した直後、ホスト上で `dmesg` を実行し出力を確認する：

```
# dmesg
...
[ 3443.989849] oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=system.slice,mems_allowed=0,global_oom,task_memcg=/machine.slice/machine-qemu\x2d9\x2dmaster0.scope/libvirt,task=qemu-kvm,pid=3563,uid=107
[ 3443.989880] Out of memory: Killed process 3563 (qemu-kvm) total-vm:34551624kB, anon-rss:31759628kB, file-rss:0kB, shmem-rss:0kB, UID:107 pgtables:62552kB oom_score_adj:0
```

#### 解決策：メモリ不足の解消
上記はメモリ不足が原因なので、VM のメモリを減らすなどして対策する。

参考：[Intel GVT-g: QEMU unexpectedly closed the monitor : r/VFIO](https://www.reddit.com/r/VFIO/comments/1036rm9/intel_gvtg_qemu_unexpectedly_closed_the_monitor/)


## Links

### 手順に関する参考資料

https://github.com/chikobara/guide-QEMU-KVM-GPU-Passthrough?tab=readme-ov-file

https://docs.redhat.com/ja/documentation/red_hat_enterprise_linux/9/html/configuring_and_managing_virtualization/assembly_managing-gpu-devices-in-virtual-machines_configuring-and-managing-virtualization#proc_assigning-a-gpu-to-a-virtual-machine_assembly_managing-gpu-devices-in-virtual-machines

### IOMMU, VFIO のアーキテクチャ関連の参考資料

https://wiki.qemu.org/Features/VT-d

https://developer.ibm.com/tutorials/l-pci-passthrough/