Arch Linux如何將Nvidia GPU直通給Windows QEMU/KVM虛擬機

目錄
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.4.7
- 桌面環境: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核心模組載入回來。
- 再次編輯GRUB
sudo vim /etc/default/grub
- 將
GRUB_CMDLINE_LINUX_DEFAULT
增加為以下內容,vfio-pci.ids
後面接的是顯示卡的硬體ID,以逗號分隔。
GRUB_CMDLINE_LINUX_DEFAULT="intel_iommu=on iommu=pt video=efifb:off vfio-pci.ids=10de:1c82,10de:0fb9"
- Arch Linux可使用initramfs載入核心模組,因此修改initramfs內容:
sudo vim /etc/mkinitcpio.conf
- 將
Modules()
修改成如下,設定開機載入vfio核心模組。
MODULES=(vfio_pci vfio vfio_iommu_type1)
- 新增開機禁止載入的核心模組列表
sudo vim /etc/modprobe.d/blacklist.conf
- 填入以下內容,禁止Nvidia顯示卡的核心模組開機載入
blacklist nvidia
blacklist nouveau
- 刪除Xorg的設定檔
sudo rm /etc/X11/xorg.conf
- 重新生成initramfs。
sudo mkinitcpio -p linux
- 重新產生GRUB設定檔
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顯示卡,就是成功了。
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核心模組即可重新讓實體機存取顯示卡。但同樣地,虛擬機需要顯示卡的話就得將其手動綁回去。
本節內容參考自bryansteiner的作法。前面提過本節的內容可以用libvirt hook自動化執行,可是Arch Linux會卡死,所以只好手動執行。
以下內容可自行寫成獨立的script。
虛擬機關機。測試
nvidia-smi
指令,應該會顯示NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver
。利用virt指令,解除綁定vfio-pci裝置(
pci_0000_01_00_0
來自lscpi
結果)
sudo virsh nodedev-reattach pci_0000_01_00_0
sudo virsh nodedev-reattach pci_0000_01_00_1
- 移除vfio核心模組
sudo modprobe -r vfio_pci
sudo modprobe -r vfio_iommu_type1
sudo modprobe -r vfio
- 嘗試載入Nvidia核心模組
sudo modprobe nvidia_modeset
sudo modprobe nvidia_uvm
sudo modprobe nvidia
- 執行
nvidia-smi
指令看能不能檢測到顯示卡資訊。
此時可能要重新啟動桌面環境,應用程式才會抓得到Nvidia顯示卡,不然就得用Nvidia prime-run
指令來指定應用程式用Nvidia顯示卡運算(參見
Nvidia PRIME用法)
如果您要把vfio綁回去給虛擬機使用:
sudo modprobe -r nvidia_modeset
sudo modprobe -r nvidia_uvm
sudo modprobe -r nvidia
sudo modprobe vfio_pci
sudo modprobe vfio_iommu_type1
sudo modprobe vfio
sudo virsh nodedev-detach pci_0000_01_00_0
sudo virsh nodedev-detach pci_0000_01_00_1