Quick start: Cấu hình nhanh trong 5 phút
2 giờ sáng, ticket réo: máy ảo Windows dựng hình Blender bị nghẽn nặng. Card ảo VirtIO chỉ lết được 4 FPS trên viewport. Để cứu vãn tình thế, giải pháp duy nhất là bàn giao trực tiếp GPU vật lý cho VM qua VFIO.
Dưới đây là các bước thao tác trực tiếp trên Ubuntu 22.04 hoặc Debian 12:
Bước 1: Bật IOMMU trong bootloader
# Mở cấu hình GRUB
sudo nano /etc/default/grub
# Thêm tham số phù hợp với CPU vào biến GRUB_CMDLINE_LINUX_DEFAULT:
# CPU Intel: intel_iommu=on iommu=pt
# CPU AMD: amd_iommu=on iommu=pt
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_iommu=on iommu=pt"
# Cập nhật GRUB và khởi động lại
sudo update-grub
sudo reboot
Bước 2: Xác định Hardware ID của GPU
lspci -nn | grep -E "VGA|Audio"
# Kết quả mẫu từ RTX 3060:
# 01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GA106 [GeForce RTX 3060] [10de:2504] (rev a1)
# 01:00.1 Audio device [0403]: NVIDIA Corporation GA106 High Definition Audio Controller [10de:228e] (rev a1)
# Cặp Vendor:Device ID cần ghi nhớ là: 10de:2504, 10de:228e
Bước 3: Cô lập GPU bằng driver vfio-pci
Host Linux cần nhả card trước khi máy ảo khởi động. Tạo file cấu hình để gán driver vfio-pci cho cặp ID trên:
echo "options vfio-pci ids=10de:2504,10de:228e" | sudo tee /etc/modprobe.d/vfio.conf
# Nạp các module cần thiết khi boot
echo -e "vfio\nvfio_iommu_type1\nvfio_pci" | sudo tee -a /etc/modules
# Chặn driver đồ họa mặc định chiếm quyền điều khiển card
echo -e "blacklist nouveau\nblacklist nvidia\nblacklist snd_hda_intel" | sudo tee /etc/modprobe.d/blacklist.conf
# Cập nhật initramfs và reboot
sudo update-initramfs -u -k all
sudo reboot
Sau khi máy chủ khởi động lại, gõ lệnh kiểm tra:
lspci -nnk -d 10de:2504
# Kết quả dòng 'Kernel driver in use' phải báo chính xác: vfio-pci
Bản chất kỹ thuật: IOMMU và bài toán IOMMU Group
Máy ảo thông thường chỉ giao tiếp qua card màn hình ảo hóa như QXL hay VirtIO GPU. CPU host phải gánh toàn bộ khâu biên dịch tập lệnh đồ họa. Cách làm này đủ mượt để gõ văn bản hay duyệt web. Nhưng với render 3D hoặc game AAA, hiệu năng tụt hơn 70%.
VFIO (Virtual Function I/O) giải quyết triệt để nút thắt này. Cơ chế này cho phép máy ảo chiếm quyền điều khiển trực tiếp thanh ghi PCIe của GPU vật lý. Kết quả là hiệu năng đạt 97-99% so với cài trực tiếp trên bare-metal.
Hiểu đúng về IOMMU Group
IOMMU đóng vai trò như MMU dành riêng cho thiết bị I/O. Nó ánh xạ không gian địa chỉ bộ nhớ ảo của VM trực tiếp vào bus DMA của card phần cứng. Dù vậy, phần cứng bo mạch chủ không cô lập từng cổng PCIe độc lập mà gom chúng lại thành các IOMMU Group.
Quy tắc bất di bất dịch của KVM rất rõ ràng: IOMMU Group là đơn vị cô lập phần cứng nhỏ nhất. Bạn phải chuyển toàn bộ thiết bị trong cùng một group vào máy ảo, hoặc không chuyển thiết bị nào cả. Không có ngoại lệ.
Chạy script sau để rà soát nhóm thiết bị:
#!/bin/bash
for d in /sys/kernel/iommu_groups/*/devices/*; do
n=${d#*/iommu_groups/*}; n=${n%%/*}
printf 'IOMMU Group %s ' "$n"
lspci -nns "${d##*/}"
done | grep -i nvidia
Nếu card đồ họa và chip âm thanh HDMI nằm chung Group 13, bạn bắt buộc phải gán cả hai vào vfio-pci. Ngược lại, nếu GPU bị gom chung group với controller USB hay card mạng onboard, VM sẽ không thể boot độc lập.
Tối ưu chuyên sâu: Diệt Code 43, hạ độ trễ Frametime
Nhận card thành công ở tầng host mới xong một nửa công việc. Khởi động Windows lên, lỗi chấm than vàng kinh điển lập tức xuất hiện: Code 43.
1. Khắc phục triệt để Code 43
Từ bản driver 465 (tháng 4/2021), NVIDIA đã gỡ bỏ lệnh chặn máy ảo trên card GeForce. Dù vậy, nhiều tựa game và ứng dụng đồ họa vẫn chủ động quét hypervisor rồi tự dừng hoạt động. Giải pháp triệt để là ẩn dấu vết KVM trong file XML của máy ảo (virsh edit win10-gpu):
<features>
<acpi/>
<apic/>
<hyperv mode='custom'>
<relaxed state='on'/>
<vapic state='on'/>
<spinlocks state='on' threshold='8191'/>
<vendor_id state='on' value='GenuineIntel'/>
</hyperv>
<kvm>
<hidden state='on'/>
</kvm>
<ioapic driver='kvm'/>
</features>
2. Gán PCIe và nạp sạch VBIOS
Nếu GPU passthrough là card xuất hình duy nhất của host lúc khởi động, UEFI của host đã ghi đè lên bộ nhớ ROM của card. Hậu quả là màn hình đen xì khi máy ảo nạp driver. Hãy tải file VBIOS gốc từ TechPowerUp hoặc dump từ trước, sau đó chỉ định trực tiếp trong thẻ <hostdev>:
<hostdev mode='subsystem' type='pci' managed='yes'>
<source>
<address domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
</source>
<rom file='/var/lib/libvirt/vbios/rtx3060.rom'/>
<address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0' multifunction='on'/>
</hostdev>
<hostdev mode='subsystem' type='pci' managed='yes'>
<source>
<address domain='0x0000' bus='0x01' slot='0x00' function='0x1'/>
</source>
<address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x1'/>
</hostdev>
3. CPU Pinning và HugePages: Triệt tiêu Micro-stutter
Hiện tượng giật hình bất chợt (micro-stutter) xảy ra do CPU host liên tục nhảy core (context switching) và bộ nhớ RAM bị phân mảnh. Cố định cặp core/thread vật lý vào từng vCPU sẽ ghìm frametime dao động dưới 2ms:
<vcpu placement='static'>8</vcpu>
<cputune>
<vcpupin vcpu='0' cpuset='4'/>
<vcpupin vcpu='1' cpuset='12'/>
<vcpupin vcpu='2' cpuset='5'/>
<vcpupin vcpu='3' cpuset='13'/>
<vcpupin vcpu='4' cpuset='6'/>
<vcpupin vcpu='5' cpuset='14'/>
<vcpupin vcpu='6' cpuset='7'/>
<vcpupin vcpu='7' cpuset='15'/>
<emulatorpin cpuset='0,8'/>
</cputune>
<memoryBacking>
<hugepages/>
</memoryBacking>
Cấu hình thêm HugePages 2MB giúp bảng phân trang TLB giảm tỉ lệ cache miss từ 12% xuống còn dưới 0.8%, giúp khung hình duy trì ổn định.
Kinh nghiệm thực chiến khi xử lý sự cố
- Mainboard phân nhóm IOMMU kém: Khi GPU bị kẹp chung cụm với controller SATA, giải pháp tình thế là thêm tham số kernel
pcie_acs_override=downstream,multifunction. Cách này ép hệ thống xé lẻ group. Nhược điểm: ranh giới bảo mật DMA giữa các VM bị phá vỡ, chỉ nên dùng trong homelab hoặc môi trường dev. - Lỗi màn hình đen khi VM khởi chạy: Hãy tắt CSM (Compatibility Support Module) trong BIOS bo mạch chủ. Luôn khởi tạo VM với firmware
OVMF (UEFI)kết hợp chipsetQ35, tránh xa SeaBIOS hay chuẩn i440fx cũ kỹ. - Vận hành máy ảo không cần cắm màn hình: Cắm một đầu HDMI Dummy Plug (khoảng 35.000 VNĐ) vào cổng card. Cài đặt Sunshine server trên VM Windows và stream về máy làm việc qua client Moonlight. Thiết lập mạng LAN 1Gbps cho phép stream 2K 120Hz mượt mà với độ trễ giải mã dưới 3ms.
- Giải phóng xung đột BAR 0: Khi lệnh
dmesg | grep -i vfiobáo lỗi BAR 0: cannot reserve, framebuffer của Linux đang giữ chặt GPU. Khắc phục bằng cách gắn thêm tham sốvideo=efifb:offhoặcinitcall_blacklist=sysfb_initvào GRUB.

