--- title: "PCI passthrough con VFIO y KVM en Ubuntu 24.04" description: "Activa IOMMU, asigna una GPU u otro dispositivo PCIe al driver vfio-pci y pásalo a una VM KVM con libvirt en Ubuntu 24.04 con rendimiento casi nativo." category: "Gestión de Hardware y Sistema" language: es source: https://cubepath.com/es/docs/gestión-de-hardware-y-sistema/pci-passthrough-para-maquinas-virtuales updated: 2026-09-25 --- # PCI passthrough con VFIO y KVM en Ubuntu 24.04 El PCI passthrough asigna un dispositivo PCIe físico (una GPU, una tarjeta de red o una controladora) directamente a una máquina virtual. La VM usa el dispositivo con su driver nativo y con un rendimiento casi igual al del hardware real, algo imprescindible para cargas de IA con GPU. En este tutorial activarás IOMMU en un host Ubuntu 24.04, reservarás el dispositivo para el driver `vfio-pci`, crearás una máquina virtual KVM con libvirt que lo use y comprobarás que el sistema invitado lo detecta. ## Requisitos previos - Un servidor físico con Ubuntu 24.04 LTS, por ejemplo un servidor dedicado (bare metal) de CubePath. Un VPS no sirve: no expone las extensiones de virtualización ni el IOMMU del hardware. - CPU con virtualización e IOMMU: Intel VT-x y VT-d, o AMD-V y AMD-Vi. - VT-d o AMD-Vi (a veces llamado "IOMMU") activado en la BIOS/UEFI del servidor, además de la virtualización. - El dispositivo que vas a pasar a la VM, y otro distinto (o la gráfica integrada/BMC) para la consola del host. - Un usuario no root con privilegios `sudo` y acceso a la consola remota del servidor (IPMI/KVM) por si un cambio de arranque falla. En los ejemplos se usa una GPU NVIDIA en la dirección PCI `01:00.0` con su controlador de audio en `01:00.1`. Sustituye las direcciones e identificadores por los de tu hardware. ## Paso 1: Comprobar la virtualización y activar IOMMU Comprueba que la CPU expone las extensiones de virtualización. El resultado debe ser mayor que 0: ```bash grep -cE 'vmx|svm' /proc/cpuinfo ``` ```output 64 ``` En CPUs Intel hay que activar el IOMMU con un parámetro del kernel. En AMD el kernel de Ubuntu 24.04 ya lo activa si está habilitado en la BIOS. En ambos casos conviene añadir `iommu=pt`, que deja en modo de paso directo a los dispositivos que el host sigue usando y evita perder rendimiento en ellos. Edita la configuración de GRUB: ```bash sudo nano /etc/default/grub ``` Añade los parámetros a la línea `GRUB_CMDLINE_LINUX_DEFAULT`, conservando los que ya tenga. En Intel: ```ini GRUB_CMDLINE_LINUX_DEFAULT="intel_iommu=on iommu=pt" ``` En AMD: ```ini GRUB_CMDLINE_LINUX_DEFAULT="iommu=pt" ``` Regenera la configuración de GRUB y reinicia: ```bash sudo update-grub sudo systemctl reboot ``` Tras el reinicio, confirma que el IOMMU está activo: ```bash sudo dmesg | grep -iE 'DMAR: IOMMU enabled|AMD-Vi: .*IOMMU|iommu: Default domain' ``` ```output [ 0.412345] DMAR: IOMMU enabled [ 1.034567] iommu: Default domain type: Passthrough (set via kernel command line) ``` Si el directorio `/sys/kernel/iommu_groups/` está vacío, el IOMMU no está activo: revisa que VT-d o AMD-Vi esté habilitado en la BIOS. ## Paso 2: Identificar el dispositivo y su grupo IOMMU El IOMMU agrupa los dispositivos que no puede aislar entre sí. Solo puedes pasar a una VM un grupo completo, así que el dispositivo debería estar solo en su grupo o acompañado únicamente de funciones propias (como el audio HDMI de una GPU). Lista los grupos con sus dispositivos: ```bash for d in /sys/kernel/iommu_groups/*/devices/*; do g=${d#/sys/kernel/iommu_groups/}; echo "Grupo ${g%%/*}: $(lspci -nns "${d##*/}")"; done | sort -V ``` ```output Grupo 14: 0000:00:1f.0 ISA bridge [0601]: Intel Corporation C621 Series Chipset LPC/eSPI Controller [8086:a1c1] Grupo 27: 0000:01:00.0 VGA compatible controller [0300]: NVIDIA Corporation AD102 [GeForce RTX 4090] [10de:2684] (rev a1) Grupo 27: 0000:01:00.1 Audio device [0403]: NVIDIA Corporation AD102 High Definition Audio Controller [10de:22ba] (rev a1) ``` En este ejemplo el grupo 27 contiene solo la GPU y su audio, lo ideal. Anota dos datos: - Las direcciones PCI: `01:00.0` y `01:00.1`. - Los identificadores `fabricante:dispositivo` entre corchetes al final: `10de:2684` y `10de:22ba`. Si el grupo incluye otros dispositivos del sistema (un puerto raíz PCIe compartido, una controladora SATA), prueba a mover la tarjeta a otra ranura, que suele colgar de otro puerto raíz. ## Paso 3: Reservar el dispositivo para vfio-pci Si el host carga el driver normal de la GPU (`nouveau` o `nvidia`), la tarjeta queda ocupada. Configura el módulo `vfio-pci` para que reclame los dispositivos por su identificador y para que se cargue antes que esos drivers. Crea el archivo de opciones del módulo: ```bash sudo nano /etc/modprobe.d/vfio.conf ``` Añade tus identificadores separados por comas: ```text options vfio-pci ids=10de:2684,10de:22ba softdep nouveau pre: vfio-pci softdep nvidia pre: vfio-pci ``` Las líneas `softdep` obligan a cargar `vfio-pci` antes que los drivers de NVIDIA. Para una GPU AMD usa `softdep amdgpu pre: vfio-pci`. Para que `vfio-pci` reclame el dispositivo durante el arranque temprano, añade los módulos al initramfs: ```bash sudo nano /etc/initramfs-tools/modules ``` Añade al final: ```text vfio vfio_iommu_type1 vfio_pci ``` Regenera el initramfs y reinicia: ```bash sudo update-initramfs -u sudo systemctl reboot ``` Comprueba qué driver está usando ahora cada función del dispositivo: ```bash lspci -nnk -s 01:00 ``` ```output 01:00.0 VGA compatible controller [0300]: NVIDIA Corporation AD102 [GeForce RTX 4090] [10de:2684] (rev a1) Kernel driver in use: vfio-pci Kernel modules: nouveau 01:00.1 Audio device [0403]: NVIDIA Corporation AD102 High Definition Audio Controller [10de:22ba] (rev a1) Kernel driver in use: vfio-pci Kernel modules: snd_hda_intel ``` `Kernel driver in use: vfio-pci` en las dos funciones confirma que el dispositivo está listo para la VM. ## Paso 4: Instalar KVM y libvirt Instala QEMU, libvirt, `virt-install` y el firmware UEFI OVMF (las GPU modernas funcionan mejor con VMs UEFI): ```bash sudo apt install qemu-system-x86 libvirt-daemon-system libvirt-clients virtinst ovmf ``` Añade tu usuario al grupo `libvirt` para gestionar VMs sin `sudo` y abre una sesión nueva para que surta efecto: ```bash sudo usermod -aG libvirt "$USER" ``` Comprueba que libvirt responde y que el dispositivo está disponible como nodo PCI: ```bash virsh -c qemu:///system nodedev-list --cap pci | grep 01_00 ``` ```output pci_0000_01_00_0 pci_0000_01_00_1 ``` Estos nombres son los que usarás para asignar el dispositivo. ## Paso 5: Crear la máquina virtual con el dispositivo Descarga la ISO del sistema invitado en el directorio de imágenes de libvirt. En este ejemplo, Ubuntu Server 24.04: ```bash sudo wget -P /var/lib/libvirt/images https://releases.ubuntu.com/24.04/ubuntu-24.04.3-live-server-amd64.iso ``` > Nota: el nombre exacto de la ISO cambia con cada versión puntual. Consulta el actual en [releases.ubuntu.com/24.04](https://releases.ubuntu.com/24.04/). Crea la VM con arranque UEFI, 16 GB de RAM, 8 vCPU, un disco de 100 GB y las dos funciones de la GPU: ```bash virt-install \ --connect qemu:///system \ --name vm-gpu \ --memory 16384 \ --vcpus 8 \ --cpu host-passthrough \ --os-variant ubuntu24.04 \ --boot uefi \ --disk size=100,format=qcow2 \ --cdrom /var/lib/libvirt/images/ubuntu-24.04.3-live-server-amd64.iso \ --network network=default \ --graphics vnc,listen=127.0.0.1 \ --host-device pci_0000_01_00_0 \ --host-device pci_0000_01_00_1 \ --noautoconsole ``` `--host-device` añade cada función PCI a la VM en modo gestionado: libvirt la desvincula del host al arrancar la VM y la devuelve al apagarla. `--cpu host-passthrough` expone a la VM el modelo de CPU real, lo que mejora el rendimiento. La instalación se hace por VNC, que solo escucha en localhost. Averigua el puerto: ```bash virsh -c qemu:///system vncdisplay vm-gpu ``` ```output 127.0.0.1:0 ``` La pantalla `:0` corresponde al puerto 5900. Desde tu equipo, abre un túnel SSH (sustituye `your_user` y `your_server_ip`) y conecta un cliente VNC a `localhost:5900`: ```bash ssh -L 5900:127.0.0.1:5900 your_user@your_server_ip ``` Completa la instalación del sistema invitado como en cualquier servidor. ## Paso 6: Verificar el dispositivo dentro de la VM Comprueba desde el host que la VM tiene asignado el dispositivo: ```bash virsh -c qemu:///system dumpxml vm-gpu | grep -A3 '
``` Dentro de la VM, el dispositivo debe aparecer como hardware PCI normal: ```bash lspci -nn | grep -i nvidia ``` ```output 06:00.0 VGA compatible controller [0300]: NVIDIA Corporation AD102 [GeForce RTX 4090] [10de:2684] (rev a1) 07:00.0 Audio device [0403]: NVIDIA Corporation AD102 High Definition Audio Controller [10de:22ba] (rev a1) ``` Instala el driver recomendado por Ubuntu dentro de la VM y reinicia: ```bash sudo ubuntu-drivers install sudo systemctl reboot ``` Tras el reinicio, `nvidia-smi` debe mostrar la GPU con su memoria y temperatura: ```bash nvidia-smi ``` ## Paso 7: Mejorar el rendimiento con huge pages y CPU pinning (opcional) Para cargas intensivas, dos ajustes reducen la latencia: respaldar la memoria de la VM con páginas de 2 MB (huge pages) y fijar cada vCPU a un núcleo físico concreto. Reserva huge pages suficientes para la VM. Para 16 GB hacen falta 8192 páginas de 2 MB: ```bash echo "vm.nr_hugepages = 8192" | sudo tee /etc/sysctl.d/80-hugepages.conf sudo sysctl --system ``` Comprueba la reserva: ```bash grep HugePages_Total /proc/meminfo ``` ```output HugePages_Total: 8192 ``` Si el valor es menor que el pedido, la memoria está fragmentada: reinicia el host para reservarla al arrancar. Consulta la topología de la CPU para elegir núcleos que no uses en el host, preferiblemente del mismo nodo NUMA que la GPU: ```bash lscpu -e=CPU,NODE,CORE ``` Edita la definición de la VM con la VM apagada: ```bash virsh -c qemu:///system edit vm-gpu ``` Añade estos bloques dentro de ``, a la altura de `` y ``, y ajusta los números de CPU a los de tu máquina: ```xml ``` Arranca la VM y comprueba la asignación: ```bash virsh -c qemu:///system start vm-gpu virsh -c qemu:///system vcpupin vm-gpu ``` ## Solución de problemas **`lspci -k` sigue mostrando `nouveau` o `nvidia` como driver en uso.** El driver del host cargó antes que `vfio-pci`. Revisa los identificadores de `/etc/modprobe.d/vfio.conf`, confirma que añadiste los módulos a `/etc/initramfs-tools/modules` y vuelve a ejecutar `sudo update-initramfs -u`. Si tienes instalado el driver propietario de NVIDIA en el host y no lo necesitas, desinstálalo. **La VM no arranca con `group N is not viable`.** Otro dispositivo del mismo grupo IOMMU sigue en uso por el host. Todos los dispositivos del grupo deben estar libres o asignados a la VM. Revisa el grupo con el comando del paso 2 y mueve la tarjeta de ranura si comparte grupo con dispositivos que el host necesita. **No hay grupos IOMMU.** VT-d o AMD-Vi no está activado en la BIOS, o falta `intel_iommu=on` en Intel. Comprueba los parámetros del arranque actual con `cat /proc/cmdline`. **La GPU funciona la primera vez pero no tras reiniciar la VM.** Algunas GPU no se reinician correctamente al pasar de una VM a otra (el llamado "reset bug"). Apaga la VM por completo en lugar de reiniciarla y, si persiste, reinicia el host. **Errores de QEMU al arrancar la VM.** El detalle está en `/var/log/libvirt/qemu/vm-gpu.log` y en `sudo journalctl -u libvirtd -n 50`. ## Conclusión Has activado IOMMU, reservado un dispositivo PCIe para `vfio-pci` y creado una VM KVM que lo usa con su driver nativo, con ajustes opcionales de memoria y CPU para exprimir el rendimiento. Como siguientes pasos puedes: - Instalar CUDA o un runtime de contenedores con soporte de GPU dentro de la VM para cargas de IA. - Usar SR-IOV en tarjetas de red compatibles para repartir una sola NIC entre varias VMs. - Automatizar la creación de VMs con plantillas de libvirt o con Terraform y el proveedor de libvirt.