夜明け前の最も暗いとき

技術的なやったことをメモするブログ

KVM/QEMUの仮想化Windows環境でGTX1070をパススルーする

いままで使っていたWindowsマシンをLinux環境上に移行します。WindowsではGPUを使っていたのでパススルーをして仮想環境上でも使えるようにします。ホストのLinux機はDebianを使用します。

以下に機材の構成を示します。

Core i7-6700 (Skylake)
ASUSTeK Intel H170
MSI GTX 1070 GAMING X 8G

Intel CPUでパススルーを使うためにはVT-d (Virtualization Technology for Directed I/O )に対応している必要があります。

まずは、Debianの公式サイトからインストーラーのisoイメージをダウンロードします。

DHCP環境であればisoサイズの小さいnetinstが良いでしょう。

USBメモリに書き込むにはUniversal USB Installerやrufusを使うと簡単にできます。

VT-dは初期設定で無効になっていることがあるので、BIOSから有効にします。また、ホスト側でCPUのHD Graphicsを使うのであればiGPUを有効にします。

BIOSの設定が終わったらUSBデバイスからブートし、Debianをインストールします。今回はインストール時にLVMをセットアップしました。インストールが終わったのち再起動しますが、Geforceのドライバが上手く動かないことがあります。その時はGRUBのOS選択画面で起動パラメータを以下のように変更します。

quiet 
↓
quiet modprobe.blacklist=nouveau

GUIが上がってきたら先程の起動パラメータが恒久的に適用されるよう変更します。加えて、パススルーを使うためintel_iommu=onを追記しIOMMUを有効化します。

[jinglan@host-debian]$ cat /etc/default/grub
# If you change this file, run 'update-grub' afterwards to update
# /boot/grub/grub.cfg.
# For full documentation of the options in this file, see:
#   info -f grub -n 'Simple configuration'

GRUB_DEFAULT=0
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="quiet modprobe.blacklist=nouveau intel_iommu=on"
GRUB_CMDLINE_LINUX=""
...

[jinglan@host-debian]$
[jinglan@host-debian]$
[jinglan@host-debian]$ sudo update-grub
Generating grub configuration file ...
Found background image: /usr/share/images/desktop-base/desktop-grub.png
Found linux image: /boot/vmlinuz-4.9.0-7-amd64
Found initrd image: /boot/initrd.img-4.9.0-7-amd64
done
[jinglan@host-debian]$ 

次に、仮想化に必要なソフトウェアをインストールします。

[jinglan@host-debian]$ sudo apt-get install qemu bridge-utils virt-manager ovmf

仮想化管理デーモンであるlibvirtdを起動します。

[jinglan@host-debian]$ sudo systemctl status libvirtd
● libvirtd.service - Virtualization daemon
   Loaded: loaded (/lib/systemd/system/libvirtd.service; enabled; vendor preset: enabled)
   Active: active (running) since Fri 2018-12-21 21:57:45 JST; 1min 41s ago
     Docs: man:libvirtd(8)
           http://libvirt.org
 Main PID: 2571 (libvirtd)
   CGroup: /system.slice/libvirtd.service
           mq2571 /usr/sbin/libvirtd

Dec 21 21:57:45 host-debian systemd[1]: Starting Virtualization daemon...
Dec 21 21:57:45 host-debian systemd[1]: Started Virtualization daemon.
[jinglan@host-debian]$
[jinglan@host-debian]$
[jinglan@host-debian]$ sudo systemctl enable libvirtd
Synchronizing state of libvirtd.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable libvirtd
[jinglan@host-debian]$ 

続いてネットワークを設定します。ゲスト環境がネットワークに接続できるようブリッジを構成します。

[jinglan@host-debian]$ cat /etc/network/interfaces
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

source /etc/network/interfaces.d/*

# The loopback network interface
auto lo
iface lo inet loopback

auto enp4s0
iface enp4s0 inet manual

auto br0
iface br0 inet dhcp
 bridge_ports enp4s0
[jinglan@host-debian]$ 

コンピュータを再起動し、設定が反映されているか確認します。

[jinglan@host-debian]$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: enp4s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master br0 state UP group default qlen 1000
    link/ether 70:8b:cd:9e:b7:0a brd ff:ff:ff:ff:ff:ff
    inet 192.168.11.75/24 brd 192.168.11.255 scope global dynamic enp4s0
       valid_lft 171164sec preferred_lft 171164sec
3: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 70:8b:cd:9e:b7:0a brd ff:ff:ff:ff:ff:ff
    inet 192.168.11.75/24 brd 192.168.11.255 scope global br0
       valid_lft forever preferred_lft forever
    inet6 fe80::728b:cdff:fe9e:b70a/64 scope link 
       valid_lft forever preferred_lft forever
[jinglan@host-debian]$ 

また、IOMMUが有効になっているのか確認します。

[jinglan@host-debian]$ sudo dmesg | grep -e DMAR -e IOMMU
[    0.000000] ACPI: DMAR 0x00000000A2EA18F8 0000A8 (v01 INTEL  SKL      00000001 INTL 00000001)
[    0.000000] DMAR: IOMMU enabled
[    0.052829] DMAR: Host address width 39
[    0.052830] DMAR: DRHD base: 0x000000fed90000 flags: 0x0
[    0.052835] DMAR: dmar0: reg_base_addr fed90000 ver 1:0 cap 1c0000c40660462 ecap 7e3ff0505e
[    0.052836] DMAR: DRHD base: 0x000000fed91000 flags: 0x1
[    0.052838] DMAR: dmar1: reg_base_addr fed91000 ver 1:0 cap d2008c40660462 ecap f050da
[    0.052839] DMAR: RMRR base: 0x000000b3b7f000 end: 0x000000b3b9efff
[    0.052840] DMAR: RMRR base: 0x000000b5800000 end: 0x000000b7ffffff
[    0.052841] DMAR-IR: IOAPIC id 2 under DRHD base  0xfed91000 IOMMU 1
[    0.052842] DMAR-IR: HPET id 0 under DRHD base 0xfed91000
[    0.052842] DMAR-IR: Queued invalidation will be enabled to support x2apic and Intr-remapping.
[    0.054170] DMAR-IR: Enabled IRQ remapping in x2apic mode
[    0.515358] DMAR: No ATSR found
[    0.515677] DMAR: dmar0: Using Queued invalidation
[    0.515680] DMAR: dmar1: Using Queued invalidation
[    0.515864] DMAR: Setting RMRR:
[    0.515925] DMAR: Setting identity map for device 0000:00:02.0 [0xb5800000 - 0xb7ffffff]
[    0.515962] DMAR: Setting identity map for device 0000:00:14.0 [0xb3b7f000 - 0xb3b9efff]
[    0.515966] DMAR: Prepare 0-16MiB unity mapping for LPC
[    0.515999] DMAR: Setting identity map for device 0000:00:1f.0 [0x0 - 0xffffff]
[    0.516008] DMAR: Intel(R) Virtualization Technology for Directed I/O
[    0.540870] AMD IOMMUv2 driver by Joerg Roedel <jroedel@suse.de>
[    0.540870] AMD IOMMUv2 functionality not available on this system

IOMMU enabledとあれば有効になっています。 以下のスクリプトを使ってIOMMUグループを確認します。

[jinglan@host-debian]$ ./check_iommu.sh 
IOMMU Group 0 00:00.0 Host bridge [0600]: Intel Corporation Skylake Host Bridge/DRAM Registers [8086:191f] (rev 07)
IOMMU Group 10 02:00.0 PCI bridge [0604]: ASMedia Technology Inc. ASM1083/1085 PCIe to PCI Bridge [1b21:1080] (rev 04)
IOMMU Group 11 04:00.0 Ethernet controller [0200]: Realtek Semiconductor Co., Ltd. RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller [10ec:8168] (rev 15)
IOMMU Group 12 05:00.0 Non-Volatile memory controller [0108]: Intel Corporation Device [8086:f1a6] (rev 03)
IOMMU Group 1 00:01.0 PCI bridge [0604]: Intel Corporation Skylake PCIe Controller (x16) [8086:1901] (rev 07)
IOMMU Group 1 01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP104 [GeForce GTX 1070] [10de:1b81] (rev a1)
IOMMU Group 1 01:00.1 Audio device [0403]: NVIDIA Corporation GP104 High Definition Audio Controller [10de:10f0] (rev a1)
IOMMU Group 2 00:02.0 VGA compatible controller [0300]: Intel Corporation HD Graphics 530 [8086:1912] (rev 06)
IOMMU Group 3 00:14.0 USB controller [0c03]: Intel Corporation Sunrise Point-H USB 3.0 xHCI Controller [8086:a12f] (rev 31)
IOMMU Group 4 00:16.0 Communication controller [0780]: Intel Corporation Sunrise Point-H CSME HECI #1 [8086:a13a] (rev 31)
IOMMU Group 5 00:17.0 RAID bus controller [0104]: Intel Corporation SATA Controller [RAID mode] [8086:2822] (rev 31)
IOMMU Group 6 00:1c.0 PCI bridge [0604]: Intel Corporation Sunrise Point-H PCI Express Root Port #3 [8086:a112] (rev f1)
IOMMU Group 7 00:1c.3 PCI bridge [0604]: Intel Corporation Sunrise Point-H PCI Express Root Port #4 [8086:a113] (rev f1)
IOMMU Group 8 00:1d.0 PCI bridge [0604]: Intel Corporation Sunrise Point-H PCI Express Root Port #9 [8086:a118] (rev f1)
IOMMU Group 9 00:1f.0 ISA bridge [0601]: Intel Corporation Sunrise Point-H LPC Controller [8086:a144] (rev 31)
IOMMU Group 9 00:1f.2 Memory controller [0580]: Intel Corporation Sunrise Point-H PMC [8086:a121] (rev 31)
IOMMU Group 9 00:1f.3 Audio device [0403]: Intel Corporation Sunrise Point-H HD Audio [8086:a170] (rev 31)
IOMMU Group 9 00:1f.4 SMBus [0c05]: Intel Corporation Sunrise Point-H SMBus [8086:a123] (rev 31)

今回はGeForce GTX 1070をパススルーするのでGroup 1を使います。 パススルーするデバイスにVFIOドライバを割り当てるよう設定します。

[jinglan@host-debian]$ cat /etc/modprobe.d/vfio.conf 
options vfio-pci ids=8086:1901,10de:1b81,10de:10f0

[jinglan@host-debian]$ 

起動時にVFIOドライバを読み込むよう設定します。

[jinglan@host-debian]$ cat /etc/modules-load.d/modules.conf 
# /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-pci


[jinglan@host-debian]$ 

再起動後にVFIOドライバが読み込まれていることを確認します。

[jinglan@host-debian]$ sudo dmesg | grep -i vfio
[    2.818521] VFIO - User Level meta-driver version: 0.3
[    2.826175] vfio_pci: add [8086:1901[ffff:ffff]] class 0x000000/00000000
[    2.844215] vfio_pci: add [10de:1b81[ffff:ffff]] class 0x000000/00000000
[    2.864241] vfio_pci: add [10de:10f0[ffff:ffff]] class 0x000000/00000000
[jinglan@host-debian]$ 
[jinglan@host-debian]$ lspci -nnk -d 10de:1b81
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP104 [GeForce GTX 1070] [10de:1b81] (rev a1)
    Subsystem: Micro-Star International Co., Ltd. [MSI] GP104 [GeForce GTX 1070] [1462:3302]
    Kernel driver in use: vfio-pci
    Kernel modules: nouveau
[jinglan@host-debian]$ 

次に、LVMで仮想環境用のストレージを確保します。まずは現在の状態を確認します。

[jinglan@host-debian]$ sudo vgdisplay 
  --- Volume group ---
  VG Name               host-debian-vg
  System ID             
  Format                lvm2
  Metadata Areas        1
  Metadata Sequence No  5
  VG Access             read/write
  VG Status             resizable
  MAX LV                0
  Cur LV                2
  Open LV               2
  Max PV                0
  Cur PV                1
  Act PV                1
  VG Size               446.89 GiB
  PE Size               4.00 MiB
  Total PE              114404
  Alloc PE / Size       27907 / 109.01 GiB
  Free  PE / Size       86497 / 337.88 GiB
  VG UUID               gNTxoB-nh5M-4mTK-V7fa-24uO-oRnJ-RYSewh
   
[jinglan@host-debian]$ 
[jinglan@host-debian]$ sudo lvdisplay 
  --- Logical volume ---
  LV Path                /dev/host-debian-vg/swap_1
  LV Name                swap_1
  VG Name                host-debian-vg
  LV UUID                DdzeNe-qgxE-1GP6-oQt4-g2uY-9pVr-1RdcPt
  LV Write Access        read/write
  LV Creation host, time host-debian, 2018-12-21 20:51:58 +0900
  LV Status              available
  # open                 2
  LV Size                15.88 GiB
  Current LE             4066
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     256
  Block device           253:0
   
  --- Logical volume ---
  LV Path                /dev/host-debian-vg/root
  LV Name                root
  VG Name                host-debian-vg
  LV UUID                Tb8tjV-1Ig6-pzGt-o8Fc-UdqV-R5di-jY8T5e
  LV Write Access        read/write
  LV Creation host, time host-debian, 2018-12-21 20:53:02 +0900
  LV Status              available
  # open                 1
  LV Size                93.13 GiB
  Current LE             23841
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     256
  Block device           253:1
   
[jinglan@host-debian]$ 

今回は80GB分の容量を確保します。

[jinglan@host-debian]$ sudo lvcreate -L80G -n /dev/host-debian-vg/vm-win10-disk
  Logical volume "vm-win10-disk" created.
[jinglan@host-debian]$ sudo lvscan 
  ACTIVE            '/dev/host-debian-vg/swap_1' [15.88 GiB] inherit
  ACTIVE            '/dev/host-debian-vg/root' [93.13 GiB] inherit
  ACTIVE            '/dev/host-debian-vg/vm-win10-disk' [80.00 GiB] inherit
[jinglan@host-debian]$

今回はゲストとしてWindows10 Proをインストールします。WIndowsのISOイメージは公式のMedia Creation Toolを使って取得することができます。

また、Windows用のドライバを以下のサイトからダウンロードしておきます。

virt-installコマンドでゲストを作成します。

[jinglan@host-debian]$ sudo virt-install --name win10main --ram 4096 --vcpus 4 --os-type windows --disk path=/dev/host-debian-vg/vm-win10-disk,bus=sata --network bridge=br0,model=virtio --graphics=vnc,listen=0.0.0.0,port=5901 --cdrom=/home/jinglan/virt/iso/Windows10media.iso --cdrom=/home/jinglan/virt/iso/virtio-win.iso --boot uefi 
WARNING  Graphics requested but DISPLAY is not set. Not running virt-viewer.
WARNING  No console to launch for the guest, defaulting to --wait -1

Starting install...
Creating domain...                                                                                                     |    0 B  00:00:00     
Domain installation still in progress. Waiting for installation to complete.
Domain has shutdown. Continuing.
Domain creation completed.
Restarting guest.

[jinglan@host-debian]$ 

VNCを使ってlocalhost:1へ接続するとUEFIオープンソース実装であるOVMFファームウェアが起動し、Windowsのインストール画面になることが確認できます。GeForceを使う場合はUEFIで起動しないとドライバがエラー43となり使うことができません。

Windowsのインストールが終了したらGPUパススルーのため一部設定を変更します。 ここにあるように、NVIDIAGPUを使うときは以下の設定を追加します。

...
<features>
    <hyperv>
        ...
        <vendor_id state='on' value='0123456789ab'/>
        ...
    </hyperv>
     ...
    <kvm>
        <hidden state='on'/>
    </kvm>
</features>
...

続いて、パススルーするデバイスIDを指定します。

[jinglan@host-debian]$  sudo virsh edit win10main
...
    <hostdev mode='subsystem' type='pci' managed='yes'>
      <source>
        <address domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
      </source>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
    </hostdev>
    <hostdev mode='subsystem' type='pci' managed='yes'>
      <source>
        <address domain='0x0000' bus='0x01' slot='0x00' function='0x1'/>
      </source>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x08' function='0x0'/>
    </hostdev>
...
[jinglan@host-debian]$ 

再度、仮想Windowsを起動するとGPUを認識しているはずです。少々待つとWinidowsが自動的にNVIDIAのドライバをインストールしてくれます。

自分の場合はWindowsのライセンス認証が上手くいかず、MSに電話することになりました。購入していたのはパッケージ版なので仮想環境に移すのはOKらしいのですが、移すたび電話認証が必要になるかもしれないとのこと。