1. 目標#
Linux上用QEMU/KVM安裝的Windows虛擬機最缺乏的是3D圖形效能,為此利用Linux提供的VFIO(Virtual Function I/O)核心模組將實體機(Host)的獨立顯示卡綁定,直通(passthrough)給虛擬機(Guest)使用。
這樣虛擬機便能使用3D硬體加速,用於建模彩現、跑AI運算,或是用Looking Glass玩遊戲。
尚能同時使用二個系統作業。
當然獨顯直通給虛擬機時,實體機自然就無法利用獨顯運算,需將虛擬機關機實體機才可重新使用顯示卡。文中會介紹如何在虛擬機關機後,把顯示卡還給實體機存取。
2. 環境#
- Host OS: Arch Linux
- 核心版本:6.6.10
- 桌面環境:KDE Plasma X11
- Libvirt版本:9.0.5
- QEMU版本:8.0.4
- Guest OS: Windows 11 22H2
- 主機板:ASUS K31CDK,UEFI開機,關閉Secure boot
- CPU: Intel i5-7400
- GPU: Intel UHD 630 (內顯)
- GPU: Nvidia GTX-1050Ti (要直通的獨顯)
- RAM:16GB
本文屬雙GPU直通,電腦需同時有內顯+獨顯,準備二個螢幕(或者第二個螢幕用HDMI欺騙器取代,再配合Looking Glass存取)。
GPU直通前接線是這樣,內顯處於閒置狀態。
但在啟用GPU直通(啟用VFIO、改用內顯開機)後,內顯HDMI接主螢幕,獨顯HDMI接副螢幕。
3. 安裝Windows 11虛擬機#
Windows 11虛擬機必須使用UEFI(OVMF)開機,至少需要分配4核心CPU、8GB RAM、64GB硬碟。
SPICE Tools可能會衝突。
安裝好後將虛擬機關機。
4. 啟用IOMMU#
啟用IOMMU將裝置分組。
- 編輯GRUB設定檔
sudo vim /etc/default/grub
- 在
GRUB_CMDLINE_LINUX_DEFAULT
這行後面加入核心參數,啟用Intel CPU的iommu。
GRUB_CMDLINE_LINUX_DEFAULT="intel_iommu=on iommu=pt video=efifb:off"
- 重新產生設定檔,重開機
sudo grub-mkconfig -o /boot/grub/grub.cfg
sudo reboot
- 終端機貼上指令稿查找顯示卡的IOMMU群組:
#!/bin/bash
shopt -s nullglob
for g in /sys/kernel/iommu_groups/*; do
echo "IOMMU Group ${g##*/}:"
for d in $g/devices/*; do
echo -e "\t$(lspci -nns ${d##*/})"
done;
done;
- 應會看到顯示卡的分組,例如我的GTX1050 Ti被分到Group 1:
IOMMU Group 1:
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP107 [GeForce GTX 1050 Ti] [10de:1c82] (rev a1)
01:00.1 Audio device [0403]: NVIDIA Corporation GP107GL High Definition Audio Controller [10de:0fb9] (rev a1)
- 將顯示卡的硬體ID記下來,例如這裡是
10de:1c82
和10de:0fb9
。
5. 將顯示卡綁定vfio-pci#
設定開機就將顯示卡綁給vfio-pci,並禁止載入Nvidia核心模組。
儘管Githu開發者bryansteiner曾經分享用libvirt hook達成「開機後將Nvidia顯示卡直通給虛擬機,關機後Nvidia顯示卡還給實體機」的自動化操作,但是在Arch似乎會造成libvirt卡死,所以我選擇在實體機需要時Nvidia時再手動將Nvidia核心模組載入回來。
- 編輯mkinitcpio.conf
sudo vim /etc/mkinitcpio.conf
- 設定開機載入vfio核心模組
MODULES=(vfio_pci vfio vfio_iommu_type1)
- 再度編輯GRUB:
sudo vim /etc/default/grub
,video=efifb:off
防止系統搶走顯示卡,vfio-pci
加入VFIO顯示卡的硬體ID,以逗號分隔。最後面kvm.ignore_msrs=1
是為了防止KVM錯誤更正導致Windows進入BSOD。
GRUB_CMDLINE_LINUX_DEFAULT="intel_iommu=on iommu=pt video=efifb:off vfio-pci.ids=10de:1c82,10de:0fb9 kvm.ignore_msrs=1 kvm.report_ignored_msrs=0"
- 新增開機禁止載入的核心模組列表
sudo vim /etc/modprobe.d/blacklist.conf
- 填入以下內容,禁止開機載入Nvidia顯示卡的核心模組(也請檢查
modprobe.d
下有無其他檔案包含載入nvidia的選項)
blacklist nvidia
blacklist nouveau
- 刪除Xorg的設定檔
sudo rm /etc/X11/xorg.conf
- 更新initramfs和GRUB
sudo mkinitcpio -p linux
sudo grub-mkconfig -o /boot/grub/grub.cfg
6. 用內顯優先開機#
重開機,按Delete進入BIOS
設定優先以CPU內顯開機。ASUS K31CDK主機板的選項在Advanced → System Agent (SA)Configuration → IGFX
重開機後,用
sudo lspci -nnk | grep NVIDIA
檢查VFIO狀態:
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP107 [GeForce GTX 1050 Ti] [10de:1c82] (rev a1)
01:00.1 Audio device [0403]: NVIDIA Corporation GP107GL High Definition Audio Controller [10de:0fb9] (rev a1)
執行
nvidia-smi
指令,顯示NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver
,確認無法連線到Nvidia顯示卡。再執行
lspci -k | grep -A 2 -i "VGA"
指令,應該會顯示Kernel driver in use: vfio-pci
,這樣就是成功了。
如果以上步驟都無效,你還是可以繼續嘗試把Nvidia顯示卡分配給虛擬機。
7. 將Nvidia顯示卡分配給虛擬機#
開啟Virt Manager,編輯Windows 11虛擬機的硬體。點選左下角新增硬體,選擇PCI主機裝置,加入Nvidia獨顯和音效裝置至虛擬機
建議新增USB主機裝置 → 滑鼠,這樣就沒虛擬機吃不到實體機滑鼠的問題。滑鼠在虛擬機出不來的時候,按Ctrl+ALT讓滑鼠回到實體機。
虛擬機開機後會變成雙螢幕。實體機主螢幕虛擬機視窗會看到Windows畫面,而副螢幕會暫時黑螢幕。
在虛擬機內下載 Geforce Experience,登入Nvidia帳號,安裝驅動程式。
之後Windows應該就會正常顯示雙螢幕。
接著在系統設定 → 顯示器 → 圖形,指定應用程式使用Nvidia顯示卡彩現,而非QXL。
如果副螢幕有喇叭,則Windows的音效會透過HDMI從副螢幕輸出。
8. 來玩遊戲吧#
理論上遊戲效能在副螢幕(連接到Nvidia顯示卡)最好。像我這種不想接第二個螢幕的,就會將副螢幕換成「HDMI顯卡欺騙器」,約一個轉接頭大小而已,並在Windows顯示設定將螢幕只顯示於虛擬機視窗,就可以用單一螢幕同時操控Linux和Windows。
在虛擬機內部安裝 Looking Glass,這樣便可在實體機主螢幕以客戶端低延遲存取Windows桌面,也就不用接二個實體螢幕了。
使用HDMI顯卡欺騙器前務必先裝好Nvidia驅動,以及設定Looking Glass開機自動啟動。
9. 如何讓Linux實體機重新使用Nvidia獨顯#
不重開機情況下,將虛擬機關機與解除vfio-pci綁定,再載入Nvidia核心模組即可重新讓實體機存取顯示卡。但同樣地,虛擬機需要顯示卡的話就得將其手動綁回去。