備忘録

備忘録

LinuxでGPUをパススルーして仮想マシンで利用する方法

Ⅰ. はじめに

タイトルの通り「LinuxでGPUをパススルーして仮想マシンで利用する方法」です。

Ⅱ. 環境

ホスト Ubuntu 22.04.3 LTS
Linux Kernel 6.2.0-34-generic
ゲスト Windows 11 Pro 21H2
CPU AMD Ryzen 9 5900X BOX
GPU GeForce RTX 4070 Ti
マザーボード ASUS TUF GAMING B550-PLUS

Ⅲ. 手順

1. BIOS設定を変更する

CPUがAMDの場合以下3つを有効にする
1. IOMMU
2. NX mode
3. SVM mode

CPUがIntelの場合以下2つを有効にする
1. VT-d
2. VT-x
※片方しか存在しない場合は片方だけ有効にする

2. UEFIを利用してUbuntuをインストールする
3. GRUBを編集する
$ sudo vim /etc/default/grub

# CPUがAMD製の場合
GRUB_CMDLINE_LINUX_DEFAULT="amd_iommu=on quiet splash"

# CPUがIntel製の場合
GRUB_CMDLINE_LINUX_DEFAULT="intel_iommu=on quiet splash"
4. GRUBの設定を反映する
sudo update-grub
5. 再起動する
sudo reboot
6. IOMMUが有効になったか確認する
$ sudo dmesg | grep -i -e DMAR -e IOMMU
[    0.425579] iommu: Default domain type: Passthrough (set via kernel command line)
7. 仮想化ソフトウェアをインストールする
sudo apt install qemu-system-x86 libvirt-clients libvirt-daemon-system libvirt-daemon-config-network bridge-utils virt-manager ovmf  
8. ユーザのグループにkvm, libvirt を追加する
sudo usermod -a -G kvm,libvirt $(whoami)

# 確認
sudo groups $(whoami)
9. QEMUの設定を変更する
$ sudo vim /etc/libvirt/qemu.conf

user = "ENTER_YOUR_USERNAME"
group = "ENTER_YOUR_USERNAME"
10. libvirtの設定を変更する
$ sudo vim /etc/libvirt/libvirtd.conf

unix_sock_group = "libvirt"
unix_sock_rw_perms = "0770"

# 以下2行はファイル末尾に追記
log_filters="3:qemu 1:libvirt"
log_outputs="2:file:/var/log/libvirt/libvirtd.log"
11. libvirtdを実行する
sudo systemctl enable libvirtd
sudo systemctl restart libvirtd
12. libvirtネットワークを自動起動する
sudo virsh net-autostart default
13. 仮想化設定確認
virt-host-validate
14. virtioのWindows用ドライバ(ISOファイル)をダウンロードする

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

15. virt-managerを利用して仮想マシンを設定する

名前は必ず「win11」を指定する。※参考


16. Windowsをインストールする


17. nvflashまたはamdvbflashをダウンロードする
18. nvflashまたはamdvbflashを実行する
# デフォルトターゲットを変更する
sudo systemctl set-default multi-user

# ディスプレイマネージャを終了する
sudo systemctl stop gdm3

# Ctrl + Alt + F2でCUIを表示する

# NVFlashを実行する(rmmodコマンド実行を要求された場合は従う)
./nvflash --save vbios.rom

# デフォルトターゲットを変更する
sudo systemctl set-default graphical

# ディスプレイマネージャを起動する
sudo systemctl restart gdm3
19. vbios.romを編集する

0x0 〜 0x93FF まですべて削除する
(55 AA 7E EB 4B 37 34 30 30 E9 4C 19 77 CC 56 49 44 45 4F より前を全て削除する)


20. vbios.romを配置する
sudo mkdir /usr/share/vgabios
sudo cp vbios.rom /usr/share/vgabios/vbios.rom
cd /usr/share/vgabios
sudo chmod -R 644 vbios.rom
sudo chown ENTER_YOUR_USERNAME:ENTER_YOUR_USERNAME vbios.rom
21. スクリプトをcloneする
git clone https://github.com/kagasu/single-gpu-passthrough
cd single-gpu-passthrough
sudo chmod +x install_hooks.sh
sudo ./install_hooks.sh
22. 仮想マシンの設定を変更する


以下4つの要素を削除する

<graphics type="spice" autoport="yes">
  <listen type="address"/>
  <gl enable="no"/>
</graphics>
<audio id="1" type="none"/>
<video>
  <model type="bochs" vram="16384" heads="1" primary="yes"/>
  <address type="pci" domain="0x0000" bus="0x05" slot="0x00" function="0x0"/>
</video>
<channel type="spicevmc">
  <target type="virtio" name="com.redhat.spice.0"/>
  <address type="virtio-serial" controller="0" bus="0" port="1"/>
</channel>
23. パススルーするPCIデバイス(GPU)を指定する

画像の場合は2個指定する(まとめて指定できないので1個ずつ指定する)

24. 仮想マシンの設定を変更する
<rom file='/usr/share/vgabios/vbios.rom'/>


25. パススルーするUSBデバイス(マウス、キーボードなど)を指定する


26. 起動順序を変更する


27. NICのリンク状態をアクティブにする


28. 実行する


29. virtioのドライバをインストールする


実行結果



FAQ

Q. ログの出力先は?

A. 以下ファイルです

/var/log/libvirt/libvirtd.log
/var/log/libvirt/qemu/win11.log
Q. 以下ログが出力されています。問題ありますか?
$ sudo cat /var/log/libvirt/libvirtd.log | grep --text error
error : virNetSocketReadWire:1793 : End of file while reading data: 入力/出力エラーです

A. 問題ないと思われます。
筆者の環境でも同様のエラーが出力されていますが問題なくゲストが動作しています。

Q. プチフリ(Stutter)が発生します。解決方法は?

A. 筆者の環境では以下3点の設定を行う事で軽減しました。
完全に無くすことはできませんでした。まれにプチフリが発生します。競技レベルでのゲームプレイは難しいです。

1. Huge Pagesを設定する
https://qiita.com/disksystem/items/773cbcb4fd9b0bd2e1ee

2. XMLを編集する

<domain>
  <cpu mode='host-passthrough' check='none'>
    <topology sockets='1' cores='n' threads='m'/>
+   <!-- AMD製CPUでSMTを有効にする -->
+   <feature policy='require' name='topoext'/>
+   <!-- ホスト側のCPUキャッシュを仮想CPUにパススルーする -->
+   <cache mode='passthrough'/>
  </cpu>

+  <iothreads>2</iothreads>
+  <cputune>
+    <vcpupin vcpu='0' cpuset='1'/>
+    <vcpupin vcpu='1' cpuset='2'/>
+    <vcpupin vcpu='2' cpuset='3'/>
+    <vcpupin vcpu='3' cpuset='4'/>
+    <vcpupin vcpu='4' cpuset='5'/>
+    <vcpupin vcpu='5' cpuset='6'/>
+    <vcpupin vcpu='6' cpuset='7'/>
+    <vcpupin vcpu='7' cpuset='8'/>
+    <vcpupin vcpu='8' cpuset='9'/>
+    <vcpupin vcpu='9' cpuset='10'/>
+    <vcpupin vcpu='10' cpuset='11'/>
+    <vcpupin vcpu='11' cpuset='12'/>
+    <emulatorpin cpuset='13,14'/>
+    <iothreadpin iothread='1' cpuset='13'/>
+    <iothreadpin iothread='2' cpuset='14'/>
+  </cputune>

+  <memoryBacking>
+    <hugepages/>
+  </memoryBacking>

-  <memballoon model="virtio">
-    <address type="pci" domain="0x0000" bus="0x05" slot="0x00" function="0x0"/>
-  </memballoon>
+  <memballoon model="none"/>
</domain>

3. ゲスト側Windowsで「メモリ内のページのロック」に任意のユーザを設定する
この設定でHuge Pages(WindowsではLarge Pageと呼ぶ)に対応できる