最近才發現有 VGA Passthrough 這種東西 有原生系統 95% 以上的效能 看起來超棒的

Update 2016/05/28

昨天試用 Ubuntu Kernel 4.4.0-22 發現 Kernel 的問題已經修好了喔 直接使用就可以了 不需要上額外的 patch

目前遊戲機是用 iMac 因為系統關係 所以常玩的遊戲也都是找有 Mac 版的才玩 唯一會裝 PlayOnMac/PlayOnLinux 來跑的遊戲只有三國志系列(超愛) 不過用 wine 玩實在是不穩 即使有 PlayOnMac 也常常會遇到很多問題 剛好看到這個 VGA Passthrough 所以頭腦一熱 就跑去買了張二手的 AMD/ATI 7970 來試試

我的電腦配置如下

  • CPU : Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz
  • 主機:Shuttle XPC-SH67H3 (改裝 500W Power)
  • RAM:16GB (8GB + 4GB + 4GB)(有一隻 8GB 的壞了 沒買新的)
  • GPU 1:Intel HD 4000 Integrated Graphics
  • GPU 2: AMD/ATI Radeon HD7970 [1002:6798]
  • OS:Ubuntu 15.10

事實上 這是我的 server 我打算在上面跑 Windows VM + VGA Passthrough 然後用 Steam 的 In-Home Streaming 導到我的 iMac 上來玩 一切都想的很美好 直到我發現我買到是一張卡王 (囧

因為這張是卡王 而且我的 server 沒有螢幕(家裡只有一台十幾年前的 15 吋 LCD 最高解析度是 640x480) (用這個螢幕接 Windows 10 沒有畫面 只有一個視窗圖示 orz) 所以為了搞定這個 VGA Passthrough 足足花了我三週的時間 因為沒有螢幕的關係 所以只能靠 virt-manager 來幫忙安裝/管理 VM 下面我就來講講我的心路歷程

  1. 不要用 virt-manager 來產生新的 Windows VM 用 virt-manager 產生的 vm machine 是 piix4 但是剛好我們需要的特定裝置(ioh3420) 只支援 q35 所以得要自己寫 xml 來建立 vm

  2. 直接在 virsh xml 裡面寫 qemu arg 會發生下面的錯誤 試過所有可能的解法都沒效 所以就只能直接寫在 XML 裡

    1
    vfio: error opening /dev/vfio/1: Permission denied

  3. 需要將 vga 掛在 ioh3420 bus 之下 不然安裝 AMD/ATI driver 時 系統會當機 然後就沒辦法再開機 會一直自動重開

  4. 因為 2. 跟 3. 所以必須把 ioh3420 寫在 XML 裡 而 libvirt 對 ioh3420 的支援 一直到 libvirt 1.2.19 才有 [libvirt] [PATCHv3 07/13] qemu: support new pci controller model "pcie-root-port"

  5. Kernel 4.1.6 之後的 VGA Passthrough 支援是壞的 而且一直到 4.4 都還沒修好 (超囧 原本我是直接抓 Ubuntu mainline kernel 來用 v4.1.6-unstable 但是我 docker 需要的 aufs 並沒有進 mainline kernel 所以我就從 aufs 的 git tree clone 了一份 kernel 下來 aufs.sourceforge.net

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    sudo apt-get build-dep linux-image-4.2.0-16-generic
    git clone git://github.com/sfjro/aufs4-linux.git
    cd aufs4-linux
    git checkout -b aufs4.1 origin/aufs4.1
    wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.1.6-unstable/0001-base-packaging.patch
    wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.1.6-unstable/0002-debian-changelog.patch
    wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.1.6-unstable/0003-configs-based-on-Ubuntu-4.1.0-3.3.patch
    git am 000{1,2,3}*
    fakeroot debian/rules clean binary-generic binary-headers # 這邊會問 aufs 要不要開 除了 debug 的之外 我都選 Y
    sudo dpkg -i ../linux-headers-4.1.6-040106_4.1.6-040106.201508170230_all.deb ../linux-headers-4.1.6-040106-generic_4.1.6-040106.201508170230_amd64.deb ../linux-image-4.1.6-040106-generic_4.1.6-040106.201508170230_amd64.deb ../linux-image-extra-4.1.6-040106-generic_4.1.6-040106.201508170230_amd64.deb

基本上要注意的就是這些啦 VGA Passthrough 的進展非常快 很多文章都已經沒有用了 像是需要找出顯卡的 vfio group 啦 然後 unbind/bind 一大堆有的沒有的 或是要上 i915 ACS patch 什麼的 也通通不需要了 virt-manager 幫我們把 vfio 那些工作做完了 i915 現在不需要了 ACS 則是可有可無

接下來就是比較正式的操作步驟了

  1. 準備好 vm 的磁碟跟 windows 安裝 iso 我把檔案通通放在 /var/vm 底下

    產生磁碟空間 200GB
    1
    dd if=/dev/zero of=/var/vm/win10.img bs=1M seek=200000 count=0
    Windows 使用的 virtio driver
    1
    2
    cd /var/vm/iso
    wget https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso

  2. 使用 virt-manager 建立一個 Windows VM 記得將 virtio-win.iso 掛上 然後開始安裝系統 磁碟選擇 raw/scsi virtio 網路選擇 bridge 模式及 virtio 安裝時找不到 driver 就從 virtio-win.iso 裡找 得要給到最終目錄才找的到 driver

  3. 裝完 Windows 之後 可以先把會用的遠端登入系統裝一裝(我使用 splashtop 當然直接使用 RDP 也可以)

  4. 把 VM 刪掉 保留 win10.img 然後用下面的 XML 重新產生一個 VM

    建議先備份一份 img 弄壞掉就不用重裝
    1
    rsync -v --sparse --progress /var/vm/win10.img /var/vm/win10.img.orig
    重點在於 q35 pcie-root-port 及最後面那兩個 hostdev 的 source address 我的 lspci 長這樣 所以 bus:slot:function 就是 01:00:0 跟 01:00:1
    lspci
    1
    2
    01:00.0 VGA compatible controller: Advanced Micro Devices, Inc. [AMD/ATI] Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X]
    01:00.1 Audio device: Advanced Micro Devices, Inc. [AMD/ATI] Tahiti XT HDMI Audio [Radeon HD 7970 Series]
    win10.xml
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    <?xml version="1.0" encoding="UTF-8"?>
    <domain xmlns:qemu="http://libvirt.org/schemas/domain/qemu/1.0" type="kvm">
    <name>win10</name>
    <memory unit='KiB'>8388608</memory>
    <currentMemory unit='KiB'>8388608</currentMemory>
    <memoryBacking>
    <locked />
    </memoryBacking>
    <vcpu placement='static' current='4'>8</vcpu>
    <os>
    <type arch='x86_64' machine='q35'>hvm</type>
    <loader type='rom'>OVMF.fd</loader>
    <bootmenu enable='yes'/>
    </os>
    <features>
    <acpi/>
    <apic/>
    <hyperv>
    <relaxed state='on'/>
    <vapic state='on'/>
    <spinlocks state='on' retries='8191'/>
    </hyperv>
    <kvm>
    <hidden state='on'/>
    </kvm>
    </features>
    <cpu mode='host-passthrough'>
    <topology sockets='1' cores='4' threads='2'/>
    </cpu>
    <clock offset='localtime'>
    <timer name="hypervclock" present="yes" />
    </clock>
    <on_poweroff>destroy</on_poweroff>
    <on_reboot>restart</on_reboot>
    <on_crash>restart</on_crash>
    <pm>
    <suspend-to-mem enabled='no'/>
    <suspend-to-disk enabled='no'/>
    </pm>
    <devices>
    <emulator>/usr/bin/qemu-system-x86_64</emulator>
    <sound model='ich6'/>
    <controller type='sata' index='0'/>
    <controller type='pci' index='0' model='pcie-root'/>
    <controller type='pci' index='1' model='dmi-to-pci-bridge'/>
    <controller type='pci' index='2' model='pci-bridge'/>
    <controller type='pci' index='3' model='pcie-root-port' multifunction='on'/>
    <controller type='scsi' index='0' model='virtio-scsi'/>
    <disk type='file' device='disk'>
    <driver name='qemu' type='raw'/>
    <source file='/var/vm/win10.img'/>
    <target dev='sda' bus='scsi'/>
    <address type='drive' controller='0' bus='0' target='0' unit='0'/>
    </disk>
    <hostdev mode='subsystem' type='pci' managed='yes'>
    <driver name='vfio'/>
    <source>
    <address domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
    </source>
    <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0' multifunction='on'/>
    </hostdev>
    <hostdev mode='subsystem' type='pci' managed='yes'>
    <driver name='vfio'/>
    <source>
    <address domain='0x0000' bus='0x01' slot='0x00' function='0x1'/>
    </source>
    <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x1'/>
    </hostdev>
    </devices>
    </domain>
    建立 VM
    1
    virsh define win10.xml

  5. 使用 virt-manager 把需要的 device 加上去(重點是 bridge mode 的 network)(另外好像不能加 cdrom 我沒特別去查哪裡有問題)

  6. Host 的 kernel command line 要打開 iommu(其實我加了很多參數 因為之前一直撞牆 所以不是很確定那一大堆是不是都需要 不過我猜前三個是必要的)

    cat /etc/default/grub | grep GRUB_CMDLINE_LINUX_DEFAULT
    1
    GRUB_CMDLINE_LINUX_DEFAULT="intel_iommu=on i915.enable_hd_vgaarb=1 kvm.ignore_msrs=1 kvm.allow_unsafe_assigned_interrupts=1 vfio_iommu_type1.allow_unsafe_interrupts=1 kvm_intel.emulate_invalid_guest_state=0 nohz=off nomdmonddf nomdmonisw"

  7. Host blacklist radeon

    blacklist radeon
    1
    sudo sh -c "echo \"blacklist radeon\"" >> /etc/modprobe.d/blacklist.conf

  8. 標記 vfio device (這個適用於所有的 AMD/ATI 及 nVidia 顯卡)

    cat /etc/modprobe.d/vfio.conf
    1
    options vfio-pci ids=1002:ffffffff:ffffffff:ffffffff:00030000:ffff00ff,1002:ffffffff:ffffffff:ffffffff:00040300:ffffffff,10de:ffffffff:ffffffff:ffffffff:00030000:ffff00ff,10de:ffffffff:ffffffff:ffffffff:00040300:ffffffff

  9. 因為使用 uEFI 所以得要裝 ovmf

    1
    sudo apt-get install ovmf

  10. 重開 host machine 然後啟動 vm 然後安裝顯卡 driver 差不多就這樣了

幾個步驟可以驗證一下

  1. lspci -vvnn 看一下顯卡使用的 driver 是不是 vfio-pci

    1
    Kernel driver in use: vfio-pci

  2. 用 top 看一下 CPU loading 發生問題時 不是 100% 不然就是 0.x%

  3. 如果 vm 順利執行 可以用 arp -a 從 vm 的 mac 找到他的 ip

最後是我用 Performance Test 跑的測試結果 可以看出 跟一般 native 的 AMD/ATI 7970 並沒有太大差異 輸比較多的就是 CPU 了

參考文件

  1. KVM VGA-Passthrough using the new vfio-vga support in kernel =>3.9
  2. The vfio-users Archives
  3. VFIO tips and tricks
  4. Does my graphics card ROM support EFI?