ProxmoxVEでVMからGPUにアクセスする


追記：2022/5/9
Proxmox VE 7.2がリリースされアップグレード後にKernelのバージョンが5.15に変更されます。PCI Passthroughに関する問題が記載されていますので、しばらくkenelのバージョンを前の5.13まで戻すことを推奨します。

https://pve.proxmox.com/wiki/Roadmap#Proxmox_VE_7.2

https://qiita.com/disksystem/items/ec8d81548f3b106f74e7

## やること
ProxmoxVEで仮想マシンを構築し、NVIDIAのグラボが使えるようにする。
## Part1 BIOSの設定
BIOS画面からCPUの仮想化オプションを有効化する。
IntelならVT-d、AMDならIOMMUという名前の項目があるはず。
## Part2 GRUBオプションの編集
`/etc/default/grub` を編集する

```bash
#Before
GRUB_CMDLINE_LINUX_DEFAULT="quiet"
#After(Intelの場合)
GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on iommu=pt video=efifb:off"
#After(AMDの場合)
GRUB_CMDLINE_LINUX_DEFAULT="quiet amd_iommu=on iommu=pt video=efifb:off"
```

オプションの狙いについては以下の表を参照

| オプション名       |       効果 |重要度|
|:-----------------:|:------------------|:--:|
| amd_iommu=on             | AMDのCPUでIOMMUを有効化 |必須|
| intel_iommu=on           | IntelのCPUでIOMMUを有効化 |必須|
| iommu=pt             | ホスト側のLinuxからGPUにアクセスしない |推奨|
| video=efifb:off               | WindowsゲストのError43問題対策 |推奨|

Error43問題とはNvidiaのドライバーが仮想環境のWindowsで正常に動作しないというもの。
Windowsのデバイスマネージャでグラボを見るとError43と表示されている。念の為に設定することにする。

変更した設定を反映する

```bash
$ update-grub
```

なお、この変更後に再起動すると以後モニタに何も表示されなくなる。設定完了後、仮想マシンを起動するとGPUのHDMUに接続したモニタに仮想マシンの画面が表示される。その場合Webの管理画面からコマンド操作を行う。
内蔵GPUがないタイプのCore-i9やRyzenではそうするしかないが、そうでなければマザーボードのHDMIに差し替えればコンソールの画面が表示されるはず

## PART3 モジュール系
`/etc/modules` 必要なモジュールを追加

```bash
vfio
vfio_iommu_type1
vfio_pci
vfio_virqfd
```

`/etc/modprobe.d/blacklist.conf` 読み込まないモジュールを記述

```bash
blacklist pcspkr
blacklist radeon
blacklist nvidia
blacklist nvidiafb
blacklist nouveau
blacklist amdgpu
blacklist vfio
```

この辺で一旦サーバーを再起動

## PART4 設定ファイル系

`/etc/modprobe.d/kvm.conf` に以下オプションを記述

```bash
#MSRという特殊なレジスタへゲストからアクセスをしない
options kvm ignore_msrs=1
```

--- 
追記）以下については古い情報。
nptのバグは修正され、設定しない方がパフォーマンスがよい
https://wiki.archlinux.jp/index.php/OVMF_%E3%81%AB%E3%82%88%E3%82%8B_PCI_%E3%83%91%E3%82%B9%E3%82%B9%E3%83%AB%E3%83%BC#AMD_CPU_.E3.81.A7.E3.83.91.E3.83.95.E3.82.A9.E3.83.BC.E3.83.9E.E3.83.B3.E3.82.B9.E3.82.92.E6.94.B9.E5.96.84.E3.81.99.E3.82.8B

以前追記したオプションの行を削除したところ、画面のカクツキが解消されました。

`/etc/modprobe.d/kvm_amd.conf` にAMDの場合以下オプションを記述

```bash
#AMDでNPTを無効化するとパフォーマンスが改善される
options kvm_amd npt=0
```
--- 
`/etc/modprobe.d/iommu_unsafe_interrupts.conf` に以下記述

```bash
#ハードウェア割り込みの再マッピングをサポートしていないためにパススルーが失敗する場合があるので念の為
options vfio_iommu_type1 allow_unsafe_interrupts=1
```

デバイスIDを調査

```
$ dmesg | grep -i vfio
[151588.420384] VFIO - User Level meta-driver version: 0.3
[151588.422724] vfio-pci 0000:26:00.0: vgaarb: changed VGA decodes: olddecodes=io+mem,decodes=none:owns=none
[151588.446167] vfio_pci: add [10de:1c82[ffffffff:ffffffff]] class 0x000000/00000000
[151588.446178] vfio_pci: add [10de:0fb9[ffffffff:ffffffff]] class 0x000000/00000000
[151593.438821] vfio-pci 0000:26:00.0: vfio_ecap_init: hiding ecap 0x19@0x900
[156099.312136] vfio-pci 0000:26:00.0: vfio_ecap_init: hiding ecap 0x19@0x900
```

10de:1c82と10de:0fb9が欲しい部分。`/etc/modprobe.d/vfio.conf` に以下記述

```bash
options vfio-pci ids=10de:1c82,10de:0fb9 disable_vga=1
```

## PART5 グラボからROMの吸出し(RyzenなどCPU内蔵GPUがない場合のみ)
※かつてはNVIDIA Tesla, Quadro等にGPUの仮想化を限定していたようで、ハードウェアを精密に偽装する必要があったが、現在はGeForceもBetaではあるがサポートするようになった。Nvidia DriverのバージョンがR465以降であれば下記をしなくても動作するとのこと。
引用（https://nvidia.custhelp.com/app/answers/detail/a_id/5173/~/geforce-gpu-passthrough-for-windows-virtual-machine-%28beta%29）


プライマリーのGPUのROMはホスト側が起動時にロックしてしまうので、仮想マシンから読むことができなくなる。Intel＋Nvidiaの構成ならばCPUの内蔵GPUがプライマリで、Nvidiaがセカンダリなので問題ないが、RyzenのようにCPUに内蔵GPUがない場合はNvidiaのグラボがプライマリーとなりROMが読めなくなる。
具体的な問題として、Nvidiaのドライバのインストールに失敗する（Error43問題）
どうしたかというとGPUを2枚刺しにして、ROMをファイルとして控えておき仮想マシンに渡すという処理をしている。

```bash
$ lspci |grep NVIDIA
26:00.0 VGA compatible controller: NVIDIA Corporation GP107 [GeForce GTX 1050 Ti] (rev a1)
26:00.1 Audio device: NVIDIA Corporation GP107GL High Definition Audio Controller (rev a1)
```
26:00.0と26:00.1の番号を後々使うので控えておく

```bash
cd /sys/bus/pci/devices/0000:26:00.0/
echo 1 > rom
cat rom > /usr/share/kvm/Z_1050Ti.rom
echo 0 > rom
```

※GTX10XX系は更にVFIOパススルーできるようにROMにパッチを当てないとうまく行かなかった。
https://github.com/Matoking/NVIDIA-vBIOS-VFIO-Patcher

今回はWindowsでパッチをあてて、`/usr/share/kvm/` のなかにZ_1050Ti_MOD.romの名前で保存

## PART6 仮想マシンの作成
ようやくVMを作り始める。普通のWindows設定
![スクリーンショット 2020-04-13 12.43.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65278/715206e7-935a-7381-17c9-c30e9e93aaac.png)
「システム」で注視点がいくつかある

 * グラフィックカードは「none」
 * QEMUエージェントを有効に
 * BIOSをOVFM(UEFI)に（超重要）
 * AddEFIでEFIディスクを追加
 * 形式はrawがパフォーマンス良いかも
 * SCSIコントローラはVirtIO SCSI single
 * Machineはq35

BIOSのタイプはOSのインストール後に変更することはできないと思っていい。GPU-Passthroughするつもりなら必ずインストール前に「OVFM」を選択すること。

![スクリーンショット 2020-04-13 12.48.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65278/19676fa0-486d-ded8-7f7d-95767ffd593a.png)
ディスクは「SATA」を選ぶ

ディスクの設定についてはCrystalDiskMarkによるベンチを取ってみました。
[ProxmoxVE 設定とベンチマーク結果まとめ（ディスク編）](https://qiita.com/disksystem/items/b18412a69fdbf2fac0fc)

![スクリーンショット 2020-04-13 12.52.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65278/eaa4424c-8f31-6903-aa44-6b92a5fcef73.png)
CPUは「host」を選ぶ。host以外だとNvidiaのドライバのインストールでコケることがある。

![スクリーンショット 2020-04-13 12.53.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65278/417c3e6b-7623-1830-05bf-3f193d376a67.png)
BallooningDeviceはオフに。若干パフォーマンスが良いらしい。

![スクリーンショット 2020-04-13 12.54.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65278/bad5c456-5dd7-2835-4646-722092c6c9d0.png)
ネットワークは「VirtIO」を選ぶ

![スクリーンショット 2020-04-13 12.56.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65278/d2f75dc6-4c64-4557-f7e6-96c360882584.png)
オプションをいじる。UseTabletPointerをオフにするとパフォーマンスがよい。

![スクリーンショット 2020-04-13 12.57.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65278/0d16c3e9-1a3b-2067-ed8a-34e519e0aec6.png)
ハードウェアをいくつか追加。まずRNG。

![スクリーンショット 2020-04-13 12.59.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65278/6a4f78c7-785e-e97b-addf-772ec56578d0.png)
そしてグラフィックボード。

![スクリーンショット 2020-04-13 13.00.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65278/90e5a856-fc72-edfe-3950-5bb05ad30dd4.png)
一覧からGTXを選ぶ

![スクリーンショット 2020-04-13 13.01.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65278/03f11ae0-8e45-94de-1e8a-31575bdebf7a.png)
全部チェックで

![スクリーンショット 2020-04-13 13.02.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65278/3f8852ec-5041-d6d5-8ee2-824c7e75ac08.png)
Windowsのディスクとは別にドライバーを入れるためのCDを追加する
ISOイメージは以下から入手。安定版の「Stable」か最新版の「Latest」かはお好みで。

https://github.com/virtio-win/virtio-win-pkg-scripts/blob/master/README.md

![スクリーンショット 2020-04-13 13.03.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65278/75cdb03c-1cc8-ce01-7eea-566fdeb4ad39.png)
USBも全部つける。キーボードやマウスが使えるようになる。

![スクリーンショット 2020-04-13 13.06.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65278/b27fdb54-a713-0389-fe12-cff1b5b92a9f.png)

## PART7 設定ファイルの追加修正
GUIで設定した内容はテキストで保存されている。GUI上にはない項目もあるので直接編集する
`/etc/pve/qemu-server/111.conf` 「111」はマシン番号。 

行追加。必須なのは「host」「hv_vendor_id=whatever」「kvm=off」。これがないとNvidiaのドライバのインストールでコケることがある。その他はCPUのパフォーマンスに関するものなので、お好みで。

```bash
args: -cpu 'host,+kvm_pv_unhalt,+kvm_pv_eoi,hv_vendor_id=whatever,kvm=off,hv_relaxed,hv_spinlocks=0x1fff,hv_vapic,hv_time,smep=off,-pcid,-spec-ctrl,-ssbd,-hv-evmcs,+aes'
```

Ryzen等AMDのCPUでハイパースレッディングを有効化するには、設定の追加（topoext=onの追記）が必要。topoextはデフォルトではoff

```bash
args: -cpu 'host,topoext=on,〜〜
```

マシンタイプを変更。q35でとりあえずPassthroughできるが、音声の再生時に酷い音割れとパフォーマンス低下が見られたためこの設定を行った。管理画面からは「pc-q35-3.1」は選べないので、設定ファイルに直接記入する。

```bash
#before
machine: q35
#after
machine: pc-q35-3.1
```

※Proxmox VE 6.4からGUIで設定できるようになりました

![machine-type.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65278/16fa4240-99f0-1ed8-0781-f09558cb1b2c.png)


PCIの項目の後ろに追加（Ryzenの場合のみ）

```bash
#before
hostpci0: 26:00,pcie=1,x-vga=1
#after
hostpci0: 26:00,pcie=1,x-vga=1,romfile=Z_1050Ti_Mod.rom
```

あとはVMを起動すればモニタが映って、いつものキーボードとマウスが使えるはず。「Please any Key boot from DVD...」が表示されるのですかさずEnterを押す。以後Windowsのインストール作業。
CDドライブに入ったVirtIOのドライバのインストーラ実行やNvidiaのドライバのインストールもあるが、話が長くなったので一旦ここでおしまい。