PCI Passthrough para Máquinas Virtuales con VFIO
El PCI passthrough permite asignar dispositivos de hardware físico directamente a máquinas virtuales KVM, ofreciendo rendimiento nativo sin virtualización de la GPU u otros dispositivos PCIe. Esta guía cubre la configuración de IOMMU, el binding con el driver VFIO, el GPU passthrough completo, el ajuste de rendimiento y la resolución de problemas comunes.
Requisitos Previos
- CPU con soporte de virtualización: Intel VT-x + VT-d, o AMD-V + AMD-Vi/IOMMU
- BIOS/UEFI con IOMMU habilitado (buscar "VT-d", "AMD IOMMU", o "SR-IOV")
- Linux con kernel 4.1+ (soporte VFIO)
- KVM/QEMU instalado
- El dispositivo a pasar debe estar en un grupo IOMMU propio o con todos los dispositivos del grupo
# Verificar soporte de virtualización
egrep -c '(vmx|svm)' /proc/cpuinfo
# Verificar soporte IOMMU de la CPU
dmesg | grep -E "IOMMU|DMAR"
# Verificar la versión del kernel
uname -r
Activación de IOMMU
# Editar la configuración de GRUB
nano /etc/default/grub
# Para Intel VT-d, añadir a GRUB_CMDLINE_LINUX:
GRUB_CMDLINE_LINUX="intel_iommu=on iommu=pt"
# Para AMD-Vi, añadir:
GRUB_CMDLINE_LINUX="amd_iommu=on iommu=pt"
# iommu=pt (passthrough mode) mejora el rendimiento de los dispositivos
# que NO usan passthrough
# Actualizar GRUB
update-grub # Ubuntu/Debian
grub2-mkconfig -o /boot/grub2/grub.cfg # CentOS/Rocky
# Reiniciar el servidor
reboot
# Verificar que IOMMU está activo después del reinicio
dmesg | grep -E "IOMMU enabled|Adding to iommu"
cat /proc/cmdline | grep iommu
Verificar los grupos IOMMU:
# Script para listar todos los grupos IOMMU y sus dispositivos
cat << 'EOF' > /usr/local/bin/list-iommu-groups.sh
#!/bin/bash
# Listar grupos IOMMU con sus dispositivos
shopt -s nullglob
for g in /sys/kernel/iommu_groups/*; do
echo "Grupo IOMMU ${g##*/}:"
for d in $g/devices/*; do
echo " $(lspci -nns ${d##*/})"
done
done | sort -V
EOF
chmod +x /usr/local/bin/list-iommu-groups.sh
/usr/local/bin/list-iommu-groups.sh
Identificación de Dispositivos PCIe
# Listar todos los dispositivos PCIe con sus IDs
lspci -nn
# Buscar la GPU (o el dispositivo a pasar)
lspci -nn | grep -E "VGA|3D|NVIDIA|AMD|Radeon|Display"
# Salida típica:
# 01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GA102 [GeForce RTX 3090] [10de:2204] (rev a1)
# 01:00.1 Audio device [0403]: NVIDIA Corporation GA102 High Definition Audio [10de:1aef] (rev a1)
# IMPORTANTE: Para GPU passthrough necesitas AMBOS dispositivos
# La GPU (VGA) y el audio de la GPU deben estar en el mismo grupo IOMMU
# Ver el grupo IOMMU de la GPU
IOMMU_GROUP=$(ls /sys/bus/pci/devices/0000:01:00.0/iommu_group/devices/)
echo "Grupo IOMMU de la GPU:"
for dev in $IOMMU_GROUP; do
lspci -nns ${dev##*/}
done
# Anotar los IDs de vendor:device para la configuración VFIO
# Ejemplo: 10de:2204 y 10de:1aef para la GPU NVIDIA
Configuración de VFIO
# Instalar VFIO y herramientas de KVM
apt install -y qemu-kvm libvirt-daemon-system virt-manager
# Cargar el módulo VFIO
modprobe vfio
modprobe vfio-pci
modprobe vfio_iommu_type1
# Hacer permanente la carga de módulos
echo "vfio" >> /etc/modules-load.d/vfio.conf
echo "vfio-pci" >> /etc/modules-load.d/vfio.conf
echo "vfio_iommu_type1" >> /etc/modules-load.d/vfio.conf
# Configurar VFIO para que tome control de los dispositivos ANTES que el driver nativo
# Usando los IDs del paso anterior (10de:2204 y 10de:1aef para NVIDIA)
cat << 'EOF' > /etc/modprobe.d/vfio.conf
# Asignar los dispositivos PCI al driver VFIO en lugar del driver nativo
# Formato: options vfio-pci ids=VENDOR:DEVICE[,VENDOR:DEVICE]
options vfio-pci ids=10de:2204,10de:1aef
EOF
# Para NVIDIA: evitar que el driver noveau/nvidia cargue antes que VFIO
cat << 'EOF' >> /etc/modprobe.d/vfio.conf
# Desactivar los drivers de NVIDIA para que VFIO tenga prioridad
softdep nvidia pre: vfio-pci
softdep nouveau pre: vfio-pci
EOF
# En Ubuntu, actualizar initramfs
update-initramfs -u
# Reiniciar el servidor
reboot
# Verificar que VFIO tiene el control de los dispositivos
lspci -nnk | grep -A3 "NVIDIA"
# Debe mostrar: Kernel driver in use: vfio-pci
GPU Passthrough
Para pasar una GPU completa a una VM:
# Verificar que VFIO tiene control de la GPU
lspci -k | grep -E "NVIDIA|vfio"
# Ver el dispositivo desde el directorio de VFIO
ls /dev/vfio/
# Añadir el usuario libvirt al grupo de acceso a VFIO
usermod -aG kvm,libvirt usuario_admin
# Crear una VM con virt-install para GPU passthrough
virt-install \
--name vm-gpu \
--memory 16384 \
--vcpus 8 \
--os-variant win10 \
--disk path=/var/lib/libvirt/images/vm-gpu.qcow2,size=100,format=qcow2 \
--cdrom /ruta/windows.iso \
--graphics spice \
--video qxl \
--host-device 01:00.0 \ # GPU
--host-device 01:00.1 \ # Audio de la GPU
--boot uefi
Configurar el XML de la VM para passthrough óptimo:
<!-- virsh edit vm-gpu -->
<!-- Añadir en el bloque <features>: -->
<features>
<acpi/>
<apic/>
<!-- Ocultar la virtualización KVM de la GPU (necesario para algunos drivers) -->
<kvm>
<hidden state='on'/>
</kvm>
<!-- Hypervisor ID - necesario para NVIDIA -->
<hyperv mode='passthrough'>
<relaxed state='on'/>
<vapic state='on'/>
<spinlocks state='on' retries='8191'/>
<vendor_id state='on' value='1234567890ab'/>
</hyperv>
</features>
<!-- Configuración del dispositivo PCI en el XML: -->
<hostdev mode='subsystem' type='pci' managed='yes'>
<driver name='vfio'/>
<source>
<address domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
</source>
<address type='pci' domain='0x0000' bus='0x07' slot='0x00' function='0x0'/>
</hostdev>
Configuración de la VM con Passthrough
# Configurar hugepages para mejor rendimiento de memoria en la VM
# Reservar 16 GB (para una VM de 16 GB)
echo 8192 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
# Hacer permanente
echo "vm.nr_hugepages = 8192" >> /etc/sysctl.conf
sysctl -p
# Añadir hugepages a la configuración de la VM
virsh edit vm-gpu
# Añadir en <memoryBacking>:
<memoryBacking>
<hugepages>
<page size='2' unit='M'/>
</hugepages>
</memoryBacking>
<!-- Configurar la política de NUMA para mejor rendimiento -->
<numatune>
<memory mode='strict' nodeset='0'/>
</numatune>
<!-- CPU pinning: asignar cores físicos específicos a la VM -->
<cputune>
<vcpupin vcpu='0' cpuset='2'/>
<vcpupin vcpu='1' cpuset='3'/>
<vcpupin vcpu='2' cpuset='4'/>
<vcpupin vcpu='3' cpuset='5'/>
<!-- Cores del host reservados para el SO -->
<emulatorpin cpuset='0-1'/>
</cputune>
Aislamiento de CPU y Ajuste de Rendimiento
# Aislar CPUs del sistema operativo host para la VM
# Editar parámetros del kernel en GRUB
# GRUB_CMDLINE_LINUX="... isolcpus=2-9 nohz_full=2-9 rcu_nocbs=2-9"
# Verificar el aislamiento de CPUs
cat /sys/devices/system/cpu/isolated
# Configurar el governor de CPU para rendimiento máximo
for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
echo "performance" > $cpu
done
# Verificar el governor activo
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
# Activar/Desactivar MSR para la VM (Intel)
echo 1 > /sys/bus/pci/devices/0000:01:00.0/enable
Solución de Problemas
# IOMMU no está activo después del reinicio
dmesg | grep -i iommu
# Si no aparece nada, verificar en BIOS/UEFI que VT-d/AMD-Vi está habilitado
# Error "Device or resource busy" al asignar a VFIO
# El driver nativo está cargado antes que VFIO
lspci -k | grep -A2 "0000:01:00.0"
# Si muestra un driver diferente a vfio-pci:
echo "0000:01:00.0" > /sys/bus/pci/devices/0000:01:00.0/driver/unbind
echo "10de 2204" > /sys/bus/pci/drivers/vfio-pci/new_id
# Error en la VM: "VFIO: Failed to open /dev/vfio/iommu_group"
ls -la /dev/vfio/
# Verificar permisos
chmod 666 /dev/vfio/*
# La GPU no es detectada en la VM (Error 43 en Windows)
# Añadir vendor_id y ocultar KVM:
virsh edit vm-gpu
# Añadir <vendor_id state='on' value='1234567890ab'/> en <hyperv>
# Verificar que el grupo IOMMU es correcto
/usr/local/bin/list-iommu-groups.sh | grep -B1 -A5 "01:00"
# Ver los logs de QEMU/KVM
journalctl -u libvirtd -f
virsh dumpxml vm-gpu | grep hostdev
Conclusión
El PCI passthrough con VFIO transforma KVM en una plataforma de virtualización de rendimiento nativo para dispositivos GPU, tarjetas de captura de video, o cualquier hardware PCIe que requiera acceso directo. La configuración es compleja pero el resultado es una VM con rendimiento de hardware real, ideal para servidores de IA/ML, estaciones de trabajo remotas con GPU, o la consolidación de workloads especializados en hardware compartido. El éxito depende principalmente de la correcta configuración de IOMMU y de que el dispositivo esté en un grupo IOMMU propio o con dispositivos que también se vayan a pasar.


