自宅サーバーのセットアップ (5. KVMの設定 後編)

はじめに

前回の記事で構築した仮想マシン上で GTX1650 が利用できるように PCI パススルーの設定を行います。

PCI パススルーの設定

参考: 技術メモ 【Linux Mint】【QEMU/KVM】KVM による仮想化 Win10 への GPU パススルー設定

PCI パススルーが使用できるか確認

PCI パススルーを使用するためには、仮想化拡張機能、IOMMU 機能を有効にする必要があります。
そのために grub の設定ファイルを編集して IOMMU を有効にします。

参考: 付録 G PCI パススルーを有効にするためのホストの設定

$ vim /etc/default/grub

変更前

GRUB_CMDLINE_LINUX_DEFAULT="splash nouveau.modeset=0"

変更後

GRUB_CMDLINE_LINUX_DEFAULT="splash nouveau.modeset=0 amd_iommu=on"

変更が完了したら変更を適用して再起動します。

$ sudo update-grub
$ sudo reboot

有効になっているか確認します。

$ sudo dmesg | grep -e DMAR -e IOMMU
[ 0.501436] AMD-Vi: IOMMU performance counters supported
[ 0.507651] AMD-Vi: Found IOMMU at 0000:00:00.2 cap 0x40
[ 0.509975] perf/amd_iommu: Detected AMD IOMMU #0 (2 banks, 4 counters/bank).
[ 0.591185] AMD IOMMUv2 driver by Joerg Roedel <[email protected]>

これで IOMMU が有効になりました。

IOMMU グループの確認

PCI デバイスが IOMMU グループにどのようにマッピングされたか確認します。

今回は GTX1650 を PCI パススルーしたいので、GTX1650 の IOMMU グループを確認します。

参考: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

ます確認用のスクリプトを作成します。

$ touch iommu.sh
$ vim iommu.sh

以下のようなコードを追加して保存します。

#!/bin/bash
shopt -s nullglob
for d in /sys/kernel/iommu_groups/*/devices/*; do
n=${d#*/iommu_groups/*}; n=${n%%/*}
printf 'IOMMU Group %s ' "$n"
lspci -nns "${d##*/}"
done;

スクリプトを実行します。

$ sudo bash iommu.sh

私の環境では GTX1650 の IOMMU グループや pci の情報は以下のようになっていました。

IOMMU Group 14 0a:00.0 VGA compatible controller [0300]: NVIDIA Corporation TU107 [10de:1f82] (rev a1)
IOMMU Group 14 0a:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:10fa] (rev a1)

IOMMU グループは仮想マシンにパススルーすることができる最小単位です。
この場合、VGA compatible controllerAudio device は両方セットでパススルーする必要があります。

vfio の有効化

使用しているドライバーを確認します。

現状は以前インストールした nvidia を使用しているのでこれを vfio-pci に変更します。

$ lspci -nnk -d 10de:1f82
0a:00.0 VGA compatible controller [0300]: NVIDIA Corporation TU107 [10de:1f82] (rev a1)
Subsystem: Gigabyte Technology Co., Ltd TU107 [1458:400e]
Kernel driver in use: nvidia
Kernel modules: nouveau, nvidia_drm, nvidia
$ lspci -nnk -d 10de:10fa
0a:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:10fa] (rev a1)
Subsystem: Gigabyte Technology Co., Ltd Device [1458:400e]
Kernel driver in use: snd_hda_intel
Kernel modules: snd_hda_intel

起動時に読み込むカーネルモジュールを指定します。
自分の場合は /etc/modules を以下のように変更しました。

# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.
vfio
vfio_iommu_type1
vfio_pci
vfio_virqfd
kvm
kvm_amd

再起動後、カーネルモジュールが読み込まれているかどうか確認します。

$ sudo lsmod | grep vfio
vfio_pci 53248 0
vfio_virqfd 16384 1 vfio_pci
irqbypass 16384 2 vfio_pci,kvm
vfio_iommu_type1 28672 0
vfio 32768 2 vfio_iommu_type1,vfio_pci

また、設定を書くことで vfio-pci を読み込むように設定します。
こちらのページを参考にしました。

$ sudo vim /etc/modprobe.d/vfio.conf

以下のように記載します。
ベンダーとデバイス ID は lspciコマンドで確認できます。

options vfio-pci ids=10de:1f82,10de:10fa

もう一度再起動して、ドライバが変更されたことを確認します。

$ lspci -nnk -d 10de:1f82
0a:00.0 VGA compatible controller [0300]: NVIDIA Corporation TU107 [10de:1f82] (rev a1)
Subsystem: Gigabyte Technology Co., Ltd TU107 [1458:400e]
Kernel driver in use: vfio-pci
Kernel modules: nouveau, nvidia_drm, nvidia
$ lspci -nnk -d 10de:10fa
0a:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:10fa] (rev a1)
Subsystem: Gigabyte Technology Co., Ltd Device [1458:400e]
Kernel driver in use: vfio-pci
Kernel modules: snd_hda_intel

dmesg からもエラーがないか確認します。

$ sudo dmesg | grep -i vfio
[ 4.153858] VFIO - User Level meta-driver version: 0.3
[ 4.159432] vfio-pci 0000:0a:00.0: vgaarb: changed VGA decodes: olddecodes=io+mem,decodes=io+mem:owns=io+mem
[ 4.176161] vfio_pci: add [10de:1f82[ffffffff:ffffffff]] class 0x000000/00000000
[ 4.196370] vfio_pci: add [10de:10fa[ffffffff:ffffffff]] class 0x000000/00000000

このようなエラーが表示された場合、こちらのサイトを参考にして grub からカーネルオプションを設定することで解決しました。 具体的には GRUB_CMDLINE_LINUX_DEFAULTpci=nommconf を追加することで解決しました。

参考

$ sudo dmesg | grep -i vfio
[ 4.551625] VFIO - User Level meta-driver version: 0.3
[ 4.556199] vfio-pci 0000:0a:00.0: vgaarb: changed VGA decodes: olddecodes=io+mem,decodes=io+mem:owns=io+mem
[ 4.580700] vfio_pci: add [10de:1f82[ffffffff:ffffffff]] class 0x000000/00000000
[ 4.581367] vfio-pci 0000:0a:00.0: PCIe Bus Error: severity=Corrected, type=Data Link Layer, (Transmitter ID)
[ 4.584308] vfio-pci 0000:0a:00.0: device [10de:1f82] error status/mask=0000b000/0000a000
[ 4.584309] vfio-pci 0000:0a:00.0: [12] Timeout
[ 4.604363] vfio_pci: add [10de:10fa[ffffffff:ffffffff]] class 0x000000/00000000

仮想マシンに PCI デバイスを追加

Add Hardware から対応する PCI デバイスを追加します。
VGA compatible controllerAudio device を両方セットで追加しました。

image

あとは、仮想ディスプレイと仮想グラフィックデバイスを削除して起動します。
NVIDIA のドライバーをインストールしなくても成功すれば、HDMI 経由で実際のディスプレイ上に画面が表示されるはずです。

image

デバイスマネージャーからも正常に認識されています。

image

以上で PCI パススルーの設定は完了です。

追記 KVM 上の Windows を半年使ってみた感想

よかったこと

  • CPU やメモリ,GPU のリソース管理が楽(自己満)
  • Windows を複数起動できる(一台しか使わない)

不便なこと

  • USB デバイスを追加接続するのに再起動が必要(起動時にあらかじめ接続しておく必要がある)
  • 手持ちの USB デバイスが一部動作しない
  • vanguard がインストールできないので Riot 社のゲームはプレイできない
  • 特定のゲームが起動中にたまにフリーズする
  • サーバーとゲーム用 PC の 2 台構成より電気代のコストが削減できると思っていたが、linux 上で GPU に常に電源供給されてるので場合によっては電気代が高い