Intel GVT-g

出自 Arch Linux 中文维基

Intel GVT-g 是一項為Intel GPU (Broadwell之後的架構)提供中介設備直通的技術,可以在不妨礙宿主機正常使用GPU的同時,將GPU虛擬化出多個性能接近原生硬體的虛GPU供多個虛擬機使用。這對於硬體加速虛擬機中的Windows圖形是很有用的,對於沒有獨立顯卡可用於全設備直通的筆記本來說尤其如此。(英偉達和AMD的GPU也有類似的功能,但只給Quadro、Radeon Pro這類「專業版」GPU提供。)

Intel還有另一名字相似的技術叫做GVT-d,即使用vfio-pci驅動進行全設備直通。若使用GVT-d,宿主機不能在虛擬化後使用GPU。

準備步驟[編輯 | 編輯原始碼]

Intel GVT-g在Intel Broadwell (5代) 到 Comet Lake (10代)上是受支持的,但在Ice Lake (10代移動處理器)、Rocket Lake (11th台式機處理器)缺少i915驅動的支持。參見Intel Support Post 以及 Github Issue 了解具體細節。

有關Intel顯卡對虛擬化支持可以參考官網https://www.intel.cn/content/www/cn/zh/support/articles/000093216/graphics.html?wapkw=gvt-g

目前Ice Lake只支持 GVT-d。 對於Xe Architecture (Gen12)GPU,則需要SR-IOV特性。參考QEMU/Guest graphics acceleration#SR-IOV了解具體細節。

首先,你需要創建一個虛GPU,然後將它分配給某個虛擬機。客戶機(guest)會將虛GPU視為「正常」的GPU,因此直接安裝原生驅動即可,不需要使用特殊驅動(但要保證驅動不過時)。

步驟如下:

  • 使用Linux 4.16(或更新) 和 QEMU 2.12(或更新)
  • intel_iommu=on 添加到 kernel parameters以啟用IOMMU
  • 啟用 內核模塊: kvmgt, vfio-iommu-type1mdev
  • 設置 i915 模塊啟動參數 enable_gvt=1 以啟用GPU虛擬化
  • i915.enable_guc=0 添加到 kernel parameters, 參見 Intel graphics#Enable GuC / HuC firmware loading的警告
  • 檢索GPU的PCI地址和區域號(下文分別記為$GVT_PCI$GVT_DOM, as it resides in /sys/bus/pci/devices。 可以用 lspci -D -nn檢視含有VGA compatible controller: Intel Corporation HD Graphics ...的那一行,左邊的地址即為$GVT_PCI,大概形同0000:00:02.0
  • 為虛GPU生成一個GUID(下文記為$GVT_GUID),之後將用於創建和分配虛GPU。虛GPU與GUID一一對應,如果要創建多個虛GPU,那麼它們的GUID必須不同。可以使用uuidgen生成隨機的GUID。
警告: 一些用戶報告在 5.12 或更新版本的內核上,會導致宿主機和客戶機崩潰,參見 https://github.com/intel/gvt-linux/issues/188

創建虛GPU[編輯 | 編輯原始碼]

正確設置上文的內核參數和模塊參數,重啟後即可創建虛GPU。

虛GPU的類型有多種,區別在於分配給他們的資源量。用以下命令查看可用類型(另外,在對應類型的目錄下cat description可以查看此類型的細節):

# ls /sys/devices/pci${GVT_DOM}/$GVT_PCI/mdev_supported_types
i915-GVTg_V5_1  # Video memory: <512MB, 2048MB>, resolution: up to 1920x1200
i915-GVTg_V5_2  # Video memory: <256MB, 1024MB>, resolution: up to 1920x1200
i915-GVTg_V5_4  # Video memory: <128MB, 512MB>, resolution: up to 1920x1200
i915-GVTg_V5_8  # Video memory: <64MB, 384MB>, resolution: up to 1024x768
注意: If the directory is presented but contains nothing, you may try to increase the AGP aperture size in your computer firmware.

選擇一個類型(下文記為$GVT_TYPE),用下面的命令創建指定類型的虛GPU:

# echo "$GVT_GUID" > "/sys/devices/pci${GVT_DOM}/$GVT_PCI/mdev_supported_types/$GVT_TYPE/create"

要創建多個虛GPU,則修改GUID,重複上面的指令多次。創建好的虛GPU將出現在 /sys/bus/pci/devices/$GVT_PCI/中。

要刪除已經創建的虛GPU,執行下面的指令

# echo 1 > /sys/bus/pci/devices/$GVT_PCI/$GVT_GUID/remove

libvirt qemu 鉤子[編輯 | 編輯原始碼]

libvirt qemu 鉤子可在對應虛擬機啟動的時自動創建虛GPU、關閉時自動刪除虛GPU。按照實際情況替換下面變量的值(DOMAIN name是對應虛擬機的domain)。

/etc/libvirt/hooks/qemu
#!/bin/sh
GVT_PCI=<GVT_PCI>
GVT_GUID=<GVT_GUID>
MDEV_TYPE=<GVT_TYPE>
DOMAIN=<DOMAIN name>
if [ $# -ge 3 ]; then
    if [ "$1" = "$DOMAIN" ] && [ "$2" = "prepare" ] && [ "$3" = "begin" ]; then
        echo "$GVT_GUID" > "/sys/bus/pci/devices/$GVT_PCI/mdev_supported_types/$MDEV_TYPE/create"
    elif [ "$1" = "$DOMAIN" ] && [ "$2" = "release" ] && [ "$3" = "end" ]; then
        echo 1 > /sys/bus/pci/devices/$GVT_PCI/$GVT_GUID/remove
    fi
fi

記得給予此文件executable權限,變量的值記得使用引號,例如GVT_PCI='0000:00:02.0'.

注意:
  • 如果使用libvirt用戶會話, 此腳本需要使用權限提升命令,如 pkexec(1)或者sudo.
  • domain的XML將通過標準輸入傳入此腳本。可以使用xmllint和XPath表達式從標準輸入提取 GVT_GUID,如:
    GVT_GUID="$(xmllint --xpath 'string(/domain/devices/hostdev[@type="mdev"][@display="on"]/source/address/@uuid)' -)"

使用systemd service[編輯 | 編輯原始碼]

可以使用systemd service在啟動時自動創建虛GPU。優點如下:

  • 不依賴於 libvirt
  • 可不使用權限提升,因為你可以讓systemd直接以root身份執行腳本
  • 雖然不是按需創建虛GPU,但虛GPU閒置的時候似乎不會影響宿主機的GPU性能

創建一個 bash 腳本,內容即#準備步驟中提到的步驟。給予其可執行權限。確保腳本有修改權限,因為它將在啟動的時候被root用於運行。 接下來 創建 systemd 服務,使之啟動時執行此腳本,並設置下列屬性:

After=graphical.target
Type=oneshot
User=root

分配虛GPU[編輯 | 編輯原始碼]

如果以普通用戶身份運行 qemulibvirtd ,可能會報告 /dev/vfio/number 不可寫,那麼需要給予此用戶寫對應目錄的權限(使用 chmod 或者 setfacl 修改權限)

QEMU CLI[編輯 | 編輯原始碼]

要創建一個帶有虛GPU的虛擬機,將此參數添加到QEMU命令中:

-device vfio-pci,sysfsdev=/sys/bus/mdev/devices/$GVT_GUID
注意: KVM 需要用 -enable-kvm啟用

libvirt[編輯 | 編輯原始碼]

把這一設備添加對應虛擬機XML的 devices 元素中

$ virsh edit vmname
...
    <hostdev mode='subsystem' type='mdev' managed='no' model='vfio-pci' display='off'>
      <source>
        <address uuid=''GVT_GUID''/>
      </source>
    </hostdev>
...

GVT_GUID 替換成你虛GPU的UUID。

獲取虛GPU的顯示內容[編輯 | 編輯原始碼]

有幾種不同的方式可以從虛GPU中獲取顯示內容。

使用 DMA-BUF 顯示[編輯 | 編輯原始碼]

警告: 根據這一 issue, 此方法不適用於使用(未修改的)OVMF的UEFI虛擬機。使用基於BIOS的虛擬機(比如SeaBISO) 或者參考下文的補丁或者處理方法。

QEMU CLI[編輯 | 編輯原始碼]

display=on,x-igd-opregion=on 添加到 -device vfio-pci 參數的後面,如:

-device vfio-pci,sysfsdev=/sys/bus/mdev/devices/$GVT_GUID,display=on,x-igd-opregion=on

libvirt[編輯 | 編輯原始碼]

首先,修改虛擬機的XML,以便於之後使用QEMU相關的元素。修改:

$ virsh edit vmname
<domain type='kvm'>

$ virsh edit vmname
<domain xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0' type='kvm'>

然後把本配置文件添加到<domain>元素的的末尾,比如,把這段文本插入到</domain>標籤的上面:

$ virsh edit vmname
...
  <qemu:override>
    <qemu:device alias="hostdev0">
      <qemu:frontend>
        ...
        <qemu:property name="x-igd-opregion" type="bool" value="true"/>
      </qemu:frontend>
    </qemu:device>
  </qemu:override>
...

使用帶UEFI/OVMF的DMA-BUF[編輯 | 編輯原始碼]

如上文所說,DMA-BUF顯示不能與使用(未修改過的)OVMF的UEFI客戶機一同工作,原因在於它不會通過QEMU的非標準fw_cfg接口暴露出所需的ACPI OpRegion。參見this OVMF bug

根據 GitHub上的討論,OVMF bug報告提出了幾種解決方案。可以

在此選擇最後一種方法。

注意: if the link and the archive go down, the OpROM can be extracted from the kernel patch by hand.

下載 vbios_gvt_uefi.rom 並將其置與某處。(本例中為/)。

libvirt[編輯 | 編輯原始碼]

然後編輯虛擬機的XML定義,把下面這段配置添加到先前創建的qemu:commandline元素中。

$ virsh edit vmname
...
    <qemu:arg value='-set'/>
    <qemu:arg value='device.hostdev0.romfile=/vbios_gvt_uefi.rom'/>
...

啟用RAMFB顯示 (可選)[編輯 | 編輯原始碼]

本操作是上文的DMA-BUF配置的補充,用於顯示虛擬機Intel驅動載入前的顯示畫面(如POST,固件界面,客戶機初始化)

QEMU CLI[編輯 | 編輯原始碼]

ramfb=on,driver=vfio-pci-nohotplug添加到-device vfio-pci參數的末尾,如:

-device vfio-pci,sysfsdev=/sys/bus/mdev/devices/$GVT_GUID,display=on,x-igd-opregion=on,ramfb=on,driver=vfio-pci-nohotplug

libvirt[編輯 | 編輯原始碼]

首先,參照這一小節修改虛擬機的XML定義。

然後把下面的配置添加到<domain>元素中,即插入到</domain>標籤前面:

$ virsh edit vmname
...
  <qemu:override>
    <qemu:device alias="hostdev0">
      <qemu:frontend>
        ...
        <qemu:property name="driver" type="string" value="vfio-pci-nohotplug"/>
        <qemu:property name="ramfb" type="bool" value="true"/>
      </qemu:frontend>
    </qemu:device>
  </qemu:override>
...

顯示虛GPU輸出[編輯 | 編輯原始碼]

由於spice-gtk相關的問題,不同EGL實現的SPICE客戶端的配置方法不同。

使用QEMU GTK顯示器輸出[編輯 | 編輯原始碼]

在性能較弱的CPU上,本方法的刷新率較高、顯示延遲較小,至少對於Windows虛擬機來說是如此。並且相比於Looking Glass,本方法的CPU負載較小。代價是得放棄一些SPICE GPU特性,如:

  • 共享剪貼板
  • 自動 USB 重定向 (需要在啟動虛擬機前手動分配USB)
  • 滑鼠指針自由進出虛擬機
  • 與virt-manager的顯示器輸入整合 (會在另一個窗口裡顯示)

只有在虛擬機加載了正確的Intel GPU驅動後才會開始輸入顯示內容(通常是登錄界面)。這意味著:

  • 最好預先安裝好正確的Intel GPU驅動。安裝前,可以暫時使用另一種虛擬顯示器適配器與Intel vGPU一起工作(如 -vga std 或者 -std-vga(針對libvirt),安裝後移除std視頻適配器.
  • 無法看到系統的啟動過程。如果系統在登錄前崩潰,只能暫用另一種虛擬顯示器適配器以排查錯誤。
  • 要進入BIOS,得啟用RAMFB顯示.
提示:在QEMU GTK中, Ctrl+Alt+G 可以捕獲或釋放滑鼠指針,Ctrl+Alt+F可以在全屏模式和窗口模式間切換。

QEMU CLI[編輯 | 編輯原始碼]

-display gtk,gl=on添加到命令後面。QEMU VGA適配器可以通過添加-vga none禁用。或者也可以同時用兩個虛擬顯示屏,只不過連接到QEMU VGA適配器的那個是空白的。

libvirt[編輯 | 編輯原始碼]

  • 確保上面添加的<hostdev>設備把display屬性設為'off'
  • 確保已經把xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'添加到domain(步驟使用DMA-BUF顯示器)。
  • 移除所有<graphics><video>設備。

QEMU GTK顯示窗口需要你指定運行OpenGL的顯示輸出。如果使用筆記本電腦,則先把所有外接顯示器斷開,確保筆記本電腦屏幕是唯一的顯示器。使用這行的命令獲取顯示器編號echo $DISPLAY,形如:0。獲取編號後即可重連外接顯示器。把剛才獲取的編號插入到下面env name='DISPLAY'的這行中。

  • 添加下面的QEMU命令行參數
$ virsh edit vmname
...
  <qemu:commandline>
    <qemu:arg value="-display"/>
    <qemu:arg value="gtk,gl=on,zoom-to-fit=off"/>
    <qemu:env name="DISPLAY" value=":0"/>
  </qemu:commandline>
  <qemu:override>
    <qemu:device alias="hostdev0">
      <qemu:frontend>
        <qemu:property name="display" type="string" value="on"/>
        ...
      </qemu:frontend>
    </qemu:device>
  </qemu:override>
...

縮放[編輯 | 編輯原始碼]

窗口模式中,-display gtk,gl=on,zoom-to-fit=off使GTK顯示窗口大小和虛擬機的屏幕的解析度一致,保證像素縱橫比是1:1。不啟用這個參數(或預設)會使虛擬機的顯示匹配窗口的大小,不能保持像素縱橫比為1:1,這種縮放不太好看。

在全屏模式中,縮放自動啟用。在修改客戶機的解析度的時,只有降低解析度會更新縮放,如果虛擬機的解析度調得比宿主機要高,則需手動退出重進全屏模式。

GTK顯示產生的CPU負載[編輯 | 編輯原始碼]

gl=es可能可以降低CPU負載,但2021年11月過後。gl=on似乎更有優勢。

使用MESA EGL實現的SPICE輸出[編輯 | 編輯原始碼]

QEMU CLI[編輯 | 編輯原始碼]

-display spice-app,gl=on添加到命令行。須安裝virt-viewer

libvirt[編輯 | 編輯原始碼]

  • 確保上面添加的<hostdev>設備的display屬性設為'on'
  • 移除所有的{ic|<graphics>}}和<video>設備。
  • 添加下面的設備:
$ virsh edit vmname
...
    <graphics type='spice'>
      <listen type='none'/>
      <gl enable='yes'/>
    </graphics>
    <video>
      <model type='none'/>
    </video>
...

gl標籤中的可選屬性rendernode可以用於指定渲染器,如:

<gl enable='yes' rendernode='/dev/dri/by-path/pci-0000:00:02.0-render'/>

使用NVIDIA EGL實現的SPICE或VNC輸出[編輯 | 編輯原始碼]

libvirt[編輯 | 編輯原始碼]

  1. 確保上面添加的<hostdev> 設備的display屬性設為'on'
  2. 移除所有<graphics> and <video>設備。
  3. 添加下面的設備:
$ virsh edit vmname
...
    <graphics type='spice' autoport='yes'>
      <listen type='address'/>
    </graphics>
    <graphics type='egl-headless'/>
    <video>
      <model type='none'/>
    </video>
...

要使用VNC,則須將<graphics type='spice'>type屬性改為'vnc'

<graphics type='egl-headless'>標籤中的<gl>可選屬性可以用來指定渲染器(由於前面提到的bug,不要把這一可選屬性添加到spice圖形中)。例如:

<graphics type='egl-headless'>
  <gl rendernode='/dev/dri/by-path/pci-0000:00:02.0-render'/>
</graphics>

禁用所有輸出[編輯 | 編輯原始碼]

如果禁用了所有輸出,那麼只能使用RDP、VNC、Looking Glass等軟體獲取顯示內容。參見PCI passthrough via OVMF#Using Looking Glass to stream guest screen to the host

QEMU CLI[編輯 | 編輯原始碼]

-device vfio-pci參數中,將ramfb=on改為display=off。添加-vga none以禁用QEMU VGA適配器。

libvirt[編輯 | 編輯原始碼]

要確保沒有加載任何模擬的GPU,可以編輯虛擬機的配置:

  1. 移除所有<graphics>設備。
  2. <video>設備的類型都改為'none'
  3. 確保上面添加的<hostdev>設備的display屬性設為'off'

故障排查[編輯 | 編輯原始碼]

mdev_supported_types目錄缺失[編輯 | 編輯原始碼]

如果你按步驟操作,在添加i915.enable_gvt=1內核參數後仍然找不到/sys/bus/pci/devices/0000:02:00.0/mdev_supported_types目錄,請再次檢查kvmgt模塊是否已載入。

然後檢查你的硬體是否支持,檢視dmesg的輸出裡是否有這條信息:

# dmesg | grep -i gvt 
[    4.227468] [drm] Unsupported device. GVT-g is disabled

如果都沒有問題,檢查上游是否有支持計劃。如,對於"Coffee Lake" (CFL)平台的支持可以參見https://github.com/intel/gvt-linux/issues/53

Windows提示內存損壞錯誤(bad memory error)[編輯 | 編輯原始碼]

如果Windows虛擬機由於內存損壞錯誤卡死,檢視宿主機dmesg的輸出以獲取更多細節。如果內核日誌中有類似內存溢出上限(rlimit memory exceeded)的內容,則可能需要增加Linux分配給QEMU的內存上限。若用戶在kvm組中,把下面的內容添加到/etc/security/limits.d/42-intel-gvtg.conf然後重啟。

# qemu kvm, need high memlock to allocate memory for vga-passthrough
@kvm - memlock 8388608

同時使用Intel GVT-G和PRIME render offload[編輯 | 編輯原始碼]

在宿主機上同時使用Intel GVT-G和NVIDIA的PRIME render offload會導致客戶機出現一些問題。建議使用bbswitch關閉獨立顯卡或者與Bumblebeenvidia-xrun或者optimus-manager一同使用。

無顯示器[編輯 | 編輯原始碼]

如果虛擬機使用RAMFB顯示器並且沒有輸出任何顯示內容,嘗試增加以下選項到<qemu:commandline>標籤:

$ virsh edit vmname
...
  <qemu:commandline>
    <qemu:arg value="-set"/>
    <qemu:arg value="device.hostdev0.display=on"/>
  </qemu:commandline>
...

花屏[編輯 | 編輯原始碼]

如果滑鼠移入後虛擬機屏幕花屏,下面的方法可能有效

首先,按照#libvirt 2修改虛擬機的XML定義。

然後,把下面的內容插入到</domain>標籤的上面。如果<qemu:commandline>標籤已經存在,就直接插入到其中去:

$ virsh edit vmname
...
  <qemu:commandline>
    <qemu:env name="MESA_LOADER_DRIVER_OVERRIDE" value="i965"/>
  </qemu:commandline>
...

宿主機在掛起時卡死[編輯 | 編輯原始碼]

創建GVT-g虛GPU後,宿主機可能在掛起時卡死。參見github以追蹤此bug。

一個可行的解決方法是,在掛起前將GVT-g虛GPU移除,喚醒後才重新創建。你可以安裝gvtg_vgpu-gitAUR自動化這個過程。

修改虛GPU的顯示解析度[編輯 | 編輯原始碼]

虛GPU默認使用其支持的最大解析度。無論虛擬機設置了多大的解析度,所顯示的內容都會被縮放到虛GPU的解析度,造成顯示效果不佳。

要真正改變顯示解析度,將下面的內容添加到XML的<qemu:commandline>元素中:

$ virsh edit vmname
...
    <qemu:arg value='-set'/>
    <qemu:arg value='device.hostdev0.xres=1440'/>
    <qemu:arg value='-set'/>
    <qemu:arg value='device.hostdev0.yres=900'/>
...

參見[編輯 | 編輯原始碼]