- Posted on
- • Artificial Intelligence
GPU Passthrough for Artificial Intelligence Labs
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
GPU Passthrough for Artificial Intelligence Labs: Near-Bare-Metal AI in VMs
Ever wished you could hand your RTX/AMD GPU directly to a virtual machine and train models at almost native speed—without juggling dual-boot or risking global package conflicts? GPU passthrough makes that real. With KVM, VFIO, and a bit of kernel tuning, you can carve out isolated, reproducible AI sandboxes that deliver 95–100% of bare-metal performance for many workloads.
This guide explains why GPU passthrough is worth it, and walks you through a practical, distro-agnostic setup. You’ll get step-by-step instructions and commands for apt, dnf, and zypper, plus tips for validation and troubleshooting.
Why GPU Passthrough for AI Labs?
Isolation and reproducibility: Students and researchers get clean, snapshot-able VMs with pinned driver/toolkit versions.
Multi-tenant efficiency: Share a single GPU host across many labs, safely partitioned per VM.
Performance: PCIe passthrough bypasses emulation—latency is low, bandwidth is high, overhead is often in the low single digits.
Flexibility: Run Ubuntu, Fedora, or openSUSE guests with their own CUDA/ROCm stacks, CI pipelines, and security policies.
What you’ll need
CPU/board with IOMMU (Intel VT-d or AMD-Vi/SVM) and BIOS options for:
- SVM/AMD-V or VT-x/VT-d
- Above 4G decoding (recommended)
- Resizable BAR (optional)
A GPU in its own IOMMU group (or a board that supports good PCIe isolation)
Ideally a second GPU or onboard graphics for the host display (headless host is fine)
A recent Linux host (Ubuntu/Debian, Fedora/RHEL, openSUSE) with KVM support
Actionable Plan (4 Steps)
1) Prepare the host: Virtualization stack + IOMMU
Install KVM/libvirt/OVMF and essentials.
- Ubuntu/Debian (apt):
sudo apt update
sudo apt install -y qemu-kvm libvirt-daemon-system libvirt-clients virt-manager ovmf pciutils swtpm swtpm-tools bridge-utils dnsmasq numactl
sudo usermod -aG libvirt,kvm $USER
sudo systemctl enable --now libvirtd
- Fedora/RHEL (dnf):
sudo dnf groupinstall -y "Virtualization"
sudo dnf install -y virt-manager edk2-ovmf pciutils swtpm swtpm-tools numactl
sudo usermod -aG libvirt,kvm $USER
sudo systemctl enable --now libvirtd
- openSUSE (zypper):
sudo zypper install -y qemu-kvm libvirt virt-manager ovmf pciutils swtpm swtpm-tools numactl
sudo usermod -aG libvirt,kvm $USER
sudo systemctl enable --now libvirtd
Enable IOMMU in your kernel command line.
- Determine your vendor:
lscpu | grep -i vendor
- Edit
/etc/default/gruband append:- Intel:
intel_iommu=on iommu=pt - AMD:
amd_iommu=on iommu=pt
- Intel:
Example line:
GRUB_CMDLINE_LINUX="... amd_iommu=on iommu=pt"
- Update GRUB and reboot:
- Ubuntu/Debian:
sudo update-grub
- Fedora/RHEL (BIOS):
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
- Fedora/RHEL (UEFI, Fedora path example):
sudo grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg
- openSUSE:
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
- Reboot:
sudo reboot
Verify IOMMU is active:
dmesg | grep -e IOMMU -e DMAR
List IOMMU groups:
find /sys/kernel/iommu_groups -type l | sort
2) Bind the GPU to VFIO (passthrough driver)
Identify your GPU and its paired functions (e.g., GPU + HDMI audio share the same slot, different function).
lspci -nnk | grep -A3 -E "VGA|3D|Audio"
Note the vendor:device IDs in brackets, for example 10de:1eb8 (GPU) and 10de:10f0 (audio). Bind them to vfio-pci.
- Create a VFIO modprobe config:
sudo nano /etc/modprobe.d/vfio.conf
Add (replace IDs with yours):
options vfio-pci ids=10de:1eb8,10de:10f0 disable_vga=1
- Blacklist host GPU drivers for the passthrough device only (do NOT blacklist the driver for the GPU driving your host display):
sudo nano /etc/modprobe.d/blacklist-gpu.conf
Add as appropriate for your device:
blacklist nouveau
blacklist nvidia
# or for AMD passthrough device:
# blacklist amdgpu
# blacklist radeon
- Regenerate initramfs and reboot:
- Ubuntu/Debian:
sudo update-initramfs -u
- Fedora/RHEL/openSUSE:
sudo dracut -f
- Reboot:
sudo reboot
Confirm it’s bound to vfio-pci:
lspci -nnk -d 10de:1eb8
# Should show: Kernel driver in use: vfio-pci
Tip: If the device won’t bind (still using nvidia/amdgpu), ensure no display managers or services are using it and that you captured all functions (function 0 and 1).
3) Create the VM and attach the GPU
Use OVMF (UEFI) and Q35 machine type. You can use virt-manager (GUI) or CLI.
- virt-install (example; adjust paths and sizes):
Ubuntu/Debian OVMF path:
virt-install \
--name ai-guest \
--memory 32768 --memorybacking hugepages=on \
--vcpus 8 \
--cpu host-passthrough,topology.sockets=1,cores=4,threads=2 \
--disk size=200,bus=virtio \
--cdrom /var/lib/libvirt/boot/ubuntu-22.04.iso \
--os-variant ubuntu22.04 \
--graphics none \
--network network=default,model=virtio \
--boot loader=/usr/share/OVMF/OVMF_CODE.fd,loader.readonly=yes,loader.type=pflash,nvram.template=/usr/share/OVMF/OVMF_VARS.fd \
--machine q35
Fedora/RHEL OVMF path:
--boot loader=/usr/share/edk2/ovmf/OVMF_CODE.fd,loader.readonly=yes,loader.type=pflash,nvram.template=/usr/share/edk2/ovmf/OVMF_VARS.fd
openSUSE OVMF path (example):
--boot loader=/usr/share/qemu/ovmf-x86_64-code.bin,loader.readonly=yes,loader.type=pflash,nvram.template=/usr/share/qemu/ovmf-x86_64-vars.bin
Attach the GPU (and its audio/function). With virt-manager: Add Hardware -> PCI Host Device -> select both functions. Or via XML:
virsh edit ai-guest
Add inside the domain:
<memoryBacking>
<hugepages/>
</memoryBacking>
<devices>
<hostdev mode='subsystem' type='pci' managed='yes'>
<source>
<address domain='0x0000' bus='0x2d' slot='0x00' function='0x0'/>
</source>
</hostdev>
<hostdev mode='subsystem' type='pci' managed='yes'>
<source>
<address domain='0x0000' bus='0x2d' slot='0x00' function='0x1'/>
</source>
</hostdev>
</devices>
Use your actual domain/bus/slot/function values from lspci -nn.
Optional performance tuning:
- CPU pinning (example pin vCPUs to physical cores 2–9):
<cputune>
<vcpupin vcpu='0' cpuset='2'/>
<vcpupin vcpu='1' cpuset='3'/>
<vcpupin vcpu='2' cpuset='4'/>
<vcpupin vcpu='3' cpuset='5'/>
<vcpupin vcpu='4' cpuset='6'/>
<vcpupin vcpu='5' cpuset='7'/>
<vcpupin vcpu='6' cpuset='8'/>
<vcpupin vcpu='7' cpuset='9'/>
</cputune>
- Reserve hugepages on the host (example: 16 GB of 2 MB pages):
echo 8192 | sudo tee /proc/sys/vm/nr_hugepages
Persist via /etc/sysctl.d/99-hugepages.conf:
vm.nr_hugepages = 8192
4) Install GPU drivers inside the guest and validate
Boot the VM, install your OS, then install NVIDIA (example) or AMD drivers. Below are distro-native approaches for NVIDIA.
- Ubuntu/Debian guest (apt):
sudo apt update
sudo apt install -y build-essential dkms linux-headers-$(uname -r)
sudo ubuntu-drivers autoinstall
sudo reboot
nvidia-smi
- Fedora guest (dnf):
sudo dnf install -y akmod-nvidia xorg-x11-drv-nvidia-cuda
sudo reboot
nvidia-smi
- openSUSE guest (zypper) – add NVIDIA repo, then install:
sudo zypper addrepo --refresh https://download.nvidia.com/opensuse/tumbleweed NVIDIA
# For Leap, replace 'tumbleweed' with your Leap version path from NVIDIA docs
sudo zypper refresh
sudo zypper install -y nvidia-driver-G06
sudo reboot
nvidia-smi
Install PyTorch with CUDA and run a quick check:
python3 -m pip install --upgrade pip
python3 -m pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
python3 - <<'PY'
import torch
print("CUDA available:", torch.cuda.is_available())
print("Device:", torch.cuda.get_device_name(0) if torch.cuda.is_available() else "N/A")
PY
Optional quick training smoke test:
python3 - <<'PY'
import torch, torch.nn as nn
x = torch.randn(1024, 2048, device='cuda')
w = torch.randn(2048, 1024, device='cuda')
for _ in range(5):
y = x @ w
y = nn.functional.relu(y)
torch.cuda.synchronize()
print("OK")
PY
Real-World Notes and Troubleshooting
- Check VFIO and IOMMU logs:
dmesg | grep -e VFIO -e IOMMU -e DMAR
journalctl -b | grep -i vfio
IOMMU groups not isolated: Some motherboards group multiple PCIe slots together. If your GPU shares a group with other devices you cannot pass through safely, try different slots or update BIOS. As a last resort, the ACS override kernel parameter exists (
pcie_acs_override=downstream,multifunction), but it reduces isolation and can be unstable—use only if you understand the risks.Attach both functions: Many GPUs have an audio function; pass both or the guest may fail to init drivers.
Above 4G decoding: If the VM refuses to initialize the GPU or you see BAR-mapping errors, enable “Above 4G Decoding” in BIOS.
Headless host: If you bind your only display GPU to VFIO, you’ll lose local console graphics. Use SSH or IPMI/serial for management, or keep a separate host GPU.
Performance tips:
- Use
host-passthroughCPU in libvirt for best instruction set exposure. - Keep VM vCPUs/hugepages NUMA-local to the GPU’s PCIe root complex for low latency.
- Prefer virtio for disk/network; consider a bridged network for lab environments.
- Use
Example: A Small AI Lab on One Host
Host: 64-core EPYC, 256 GB RAM, 2×RTX 4090
Two VMs: Each gets 1 GPU, 16 vCPUs, 64 GB RAM with hugepages
Students SSH into their VM, run
condaorpip, train models. Snapshots keep environments clean; passthrough keeps training fast and consistent.
Conclusion and Next Steps (CTA)
You now have a repeatable path to put real GPUs inside virtual machines for AI work—without giving up performance. Next:
Automate with Ansible: template your libvirt XML and host tuning.
Add quotas and scheduling: integrate with Slurm or orchestration that boots VMs on demand.
Explore advanced sharing: NVIDIA MIG (A100/Hopper) or SR-IOV-capable GPUs for finer-grained multi-tenancy.
If you’d like a follow-up post with a complete Ansible playbook and libvirt hooks to hot-plug GPUs per user or per CI job, let me know what distro and GPU you’re targeting.