【Linux Mint】【QEMU/KVM】KVMによる仮想化Win10へのGPUパススルー設定

さて、STRIX-GTX1060-DC2O6Gを購入しましたが、現状はゲームで利用しているだけで流石にどうかとおもったので、思い切ってメインPCの構成を変えることにしました。

以下の内容は個人的なメモではあるものの、GPUパススルーをやってみたいという方への助けになればと思ってます。

■参考サイト

Running Windows 10 on Linux using KVM with VGA Passthrough – Heiko's Blog

うらもちゃのブログ: ゲストにPCIパススルー(GPU割当)

CentOS7 KVM で PCIパススルー、USBパススルー - わすれないうちにメモしよう

PCI passthrough via OVMF - ArchWiki

 KVM GPUパススルー設定 - 睡分不足

 

あと、用語はこちらが参考になるかと

KVM-wiki TOP - PukiWiki

 

※2018/10/23追記

一応、Ubuntu 18.04 LTSでも同じ手順でできました。

 

 

1.やりたいこと

有り余るマシンスペックを使うべく、考えた構成は以下のとおりです。

・ホストとなるベースOSはLinuxにしたい

・ホストの上に仮想マシンを構築し、Windows10とそれとは別にLinuxを載せたい

・グラボについては、仮想Win10でのモニタ表示、仮想LinuxGPGPUとして使いたい(グラボのリソースは共有できないので、どちらか一方が起動していたらどちらかは落ちてる状態)

 

正直、今のグラボは性能はいいですが、いかんせんユーザ(私)自身が十分にスペックを活用できているかというとそうではありません。

また、機械学習(AI)の勉強をしてみたいと思っており、そうであればGPUを使わない手はありません。

であれば、グラボを活用できるように構成を見直す、というのがコンセプトです。

なお、グラボに関してですが、GPUパススルー設定をするとホストOSでは一切使用できません。ホストOSで画面描写に利用するのはCore i7-6700Kに搭載されている内蔵GPU機能となります。要するにホストOSはマザボHDMIを使って、ゲストOSのWin10でグラボを使うようになります。

 

2.構成

メインPCとして利用しているマシンの構成は以下のとおりです。

マザボ:ASRock Z170 Extreme4

・メモリ:DDR4 32G

・CPU:Core i7-6700K 

・グラボ:STRIX-GTX1060-DC2O6G

 

3.KVMの利用について

まず、メインPCとして利用するホストOSについてですが、

Linux Mint 18.3 Cinnamon - Linux Mint

とします。Ubuntu系であるというのと、GUIにそれなりに気を配っている点を考慮してです。

仮想化については、KVMを使います。KVM自体はLinuxカーネルに含まれる仮想化基盤となるため、今回はQEMUと組み合わせての利用となります。

普通に仮想基板上で画面描写をした場合、これはもうFF14ベンチマークがローディングで止まるぐらいクソ遅いのですが、ここで重要になるのが仮想基盤を通さず、直接グラボとやり取りできる、いわゆるGPUパススルーです。が、それが今回の大きな難関なわけで、esxi 6.5とProxmox VEでやってみたけど、挫折してQEMU/KVMにしたというのが正直なところ・・・。

 

4.マザボの設定

いくつかのサイトで既に記載されていますが、CPUがIntel製であれば、基本的にVT-dをONに設定できるマザボである必要があります。

幸い、私のマザボのASRockはそこら辺がだいたい全部対応してるとのことで、UFEIでVT-DをONにするだけでいけます。他のメーカは・・・まぁそれぞれによるかと。

基本的に「Intel VT-d」「I/O Virtualization Technology」などの名前の設定項目を有効にすれば問題なし。

 

5.ホストOSのインストール

これは普通にインストールをするだけなので省略。

 

6.GRUBの設定

GPUパススルーをするにはintelの仮想化支援機構である、IOMMUを活性化する必要があります。これはGRUB、要するにLinuxブートローダですが、これを実行するときにIOMMUの指定が必要です。設定するファイルは以下の通り。

$ sudo vi /etc/default/grub

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash "

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_iommu=on"

$ sudo update-grub

※PCを再起動

 

参考サイトによっては、GRUB_CMDLINE_LINUX_DEFAULTで定義したり、GRUB_CMDLINE_LINUXで定義したり・・・。

両方試しましたが、どちらでもよさそうで、以下のように『IOMMU enabled』が確認できればとりあえず問題ない(はず)です。

 $ dmesg | grep -e DMAR -e IOMMU
[ 0.000000] ACPI: DMAR 0x0000000087A48EA0 000070 (v01 INTEL SKL 00000001 INTL 00000001)
[ 0.000000] DMAR: IOMMU enabled

 

7.IOMMUグループが有効であることを確認する

一旦以下のシェルを作り、PCIバイスがどのようにIOMMUグループにマップされているかを確認できます。暫定的に作るのでファイル名は適当。

$cd ~/

$ vi aaa

#!/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 chmod 755 aaa
$ ./aaa

IOMMU Group 0 00:00.0 Host bridge [0600]: Intel Corporation Sky Lake Host Bridge/DRAM Registers [8086:191f] (rev 07)
IOMMU Group 1 00:01.0 PCI bridge [0604]: Intel Corporation vfio-pciSky Lake PCIe Controller (x16) [8086:1901] (rev 07)
IOMMU Group 1 01:00.0 VGA compatible controller [0300]: NVIDIA Corporation Device [10de:1c03] (rev a1)
IOMMU Group 1 01:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:10f1] (rev a1)

・・・

 

今回は赤文字のグラボをパススルーします。なお、以下のコマンドでよって、グラボが現状どのドライバを使っているか詳細に確認できます。(後々重要)

$ lspci -nnk -d 10de:1c03

01:00.0 VGA compatible controller [0300]: NVIDIA Corporation Device [10de:1c03] (rev a1) Subsystem: ASUSTeK Computer Inc. Device [1043:85c5]

Kernel driver in use: nouveau

Kernel modules: nvidiafb, nouveau

$ lspci -nnk -d 10de:10f1

01:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:10f1] (rev a1) Subsystem: ASUSTeK Computer Inc. Device [1043:85c5]

Kernel driver in use: snd_hda_intel

Kernel modules: snd_hda_intel

 

8.各種必要なソフトウェアのインストール

aptでインストールできるlibvirtに関しては、びっくりするほどバージョンが古いため、LinuxMintのGUI上のソフトウェアソースにて、以下のPPAを予め設定しておきます。

ppa:jacob/virtualisation

f:id:engetu21:20180402225313p:plain

 

上記を行ったうえで、qemu, libvirt, ovmf, virt-managerをそれぞれインストールします。

$ sudo apt update
$ sudo apt install qemu-kvm qemu-utils qemu-efi ovmf libvirt-bin libvirt-dev libvirt0 virt-manager

libvirt:仮想化管理用の共通APIを提供する

OVMF:仮想環境で UEFI を使えるようにする

virt-manager:仮想環境をGUI上で管理運用できるようにするオープンソースソフトウェア

 

以下のコマンドで、libvirtとvirtlog(libvirtのログ出力サービス)の実行と自動実行設定を行います。

$ sudo systemctl start libvirtd
$ sudo systemctl enable libvirtd

$ sudo systemctl start virtlogd
$ sudo systemctl enable virtlogd

 

 なお、この際にlibvirtdのstatusを確認し、エラーが発生していた場合は以下の対応をします。

参考:https://kernhack.hatenablog.com/entry/2014/05/12/221554

$ sudo systemctl status libvirtd
● libvirtd.service - Virtualization daemon
Loaded: loaded (/lib/systemd/system/libvirtd.service; enabled; vendor preset: enabled)
Active: active (running) since 木 2018-03-29 18:21:51 JST; 1s ago
Docs: man:libvirtd(8)
http://libvirt.org
Main PID: 4130 (libvirtd)
CGroup: /system.slice/libvirtd.service
└─4130 /usr/sbin/libvirtd

3月 29 18:21:51 XXXX-desktop systemd[1]: Starting Virtualization daemon...
3月 29 18:21:51 XXXX-desktop systemd[1]: Started Virtualization daemon.
3月 29 18:21:51 XXXX-desktop libvirtd[4130]: libvirt version: 2.2.0, package: 0~16.04~ppa0 (Jacob Zimmermann <ppa@jzimm.net> Wed, 07 Sep 2016 22:
3月 29 18:21:51 XXXX-desktop libvirtd[4130]: hostname: XXXXXXX
3月 29 18:21:51 XXXX-desktop libvirtd[4130]: direct firewall backend requested, but /sbin/ebtables is not available: そのようなファイルやディレク
3月 29 18:21:51 XXXX-desktop libvirtd[4130]: 内部エラー: Failed to initialize a valid firewall backendIOMMU Group 1 01:00.0 VGA compatible controller [0300]: NVIDIA Corporation Device [10de:1c03] (rev a1)
IOMMU Group 1 01:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:10f1] (rev a1)

※ebtablesをインストール。

$ sudo apt install ebtables

$ sudo systemctl status libvirtd
● libvirtd.service - Virtualization daemon
Loaded: loaded (/lib/systemd/system/libvirtd.service; enab。led; vendor preset: enabled)
Active: active (running) since 木 2018-03-29 18:26:31 JST; 5s ago
Docs: man:libvirtd(8)
http://libvirt.org
Main PID: 5341 (libvirtd)
CGroup: /system.slice/libvirtd.service
├─5341 /usr/sbin/libvirtd
└─5444 /usr/sbin/libvirtd

3月 29 18:26:31 XXXX-desktop systemd[1]: Starting Virtualizod 755 aaa ation daemon...
3月 29 18:26:31 XXXX-desktop systemd[1]: Started Virtualization daemon.

 

ここで一旦再起動すれば、ソフトウェアに「仮想マシンマネージャー」が追加されているはずです。

 

9.仮想マシンを作る

CPUやメモリサイズに関しては各自の設定したいスペックに合わせて。

Win10の仮想マシンを作るときの最後のダイアログで、「インストールの前に設定をカスタマイズする」にチェックを入れ(これ重要)、完了。

出てきたダイアログの【概要】のファームウェアUEFIを選択します。

f:id:engetu21:20180402232240p:plain

 

f:id:engetu21:20180402232200p:plain

参考にさせていただいたhttp://mmi.hatenablog.com/entry/2018/03/25/010933では仮想マシン作成後に直接設定ファイルを弄ってましたが、これは一度仮想マシンを作るとGUI上は変更できないようで、作る際に上記のようにGUIで操作しておけば問題ない模様。

 

・Windows10インストール

Win10は

Windows 10 のディスク イメージ (ISO ファイル) のダウンロード

からisoファイルのダウンロードしてマウントし、クリーンインストールします。

注意点として、インストール時にVirtIOのドライバがないと、ドライバを読み込めと言われて先に進めなくなります。

そのため、まず

https://docs.fedoraproject.org/en-US/quick-docs/creating-windows-virtual-machines-using-virtio-drivers/index.html

にある「Stable virtio-win iso」をダウンロードします。

virt-managerで『ハードウェアの追加』からCD-ROMデバイスを追加し、先ほど落としたvirtio-win.isoをマウントしておきます。

f:id:engetu21:20180404230347p:plain

もう一度整理すると、Window10インストール時はWin10自体のイメージとVirtIOドライバ用のイメージの両方が必要になります。

10.vendor id偽装

今回使用するのは、NVIDIAGeForceですが、これはグラフィック用に販売されているだけあって、GPUパススルーをするにはロックがかかっていて通常はできないらしいです(NVIDIA TeslaのようなGPGPU専用製品はそうではない模様)。

しかし、今回はパススルーするために以下のように8項で作っておいた仮想マシンの設定を変更することでGPUパススルーができるようになります(※もちろんメーカ非推奨のため注意!)。

なお、仮想マシン名はwin10です。

$ virsh edit win10

<features>
  <acpi/>
  <apic/>
  <hyperv>
    <relaxed state='on'/>
    <vapic state='on'/>
    <spinlocks state='on' retries='8191'/>
    <vendor_id state='on' value='whatever'/> ←追加
  </hyperv>
  <kvm> ←追加
    <hidden state='on'/>←追加
  </kvm>←追加

</features>

 

11.vfio-pciの使用

VIFOについては↓のブログで説明されているのですが、

VFIOによるデバイス操作 - 睡分不足

vfio-pciは要するに擬似ドライバみたいなものらしく(あってる?)、それを使うというのはどういうことかというと、要するにホストOS(私の環境ではLinuxMint)で認識されているグラボを認識から外す、と言った感じになるようです。

現状のグラボのドライバ読み込みは以下のようになっています。

$ lspci -v -d 10de:1c03 | grep Kernel

Kernel driver in use: nouveau

Kernel modules: nvidiafb, nouveau

$ lspci -v -d 10de:10f1 | grep Kernel

Kernel driver in use: snd_hda_intel

Kernel modules: snd_hda_intel

nouveauはNVIDIA グラボ用のフリードライバとのこと。

1項で書いたとおり、ホストOSではグラボは使いません(ホストOSのモニタ表示はCPU内臓のGPU機能を使う)。

グラボはホストOS上の仮想Win10のモニタ表示と仮想LinuxGPGPUとして使いたいので、上記のようにホストOSでグラボが認識できるようNVIDIA用のドライバが読み込まれていると不都合なわけです。

そのため、グラボのドライバをvfio-pciにすることで、ホストOSによるモニタ表示に使えなくします(これをしないとホストOSとゲストOS(win10)でグラボを取り合うことになるので画面は正常に動かなくなる模様)。

 

 ・vfio-pciが使えるか確認

$ modinfo vfio-pci
filename: /lib/modules/4.13.0-37-generic/kernel/drivers/vfio/pci/vfio-pci.ko
description: VFIO PCI - User Level meta-driver
author: Alex Williamson <alex.williamson@redhat.com>
license: GPL v2
version: 0.201です。:00.1
srcversion: 559BDF748E53047F2FF090B
depends: vfio,irqbypass,vfio_virqfd
intree: Y
name: vfio_pci
vermagic: 4.13.0-37-generic SMP mod_unload
parm: ids:Initial PCI IDs to add to the vfio driver, format is "vendor:device[:subvendor[:subdevice[:class[:class_mask]]]]" and multiple comma separated entries can be specified (string)
parm: nointxmask:Disable support for PCI 2.3 style INTx masking. If this resolves problems for specific devices, report lspci -vvvxxx to linux-pci@vger.kernel.org so the device can be fixed automatically via the broken_intx_masking flag. (bool)
parm: disable_vga:Disable VGA resource access through vfio-pci (bool)
parm: disable_idle_d3:Disable using the PCI D3 low power state for idle, unused devices (bool) 

上記のように表示されればまず問題ないはず。というか、Linuxカーネル4.1以上でvfio-pciは組み込まれているはずなので、それ以上であれば問題ないはず。

 

・モジュールリストの更新。

以下を追加することでブート時に読み込むモジュール指定できます。

$ sudo vi /etc/modules
vfio
vfio_iommu_type1
vfio_pci
vfio_virqfd
kvm
kvm_intel

※この段階で一度再起動する

・上記の設定が問題なければ、追加したモジュールはlsmodで見れます。

$ lsmod | grep vfio
vfio_pci 40960 0
vfio_virqfd 16384 1 vfio_pci
irqbypass 16384 2 kvm,vfio_pci
vfio_iommu_type1 24576 0
vfio 28672 2 vfio_iommu_type1,vfio_pci


$ lsmod | grep kvm
kvm_intel 204800 0
kvm 589824 1 kvm_intel
irqbypass 16384 2 kvm,vfio_pci

・/etc/modprobe.d/vfio.confを新規に作ってデバイスを設定

LinuxMintにはvfio.confがはいっていなかったので、新規に作ります。私の環境では以下の通りですが、これは6項に一度記載した グラボのグラフィックとオーディオのPCI ID部分が設定対象になります。

 IOMMU Group 1 01:00.0 VGA compatible controller [0300]: NVIDIA Corporation Device [10de:1c03] (rev a1)
IOMMU Group 1 01:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:10f1] (rev a1)

 

$ sudo vi /etc/modprobe.d/vfio.conf
options vfio-pci ids=10de:1c03,10de:10f1,1b21:1242,1033:0194

後ろの2つのPCI-IDはUSB PCIのパススルー設定ですが、今回は説明を割愛。

・一度再起動した後、以下のコマンドを実行


$ dmesg | grep -i vfio
[ 3.382020] VFIO - User Level meta-driver version: 0.3
[ 3.390893] vfio_pci: add [10de:1c03[ffff:ffff]] class 0x000000/00000000
[ 3.412048] vfio_pci: add [10de:10f1[ffff:ffff]] class 0x000000/00000000
[ 3.412053] vfio_pci: add [1b21:1242[ffff:ffff]] class 0x000000/00000000
[ 3.412054] vfio_pci: add [1033:0194[ffff:ffff]] class 0x000000/00000000

上記のように登録されていればOK。

 

再起動後、ホストOS上でグラボにどのドライバが設定されているかを見ます。

$ lspci -v -d 10de:1c03 | grep Kernel

Kernel driver in use: nouveau

Kernel modules: nvidiafb, nouveau

$ lspci -v -d 10de:10f1 | grep Kernel

Kernel driver in use: vfio-pci

Kernel modules: snd_hda_intel

上記のようにオーディオが変わっているが、グラフィックのほうが変わっていない場合は更に設定を変更します。

方法は3つ。なお、私の環境では①はうまく動かず、②は他のサイトに書いてありましたが未実施、というか③のほうがより明示的な指定なので、③の方式にしました。

ブラックリストに/etc/modprobe.d/blacklist.confに記載を追加

$ sudo  vi /etc/modprobe.d/blacklist.conf
blacklist nouveau
blacklist snd-hda-intel

※保存後、再起動

②/etc/default/grubの設定で、nomodesetを追加

$ sudo vi /etc/default/grub

GRUB_CMDLINE_LINUX_DEFAULT=01:00.1"quiet splash intel_iommu=on"

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_iommu=on nomodeset"

$ sudo update-grub

※再起動

 

③/etc/default/grubの設定で、modprobe.blacklist=nouveauを追加

$ sudo vi /etc/default/grub

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_iommu=on"

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_iommu=on modprobe.blacklist=nouveau"

$ sudo update-grub

※再起動

 再起動後はグラフィックボードからのグラフィック出力がなくなるので、マザボHDMIに予め繋いでおいたほうが良いです。

設定後に以下のようになっていればOK。

$ lspci -v -d 10de:1c03 | grep Kernel

Kernel driver in use: vfio-pci

Kernel modules: nvidiafb, nouveau

 

 12.仮想マシン設定でパススルーするPCIを設定

仮想マシンの「ハードウェアの追加」からPCIを選択します。

f:id:engetu21:20180403002513p:plain

 IOMMU Group 1 01:00.0 VGA compatible controller [0300]: NVIDIA Corporation Device [10de:1c03] (rev a1)
IOMMU Group 1 01:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:10f1] (rev a1)

 6項で確認したデバイスの番号と合うものを選択します。

 

後は仮想マシン設定で「ディスプレイSpice」と「ビデオQXL」を削除します。

仮想マシンを起動して、グラボに繋いだHDMIからモニタに出力されていればGPUパススルーは完了です。

 

13.グラボのGPUパススルーと直使用の比較

FF14ベンチマークをやってみました。

GPUパススルー】

f:id:engetu21:20180403003206p:plain

【直】

f:id:engetu21:20180403003222p:plain

 

やはり性能差は出る模様ですが、ゲームをする分には支障ないレベルみたいです。

おそらくチューニングをやれば性能は上がると思われますが…

 

※追記

設定ごとの性能比較してみたけど、CPU性能とかはそんなに変わらないかもしれない。

engetu21.hatenablog.com

 

GPUパススルーでNVIDIAのCUDA(機械学習)に利用する場合はこちら

engetu21.hatenablog.com

 

※2018/10/23追記

仮想化Win10を使った音声ですが、HDMI経由の音声を使うか、USBをパススルーしてUSBオーディオ機器を使うか、音声PCIをパススルーするかになります。

音声PCIをパススルーすると仮想Win10実行中はホストOSは音声出力できなくなります。

また、HDMI経由の音声というのは、要するにモニタのしょぼいステレオから流れるということになるので、S/PDIF(光デジタルオーディオ)使ってスピーカーと繋げたいんだ!という人は「HDMI 音声 分離」でググって出てくるHDMI音声分離器とか使ってみるといいかと思います。(私はこの方法です)