Intel GVT-g - Arch Linux 中文维基

跳至內容

# Intel GVT-g

出自 Arch Linux 中文维基
翻譯狀態：

- 本文（或部分內容）譯自 Intel GVT-g，最近一次同步於 2022-12-28，若英文版本有所更改，則您可以幫助同步與翻譯更改的內容。
- 您可以在 ArchWiki 的對應頁面找到本文翻譯的原始修訂歷史。
- 本文可能與英文原文存在出入。

相關文章

- PCI passthrough via OVMF
- QEMU
- Intel graphics

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-type1` 和 `mdev`
- 設置 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

如果以普通用戶身份運行 `qemu` 或 `libvirtd` ，可能會報告 `/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>
    ...

#### 解析度配置

由於 SPICE 伺服器直接從透傳的 Intel vGPU 讀取顯示輸出，它會完全鏡像顯卡的原始信號，因此無法通過客戶機代理（Guest Agent）進行動態縮放。為了強制指定自定義顯示解析度並防止查看器窗口縮小或出現黑邊，您需要在 QEMU 覆蓋的設備屬性中明確定義目標寬度和高度：

    $ virsh edit vmname

    ...
    <qemu:override>
      <qemu:device alias="hostdev0">
        <qemu:frontend>
          ...
          <qemu:property name="xres" type="unsigned" value="1920"/>
          <qemu:property name="yres" type="unsigned" value="1080"/>
          ...
        </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報告提出了幾種解決方案。可以

- 為OVMF打補丁 (細節見此) 以添加針對Intel的特殊行為 (最直接，但和上游不一致);
- 為宿主機的內核打補丁 (細節見此) 以自動為虛GPU提供一個可選的ROM;
- 從內核補丁中提取OpROM(來源) 供QEMU重載。不需要打補丁。

在此選擇最後一種方法。

注意： 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:property name="romfile" type="string" value="/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關閉獨立顯卡或者與Bumblebee、nvidia-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'/>
    ...

## 參見

- Official GVT-g Setup Guide
- Official GVT-g DMA-BUF User Guide.
- Running Windows via QEMU/KVM and Intel GVT-g
- Blog post about using GVT[失效連結 2022-09-18 ⓘ]

分類：​

- 虛擬化
- 包含失效連結的頁面

Intel GVT-g
新增話題