Proxmox VE 7.1 NVIDIA GPU Passthrough まとめ

# Proxmox VE 7.1 NVIDIA GPU Passthrough まとめ

Proxmox VE での PCI Passthrough についての日本語の情報が少なかったため、本記事にてまとめる。(2022/2/23 時点)
詳細は公式ドキュメント[^1]を参照。

## 目的

Proxmox VE にて GPU パススルーの機能を試し、実用性（性能、安定性）を検証する。

GPU パススルー機能を検証するゲストOSは、以下を試す。

* Ubuntu Server 20.04
* Ubuntu Desktop 20.04
* Windows 10 Pro 21H2 x64

## ハードウェア環境

* ホストマシン: Dell Precision Tower 5810
  * CPU: Xeon E5-2600 v4 系
  * RAM: RDIMM DDR4 64GB
  * PCI デバイス (スロット番号については[^2]を参照):
     * スロット1: (なし)
     * スロット2: **NVIDIA Quadro M2000 (GPU)**
     * スロット3: (なし)
     * スロット4: (Intel NIC)
     * スロット5: (なし)
     * スロット6: (なし)

## ソフトウェア環境

* ホストマシン: Proxmox VE 7.1-10
    * ブートローダー: systemd-boot

## 作業手順

1. 仮想化支援機構の有効化
    ホストマシンのBIOSにて、VT-x, VT-d を有効にする。(VT-d は PCI Passthrough で必須)
    * 参考 (自分の環境の場合):
        * ✓ Enable Intel Virtualization Technology

            ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191898/8f4f1ce3-9ff4-5d7b-cdea-b007c9c9a5f8.jpeg)
        * ✓ Enable VT for Direct I/O

            ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191898/ff1c6374-c855-8ecc-5575-86611b0eba70.jpeg)
2. Proxmox VE のインストール
    1. ホストマシンに Proxmox VE をインストールする。(インストール方法は割愛)
3. IOMMU の有効化 (<https://pve.proxmox.com/wiki/Pci_passthrough#Enable_the_IOMMU>)
    1. ブートローダーのカーネルコマンド引数を変更する。
        * Proxmox VE のブート方法 (GRUB/systemd-boot) および CPU のメーカー (Intel/AMD) で作業内容が異なる点に注意
        * GRUB の場合: `/etc/default/grub` を編集
            1. `GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on"` を追記
            2. `update-grub` 実行
                * GRUB ではなく systemd-boot でブートしている場合、以下のような警告文が出る。(自分はこれで気づいた)

                    ```
                    W: This system is booted via proxmox-boot-tool:
                    W: Executing 'update-grub' directly does not update the correct configs!
                    W: Running: 'proxmox-boot-tool refresh'
                    ```
        * systemd-boot の場合: `/etc/kernel/cmdline` を編集
            1. **最初の行の末尾に** `quiet intel_iommu=on` 追記
            2. (`iommu=pt` も指定するとパフォーマンスが向上するらしいので、これも追記した。)
            3. `proxmox-boot-tool refresh` 実行
    2. マシンを再起動する。
    3. IOMMU が有効になったかどうかは、以下のコマンドを実行して確認する。(<https://pve.proxmox.com/wiki/Pci_passthrough#Verify_IOMMU_is_enabled>)
        * `dmesg | grep -e DMAR -e IOMMU`
            * `IOMMU enabled` のような出力があれば有効になっている。
        * 参考 (自分の環境の場合):

            ```
            # dmesg | grep -e DMAR -e IOMMU
            [    0.011971] ACPI: DMAR 0x00000000C9FE48E8 0000DC (v01 DELL   CBX3     00000001 INTL 20091013)
            [    0.012000] ACPI: Reserving DMAR table memory at [mem 0xc9fe48e8-0xc9fe49c3]
            [    0.144018] DMAR: IOMMU enabled
            [    0.373806] DMAR: Host address width 46
            [    0.373807] DMAR: DRHD base: 0x000000f7ffd000 flags: 0x0
            [    0.373813] DMAR: dmar0: reg_base_addr f7ffd000 ver 1:0 cap 8d2008c10ef0466 ecap f0205b
            [    0.373816] DMAR: DRHD base: 0x000000f7ffc000 flags: 0x1
            [    0.373820] DMAR: dmar1: reg_base_addr f7ffc000 ver 1:0 cap 8d2078c106f0466 ecap f020df
            [    0.373822] DMAR: RMRR base: 0x000000cafe5000 end: 0x000000caff3fff
            [    0.373824] DMAR: ATSR flags: 0x0
            [    0.373825] DMAR: RHSA base: 0x000000f7ffc000 proximity domain: 0x0
            [    0.373827] DMAR-IR: IOAPIC id 1 under DRHD base  0xf7ffc000 IOMMU 1
            [    0.373829] DMAR-IR: IOAPIC id 2 under DRHD base  0xf7ffc000 IOMMU 1
            [    0.373830] DMAR-IR: HPET id 0 under DRHD base 0xf7ffc000
            [    0.373831] DMAR-IR: x2apic is disabled because BIOS sets x2apic opt out bit.
            [    0.373832] DMAR-IR: Use 'intremap=no_x2apic_optout' to override the BIOS setting.
            [    0.374339] DMAR-IR: Enabled IRQ remapping in xapic mode
            [    0.694077] DMAR: No SATC found
            [    0.694079] DMAR: IOMMU feature sc_support inconsistent
            [    0.694081] DMAR: IOMMU feature dev_iotlb_support inconsistent
            [    0.694082] DMAR: dmar0: Using Queued invalidation
            [    0.694085] DMAR: dmar1: Using Queued invalidation
            [    0.697219] DMAR: Intel(R) Virtualization Technology for Directed I/O
            ```
4. 仮想マシンの作成 (<https://pve.proxmox.com/wiki/Pci_passthrough#GPU_OVMF_PCI_Passthrough_.28recommended.29>)
    * デフォルト設定から変更が必要な箇所:
        * System > Machine
            * Default (i440fx) → q35 (PCIe パススルーで必須)
        * System > Bios
            * Default (SeaBIOS) → OVMF(UEFI)
        * System > EFI Storage
            * (空白) → 任意
        * 他はデフォルト値のままで動作した。

        ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191898/64f4d2f2-30f0-2b1a-235c-18f7739a42b7.png)
    * 仮想マシンが作成されたら、ハードウェアの追加設定 (左側のメニューの "Hardware")
        * Display: none (none)

            ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191898/ae7025ab-bd72-ca8e-ecff-6322063102bd.png)
        * PCI Device:  (Add ボタンをクリック)
            * Device: パススルー対象の GPU を探して選択 (ブルダウンボックス)
                * 参考 (自分の環境の場合):
                    * 0000:03:00.0
            * All Functions: ✓
            * Primary GPU: ✓
            * (Advancedを ✓ して)
                * PCI-Express: ✓ (チェックしないとゲストOS起動時にハングアップ)

            ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191898/6f5724d0-b2c3-af77-050b-c489c994c1a0.png)
        * USB Device: (Add ボタンをクリック)
            * ● Use USB Port
                * Choose Port: 有効なUSBキーボードを選択
            * PCI パススルーを有効にすると(?) noVNC が使えなくなり、ゲストOSのインストールができない。そのため、キーボードを直接VMに接続する。

            ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191898/148cb928-29e7-bb1e-50a6-2ec592a73dde.png)
5. VM > Secure Boot の無効化 (:warning: Windows11 ゲストの場合はこの工程をスキップ)
    1. GPU から直接モニターに映像が出力されるため、パススルー指定した GPU にモニターを接続する。
    2. VM を起動する。
    3. **仮想マシンのブート中に F2? Esc? Del? を連打し、BIOS? に入る。Attempt Secure Boot の項目の✓を外す。**(自分の環境ではデフォルトで ON になっていた。)
        * Secure Boot が ON になっていると、NVIDIAドライバーインストール後のゲストOS再起動時にハングアップする。

        ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191898/3be4fd51-f866-97a1-9a86-a8bdfd9014f9.jpeg)
    4. F10 を押して Save
    5. Reset を選択してVMを再起動 (一番下の項目)

        ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191898/1285c2d9-3a17-3182-45d0-9490b29c4b57.jpeg)
6. VM > ゲストOSのインストール (省略)
7. VM > NVIDIAドライバのインストール
    1. NVIDIAドライバのインストール
        * ubuntu ゲスト:
            * `apt install nvidia-driver-XXX --no-install-recommends` ... XXX の部分はドライババージョン
        * windows ゲスト:
            * NVIDIA のホームページからインストーラーをダウンロードして実行
        * 参考 (自分の環境の場合):
            * ドライババージョンは 510 を選択。
        * 注: 手順 5. の Secure Boot が無効化されていないと、NVIDIAドライバのインストール中に以下の画面が表示される。

            ![vm-ubuntu-focal-gpu (ubuntu) 2022_02_22 17_35_32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191898/6368ce08-5d9c-fce7-3c45-d380995f70bf.png)
    2. VM を再起動して GPU が認識しているか確認。
        * ubuntu ゲスト:

            ```
            $ nvidia-smi
            Thu Feb 24 00:59:11 2022
            +-----------------------------------------------------------------------------+
            | NVIDIA-SMI 510.47.03    Driver Version: 510.47.03    CUDA Version: 11.6     |
            |-------------------------------+----------------------+----------------------+
            | GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
            | Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
            |                               |                      |               MIG M. |
            |===============================+======================+======================|
            |   0  Quadro M2000        Off  | 00000000:01:00.0 Off |                  N/A |
            | 56%   42C    P0    22W /  75W |      0MiB /  4096MiB |      0%      Default |
            |                               |                      |                  N/A |
            +-------------------------------+----------------------+----------------------+

            +-----------------------------------------------------------------------------+
            | Processes:                                                                  |
            |  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
            |        ID   ID                                                   Usage      |
            |=============================================================================|
            |  No running processes found                                                 |
            +-----------------------------------------------------------------------------+
            ```

            ↑ 物理マシンで GPU を使用する際と同じ出力が得られる。

            ```
            $ lspci | grep NVIDIA
            01:00.0 VGA compatible controller: NVIDIA Corporation GM206GL [Quadro M2000] (rev a1)
            01:00.1 Audio device: NVIDIA Corporation GM206 High Definition Audio Controller (rev a1)
            ```

            ↑ バス番号 "01" にパススルーされた GPU が接続されていることがわかる。

## 動作テストとベンチマーク

1. TensorFlow benchmarks [^3] (Ubuntu Server 20.04)
    * TensorFlow の動作確認 (TensorFlow については省略)
    * ベンチマークの様子:

        ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191898/17b53307-4130-2764-1053-d8034518ca6c.gif)

        (左) nvidia-smi / (右) tf_cnn_benchmarks.py
    * **GPU パススルーを使用すると、VM 上でも物理マシンと同じ感覚で GPU を扱えることがわかる。**

2. Passmark ベンチマーク [^4] (Windows 10 Pro 21H2 x64)
    * ベンチマーク結果 (3D Graphics Mark に注目)
        * 3D Graphics Mark 総合スコアは 4117 だった。

            ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191898/1f6aa92c-d93f-cf91-4dd4-23d665a815f5.png)
    * web上で公開されているベンチマーク結果 [^5]

        ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191898/203939a1-ab67-39a9-992f-86532b602f66.png)

        ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191898/bbb5c9e2-dc17-8a56-314f-c8d5ca52e543.png)
    * **GPU パススルーを使用すると、VM でも物理マシンと変わらない性能が得られることがわかる。**

## まとめ

本実験にて、以下の知見が得られた。

* Proxmox VE では、PCI デバイスを簡単に VM にパススルーすることができる。
    * ただし、ホストマシンに VT-d 機能が必要
* Ubuntu ゲストOS および Windows ゲストOS にて性能評価を行い、
パススルーされた GPU が、物理マシンで使用する場合と比較して、同等の性能を発揮できることを確認した。
* PCI パススルーの欠点 (VT-d の欠点): 同じハードウェアを複数の VM 間で共有できない。

### 補足: VT-d (Virtualization Technology Direct I/O) について

[今知っておきたい仮想化時代のCPU技術](https://thinkit.co.jp/story/2010/07/29/1671) などに詳しい説明がある。
> VT-dはI/O側に実装されている技術である。

> I/O処理を高速化するには、従来はVMやゲストに特殊なドライバが必要だったが、このハードウエア・リソースのリマッピングをハードウエアで行うのがVT-dである。

### おまけ (Processor の仮想化/仮想化なしの比較)

* Proxmox VE では、物理 CPU とは異なる CPU をエミュレートできる。
    * VM の Processors > Type を変更することで、エミュレーションする CPU が変更できる (デフォルトでは仮想?CPU `kvm64`)
        * `host` を選択すると、物理 CPU がそのまま? VM で使用される。
    * Processor Type を `kvm64` と `host` のそれぞれで Passmark ベンチマークを実行する。(Windows 10 Pro 21H2 x64)
* 結果
    * Processor Type: `kvm64` の場合

        ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191898/c47f440c-e63c-df77-11d5-345cd6fc2eee.png)
    * Processor Type: `host` の場合

        ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191898/9f26c569-bef4-64d5-9a30-54411ddb170c.png)
    * `Extended Instructions (SSE)` と `Encryption` の項目について、`kvm64` と `host` の間で大きな差が確認できる。
    * `kvm64` ではいくつかの SIMD 拡張命令 (SSE4, AVX 等) や `AES-NI` が無効のようである。
        * 前者は `Extended Instructions (SSE)` テストに、後者は `Encryption` テストに対する影響が大きいと思われる。
        * `kvm64` での `/proc/cpuinfo`:

            ```
            $ cat /proc/cpuinfo
            processor       : 0
            vendor_id       : GenuineIntel
            cpu family      : 15
            model           : 6
            model name      : Common KVM processor
            stepping        : 1
            microcode       : 0x1
            cpu MHz         : 2294.686
            cache size      : 16384 KB
            physical id     : 0
            siblings        : 4
            core id         : 0
            cpu cores       : 4
            apicid          : 0
            initial apicid  : 0
            fpu             : yes
            fpu_exception   : yes
            cpuid level     : 13
            wp              : yes
            flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx lm constant_tsc nopl xtopology cpuid tsc_known_freq pni cx16 x2apic hypervisor lahf_lm cpuid_fault pti
            bugs            : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihit
            bogomips        : 4589.37
            clflush size    : 64
            cache_alignment : 128
            address sizes   : 40 bits physical, 48 bits virtual
            power management:
            ```

        * `host` での `/proc/cpuinfo`:

            ```
            $ cat /proc/cpuinfo
            processor       : 0
            vendor_id       : GenuineIntel
            cpu family      : 6
            model           : 79
            model name      : Intel(R) Xeon(R) CPU E5-2697R v4 @ 2.30GHz
            stepping        : 1
            microcode       : 0xb000038
            cpu MHz         : 2294.625
            cache size      : 16384 KB
            physical id     : 0
            siblings        : 4
            core id         : 0
            cpu cores       : 4
            apicid          : 0
            initial apicid  : 0
            fpu             : yes
            fpu_exception   : yes
            cpuid level     : 20
            wp              : yes
            flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon rep_good nopl xtopology cpuid pni pclmulqdq vmx ssse3 fma cx16 pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm rdseed adx smap xsaveopt arat umip md_clear arch_capabilities
            bugs            : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs taa
            bogomips        : 4589.25
            clflush size    : 64
            cache_alignment : 64
            address sizes   : 46 bits physical, 48 bits virtual
            power management:
            ```

[^1]: https://pve.proxmox.com/wiki/Pci_passthrough
[^2]: https://www.dell.com/support/manuals/ja-jp/precision-t5810-workstation/precision_t5810_om_pub-v2/%E6%8A%80%E8%A1%93%E4%BB%95%E6%A7%98?guid=guid-cb1a5aa6-1e70-44b9-b690-59507a3a9f31
[^3]: https://github.com/tensorflow/benchmarks
[^4]: https://www.passmark.com/products/performancetest/index.php
[^5]: https://www.videocardbenchmark.net/gpu.php?gpu=Quadro+M2000&id=3512