Proxmox VFIO “device is in use”: separar dispositivos PCI del host de la manera correcta

¿Te fue útil?

Si alguna vez probaste el passtrough PCI en Proxmox y te encontraste con “device is in use”, has observado al kernel de Linux haciendo su trabajo: protegiendo un dispositivo que todavía pertenece a otra cosa. El problema es que ese “otra cosa” a menudo es invisible: un framebuffer cargado temprano en el arranque, una pequeña función de audio de la GPU, un controlador de almacenamiento, una regla de udev o el propio Proxmox intentando ayudar.

Realidad en producción: no quieres “simplemente reiniciar y esperar”. Quieres control determinista sobre quién es el propietario de la función PCI, cuándo se reclama y cómo demostrar que está desacoplado antes de entregarlo a una VM. Esto es cómo hacerlo sin superstición.

Un modelo mental útil: qué significa realmente “device is in use”

Cuando Proxmox (QEMU) te dice que un dispositivo PCI está “in use”, no es poético. Está informando que algo en el host actualmente mantiene el dispositivo abierto, posee el enlace del controlador o de otra manera impide que QEMU tome el control de forma segura mediante VFIO.

En Linux, una función PCI está “propiedad de” principalmente a través del vinculado del controlador. Si nouveau, amdgpu, nvidia, xhci_hcd, ixgbe, megaraid_sas, etc. están vinculados, el kernel piensa, correctamente, que el dispositivo forma parte del host. VFIO necesita que ese dispositivo esté vinculado a vfio-pci (o a veces vfio-pci más quirks específicos de reset) para que QEMU pueda mapearlo en el invitado.

Pero el vinculado es solo el comienzo. Puedes desvincular un dispositivo y aun así tener un proceso tocándolo vía sysfs, una consola framebuffer adjunta o una función de audio usada por PulseAudio que olvidaste que existía. Y Proxmox añade un giro más: es un hipervisor que trata de ser útil. Si el host cree que una GPU está disponible, algo puede tomarla durante el arranque (framebuffer, DRM) y entonces estás negociando con un objetivo en movimiento.

Así que el objetivo es simple y estricto:

  • El dispositivo (y cualquier función que deba viajar con él) están en un grupo IOMMU aislado o aceptas la compensación de seguridad con ACS override.
  • El host nunca vincula un controlador distinto a VFIO durante el arranque.
  • El dispositivo tiene capacidad de reset suficiente para sobrevivir reinicios/apagados de la VM.
  • QEMU obtiene acceso exclusivo mediante VFIO, cada vez, sin la “ruleta de desvinculación” manual.

Verdad seca: el passthrough VFIO es menos una “función lista para usar” y más un “contrato que haces cumplir”.

Guion de diagnóstico rápido (primero/segundo/tercero)

Este es el orden que encuentra el cuello de botella rápidamente en producción, donde no tienes tiempo para admirar los logs del kernel.

Primero: demuestra quién posee el dispositivo ahora mismo

  • Localiza la dirección PCI (bus:slot.func) y verifica qué controlador está vinculado.
  • Confirma si los módulos VFIO están cargados.
  • Revisa si alguna otra función en el mismo dispositivo multifunción aún está vinculada a un controlador del host.

Segundo: verifica el agrupamiento IOMMU y si estás peleando con la plataforma

  • Si el dispositivo comparte grupo IOMMU con algo que el host necesita (como el controlador SATA que ejecuta tu sistema root), detente y rediseña.
  • Si usaste ACS override, trátalo como una excepción de seguridad, no como una medalla.

Tercero: busca a los reclamantes de arranque temprano y usuarios “invisibles”

  • Framebuffer/DRM (común con GPUs).
  • udev que carga controladores según modalias.
  • servicios systemd (gestor de sesiones gráficas, daemons de persistencia).
  • Módulos del kernel residuales en initramfs.

Si haces esos tres en orden, “device is in use” se vuelve un ticket solucionable en lugar de una vibra.

Hechos y contexto histórico interesantes (por qué VFIO es raro)

  1. VFIO no fue el primer intento. Antes de que VFIO se convirtiera en el estándar, pci-stub se usaba comúnmente para “aparcar” dispositivos lejos de los controladores del host.
  2. IOMMU no es solo para virtualización. Existe para la aislamiento DMA, protegiendo la memoria de dispositivos que pueden hacer bus-master—útil para seguridad y fiabilidad incluso sin VMs.
  3. Los dispositivos PCIe pueden hacer DMA sin pedir permiso. Por eso un dispositivo en el grupo IOMMU equivocado no es simplemente inconveniente; es un problema de límites.
  4. ACS (Access Control Services) es una característica hardware, no el estado de ánimo del kernel. Cuando las plataformas no exponen ACS correctamente, Linux no siempre puede dividir grupos limpiamente.
  5. Los dispositivos multifunción son una trampa clásica. Las GPUs a menudo aparecen como VGA + audio HDMI (y a veces controladores USB-C). Pasar solo una función suele generar conflictos.
  6. El soporte de reset es desigual. Algunas GPUs de consumo son notorias por no resetearse limpiamente entre reinicios de invitados sin trucos adicionales.
  7. Los gráficos de arranque temprano existen. El framebuffer de Linux y DRM KMS arrancan pronto. Si lo toman, ya llegaste tarde a menos que pre-vincules a VFIO en initramfs.
  8. “Device is in use” suele ser un lock a nivel kernel, no un bloqueo de archivo. Herramientas como lsof pueden ser inútiles porque el “usuario” es el propio controlador del kernel.
  9. Las pilas de virtualización evolucionaron alrededor de las necesidades de QEMU. El diseño de VFIO se alinea con el modelo de QEMU/KVM: el espacio de usuario obtiene acceso mediado y seguro mientras el kernel aplica aislamiento DMA.

Una cita que ha quedado en círculos SRE: La esperanza no es una estrategia — máxima general de operaciones. Trátala como un principio SRE, no como decoración. (idea parafraseada)

Tareas prácticas: comandos, salidas y decisiones (12+)

Estos son los movimientos que realmente responden “¿quién lo está usando?” y “¿qué hago a continuación?” Cada tarea incluye: comando, qué significa la salida y la decisión que tomas.

Tarea 1: Identificar el dispositivo y el controlador actualmente vinculado

cr0x@server:~$ lspci -nnk -d 10de:2684
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation Device [10de:2684] (rev a1)
	Subsystem: ASUSTeK Computer Inc. Device [1043:88aa]
	Kernel driver in use: nouveau
	Kernel modules: nouveau, nvidia_drm, nvidia

Significado: La GPU está vinculada a nouveau. VFIO no puede tomarla mientras un controlador gráfico la posea.

Decisión: Debes evitar que el host vincule nouveau/nvidia y vincular a vfio-pci en su lugar (idealmente en initramfs).

Tarea 2: Confirmar que los módulos VFIO están cargados

cr0x@server:~$ lsmod | egrep 'vfio|kvm'
vfio_pci               16384  0
vfio_pci_core          73728  1 vfio_pci
vfio_iommu_type1       40960  0
vfio                   45056  2 vfio_pci_core,vfio_iommu_type1
kvm_intel             397312  0
kvm                  1036288  1 kvm_intel

Significado: VFIO y KVM están presentes. Es necesario pero no suficiente.

Decisión: Si faltan módulos VFIO, arregla eso primero. De lo contrario, procede con temas de binding/IOMMU.

Tarea 3: Verificar que IOMMU está habilitado en el kernel

cr0x@server:~$ dmesg | egrep -i 'iommu|dmari|remapping' | head -n 8
[    0.000000] Command line: BOOT_IMAGE=/boot/vmlinuz-6.8.12-4-pve root=ZFS=rpool/ROOT/pve-1 ro quiet intel_iommu=on iommu=pt
[    0.412345] DMAR: IOMMU enabled
[    0.412901] DMAR: Intel(R) Virtualization Technology for Directed I/O
[    0.413112] DMAR: Interrupt remapping enabled
[    0.419876] pci 0000:00:00.0: DMAR: Skip IOMMU disabling for graphics

Significado: IOMMU está activo y el remapeo de interrupciones está habilitado (bueno para estabilidad y seguridad).

Decisión: Si no ves IOMMU habilitado, detente y arregla la BIOS y los parámetros del kernel antes de tocar cualquier otra cosa.

Tarea 4: Comprobar la pertenencia al grupo IOMMU

cr0x@server:~$ for g in /sys/kernel/iommu_groups/*; do echo "Group $(basename "$g")"; ls -1 "$g/devices"; done | sed -n '1,24p'
Group 0
0000:00:00.0
0000:00:01.0
Group 10
0000:01:00.0
0000:01:00.1
Group 11
0000:02:00.0

Significado: La GPU (01:00.0) y su función de audio (01:00.1) comparten un grupo. Eso está bien; normalmente se pasan ambas.

Decisión: Si tu dispositivo objetivo comparte grupo con algo crítico (como el NVMe de arranque), no procedas con passthrough en esa configuración de plataforma.

Tarea 5: Ver qué está vinculado vía sysfs (autoritativo)

cr0x@server:~$ readlink -f /sys/bus/pci/devices/0000:01:00.0/driver
/sys/bus/pci/drivers/nouveau

Significado: El vínculo del kernel es nouveau, independientemente de lo que creas que diga tu configuración.

Decisión: Necesitas evitar que el host lo vincule en el arranque (blacklist + initramfs + ids de vfio-pci).

Tarea 6: Detectar si un dispositivo está “ocupado” por la consola/DRM (GPUs)

cr0x@server:~$ dmesg | egrep -i 'drm|framebuffer|fb0|efi fb|simpledrm' | head -n 12
[    0.623456] simpledrm: initialized
[    1.912345] nouveau 0000:01:00.0: DRM: VRAM: 8192 MiB
[    1.934567] fb0: switching to nouveaufb from simple

Significado: La GPU se convirtió en un dispositivo framebuffer (fb0). Ese es un ancla clásico de “en uso”.

Decisión: Asegura que el host nunca la use para gráficos de consola: vincula a vfio-pci en initramfs; a menudo deshabilita el handoff del framebuffer donde corresponda.

Tarea 7: Comprobar si un servicio del host mantiene los controladores GPU cargados

cr0x@server:~$ systemctl status nvidia-persistenced --no-pager
● nvidia-persistenced.service - NVIDIA Persistence Daemon
     Loaded: loaded (/lib/systemd/system/nvidia-persistenced.service; enabled)
     Active: active (running) since Fri 2025-12-26 08:11:12 UTC; 3h 14min ago
   Main PID: 2211 (nvidia-persiste)
      Tasks: 1
     Memory: 1.8M
        CPU: 2.013s

Significado: El host está gestionando activamente el stack NVIDIA, que peleará con tu passthrough.

Decisión: Desactívalo (y descarga los controladores) si esa GPU es para una VM.

Tarea 8: Detectar módulos del kernel que volverán a reclamar el dispositivo tras ununbind

cr0x@server:~$ modprobe --showconfig | egrep -n 'blacklist (nouveau|amdgpu|nvidia|radeon)' | head
412:blacklist nouveau

Significado: Existe un blacklist para nouveau, pero eso no garantiza que esté en tu initramfs o que no se activen alternativas.

Decisión: Asegura que el blacklist esté presente y luego reconstruye initramfs para que el arranque temprano lo respete.

Tarea 9: Revisar initramfs por los controladores erróneos (el misterio de “¡aún se carga!”)

cr0x@server:~$ lsinitramfs /boot/initrd.img-6.8.12-4-pve | egrep 'nouveau|amdgpu|nvidia' | head
usr/lib/modules/6.8.12-4-pve/kernel/drivers/gpu/drm/nouveau/nouveau.ko
usr/lib/modules/6.8.12-4-pve/kernel/drivers/gpu/drm/drm.ko

Significado: El módulo está dentro de initramfs, así que puede cargarse antes de que tus configuraciones del root filesystem se apliquen.

Decisión: Elimínalo de initramfs mediante la configuración adecuada (blacklist en contexto initramfs) y regenera initramfs.

Tarea 10: Vincular un dispositivo a vfio-pci usando driver_override (quirúrgico, en tiempo de ejecución)

cr0x@server:~$ echo vfio-pci | sudo tee /sys/bus/pci/devices/0000:01:00.0/driver_override
vfio-pci
cr0x@server:~$ echo 0000:01:00.0 | sudo tee /sys/bus/pci/drivers/nouveau/unbind
0000:01:00.0
cr0x@server:~$ echo 0000:01:00.0 | sudo tee /sys/bus/pci/drivers/vfio-pci/bind
0000:01:00.0

Significado: Forzaste el re-vinculado del dispositivo sin reiniciar. Útil para pruebas y recuperación de emergencia.

Decisión: Si esto “funciona”, no te quedes aquí—hazlo persistente en el arranque. Los vínculos en tiempo de ejecución son una gran forma de olvidar lo que cambiaste.

Tarea 11: Validar que el vínculo ahora es VFIO

cr0x@server:~$ lspci -nnk -s 01:00.0
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation Device [10de:2684] (rev a1)
	Subsystem: ASUSTeK Computer Inc. Device [1043:88aa]
	Kernel driver in use: vfio-pci
	Kernel modules: nouveau, nvidia_drm, nvidia

Significado: vfio-pci la posee. Los módulos listados son “disponibles”, no necesariamente cargados.

Decisión: Asegúrate de que el resto de funciones (01:00.1, etc.) también estén vinculadas a VFIO, luego inicia la VM.

Tarea 12: Comprobar la función de audio (porque las GPUs son astutas)

cr0x@server:~$ lspci -nnk -s 01:00.1
01:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:22ba] (rev a1)
	Subsystem: ASUSTeK Computer Inc. Device [1043:88aa]
	Kernel driver in use: snd_hda_intel
	Kernel modules: snd_hda_intel

Significado: La función de audio de la GPU aún está vinculada al host. QEMU puede fallar por esto, y obtendrás “in use” o inestabilidad en el invitado.

Decisión: Vincula 01:00.1 a VFIO también, o pásala explícitamente si la configuración de la VM lo espera. Trata las funciones de la GPU como un conjunto.

Tarea 13: Identificar “holders” desde la perspectiva del controlador (quién depende de ello)

cr0x@server:~$ sudo lsof /dev/nvidia0 2>/dev/null | head
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
Xorg     1880 root   15u   CHR 195,0      0t0  331 /dev/nvidia0

Significado: Si usas el stack propietario NVIDIA, procesos pueden mantener nodos de dispositivo abiertos.

Decisión: Para desvincular, detén el servicio (gestor de sesiones, Xorg, daemon de persistencia). Si estás sin cabeza (headless), no instales stacks de escritorio en un host de passthrough a menos que tengas razón para ello.

Tarea 14: Vigila qué se queja QEMU/Proxmox cuando falla el inicio

cr0x@server:~$ journalctl -u pvedaemon -u pveproxy -u pvestatd -u pve-firewall -u pve-ha-lrm -u pve-ha-crm --since "10 minutes ago" --no-pager | tail -n 12
Dec 26 11:20:41 server pvedaemon[1552]: start VM 120: UPID:server:00003A1B:0001D2A1:676D...:qmstart:120:root@pam:
Dec 26 11:20:42 server pvedaemon[1552]: VM 120 qmp command failed - unable to open /dev/vfio/10: Device or resource busy
Dec 26 11:20:42 server pvedaemon[1552]: start failed: QEMU exited with code 1

Significado: Esto apunta al nodo de grupo IOMMU (/dev/vfio/10) estando ocupado. Algo más tiene ese grupo abierto.

Decisión: Busca otra VM o proceso que use el mismo grupo IOMMU; confirma que no existe un proceso QEMU fantasma; verifica que todas las funciones del grupo se pasen de forma consistente.

Broma #1: VFIO es como los escritorios compartidos de oficina: si alguien dejó su taza en la mesa, técnicamente no puedes sentarte ahí.

Separar dispositivos del host: la manera correcta, no la afortunada

Paso 0: Decide si el host debe usar alguna vez el dispositivo

Hay dos modos legítimos de operación:

  • Dispositivo dedicado a passthrough: El host nunca debe usarlo. Vincula a VFIO en el arranque. Este es el modo sensato para GPUs, controladores USB y NICs destinados a invitados.
  • Dispositivo de passthrough ocasional: El host a veces lo usa, a veces lo pasa. Esto es frágil y atrae “device is in use” porque haces unbind/rebind en tiempo de ejecución. Úsalo solo cuando el hardware es escaso y disfrutas vivir peligrosamente.

Paso 1: Confirma que tu plataforma soporta aislamiento limpio

El aislamiento por grupo IOMMU no es un “gustito”. Es el límite que evita que una VM haga DMA a memoria de otros dispositivos. En un entorno estricto, pasas grupos enteros. En un entorno práctico, al menos entiendes qué estás sacrificando.

Si tu dispositivo comparte un grupo IOMMU con un dispositivo crítico del host, tus opciones son:

  • Mover la tarjeta a otra ranura (otro root port, diferente agrupamiento).
  • Cambiar ajustes de plataforma (a veces “Above 4G decoding”, “Resizable BAR” modifican agrupamientos indirectamente; a veces no).
  • Usar otra placa base/generación de CPU que exponga ACS correctamente.
  • Usar el parche/parametro ACS override (último recurso; advertencias de seguridad y estabilidad).

Paso 2: Hacer persistente el binding VFIO (la parte que la gente hace a medias)

En Proxmox, el enfoque limpio es: establecer parámetros del kernel para IOMMU, cargar módulos VFIO y configurar vfio-pci para reclamar los IDs de dispositivo temprano.

2a) Asegura parámetros del kernel para IOMMU (ejemplo Intel):

cr0x@server:~$ cat /etc/default/grub | egrep 'GRUB_CMDLINE_LINUX_DEFAULT'
GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on iommu=pt"

Significado: La línea de comandos del kernel incluye la habilitación de IOMMU y el modo passthrough para rendimiento (iommu=pt) mientras aún permite el aislamiento VFIO.

Decisión: Si falta, añade los parámetros correctos, luego ejecuta update-grub y reinicia.

cr0x@server:~$ sudo update-grub
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-6.8.12-4-pve
Found initrd image: /boot/initrd.img-6.8.12-4-pve
done

2b) Cargar módulos VFIO en el arranque:

cr0x@server:~$ cat /etc/modules | egrep '^vfio'
vfio
vfio_iommu_type1
vfio_pci
vfio_virqfd

Significado: Los módulos core VFIO estarán disponibles temprano.

Decisión: Si no están presentes, añádelos y reconstruye initramfs para que el arranque temprano los tenga.

2c) Vincular por vendor:device ID con vfio-pci

Crea una configuración modprobe que diga a vfio-pci que reclame los IDs. Ejemplo para una GPU y su función de audio:

cr0x@server:~$ cat /etc/modprobe.d/vfio.conf
options vfio-pci ids=10de:2684,10de:22ba disable_vga=1

Significado: Al cargarse, vfio-pci se vinculará a esos IDs. disable_vga=1 ayuda a evitar conflictos de arbitraje VGA en algunas configuraciones.

Decisión: Si tienes varias GPUs idénticas, considera usar driver_override por dirección PCI en su lugar, para evitar capturar la tarjeta equivocada.

2d) Blacklist de controladores conflictivos del host (y hacerlo para initramfs)

cr0x@server:~$ cat /etc/modprobe.d/blacklist-gpu.conf
blacklist nouveau
blacklist nvidia
blacklist nvidiafb
blacklist rivafb

Significado: Estos módulos no deberían cargarse automáticamente.

Decisión: Si aun los ves en lsmod después del reinicio, se están tirando dentro vía initramfs o dependencias; arregla initramfs a continuación.

2e) Reconstruir initramfs (donde la mayoría de las historias de “lo puse en blacklist” mueren)

cr0x@server:~$ sudo update-initramfs -u -k all
update-initramfs: Generating /boot/initrd.img-6.8.12-4-pve

Significado: El nuevo initramfs incluye tu configuración de VFIO y blacklist.

Decisión: Reinicia y vuelve a comprobar los vínculos. Si sigue mal, revisa otros módulos como simpledrm y ajustes de consola.

Paso 3: Desacoplar limpiamente en tiempo de ejecución (cuando debes)

A veces ya estás en medio de un incidente y un reinicio es caro. El desacoplamiento en tiempo de ejecución es posible, pero es un procedimiento controlado, no echo en sysfs al azar hasta que deje de gritar.

Orden de operaciones para desacople en tiempo de ejecución:

  1. Detén procesos/servicios que usen el dispositivo (gestor de sesiones, daemons de persistencia, servicios de almacenamiento).
  2. Desvincula todas las funciones PCI que se pasarán.
  3. Establece driver_override a vfio-pci para evitar re-vinculado inmediato al controlador antiguo.
  4. Vincula a vfio-pci.
  5. Inicia la VM.

Ejemplo para función VGA de GPU + función de audio:

cr0x@server:~$ sudo systemctl stop nvidia-persistenced
cr0x@server:~$ echo vfio-pci | sudo tee /sys/bus/pci/devices/0000:01:00.0/driver_override
vfio-pci
cr0x@server:~$ echo vfio-pci | sudo tee /sys/bus/pci/devices/0000:01:00.1/driver_override
vfio-pci
cr0x@server:~$ echo 0000:01:00.0 | sudo tee /sys/bus/pci/drivers/nouveau/unbind
0000:01:00.0
cr0x@server:~$ echo 0000:01:00.1 | sudo tee /sys/bus/pci/drivers/snd_hda_intel/unbind
0000:01:00.1
cr0x@server:~$ echo 0000:01:00.0 | sudo tee /sys/bus/pci/drivers/vfio-pci/bind
0000:01:00.0
cr0x@server:~$ echo 0000:01:00.1 | sudo tee /sys/bus/pci/drivers/vfio-pci/bind
0000:01:00.1

Qué puede salir mal: el controlador antiguo se vuelve a vincular inmediatamente; un framebuffer sigue sujetando; el dispositivo no hace reset; o desacoplas una NIC que transporta tu sesión SSH (sí, la gente hace esto).

Broma #2: Desvincular la NIC de gestión sobre SSH es una excelente forma de descubrir cuánto confías en tu consola fuera de banda.

Paso 4: Confirma que Proxmox está pasando la historia completa, no la mitad

En las configuraciones VM de Proxmox, normalmente usas entradas hostpciX. La cantidad de problemas causados por pasar solo la función VGA y olvidar audio, USB-C o una función bridge es impresionante en un triste sentido.

Revisa la configuración de la VM:

cr0x@server:~$ sudo cat /etc/pve/qemu-server/120.conf
agent: 1
bios: ovmf
machine: q35
memory: 16384
name: win11-gpu
ostype: win11
scsihw: virtio-scsi-single
hostpci0: 0000:01:00.0,pcie=1,x-vga=1
hostpci1: 0000:01:00.1,pcie=1

Significado: Ambas funciones se pasan; OVMF + Q35 es típicamente la línea base correcta para passthrough de GPU moderno.

Decisión: Si solo pasas 01:00.0, corrígelo. Si el grupo IOMMU incluye más funciones (como 01:00.2), pásalas también o reconsidera el dispositivo.

Modos de fallo en passthrough de GPU (los sospechosos habituales)

1) La consola del host está usando la GPU (DRM/KMS)

Esta es la causa raíz más común detrás de “in use”. Si la GPU provee salida de consola, el controlador DRM del kernel no la suelta amablemente. A veces puedes desvincularla, pero otras veces obtendrás una consola negra, controlador colgado o tarjeta medio resetada.

Consejo en producción: usa una GPU secundaria barata (o gráficos integrados) para el host, y dedica la GPU de passthrough a VFIO desde el primer milisegundo del arranque.

2) Pasaste la función VGA pero no la de audio

Muchas GPUs son dos dispositivos pegados: VGA y audio. Viven en el mismo grupo IOMMU porque comparten recursos. Si pasas una y no la otra, el host conserva una pieza y tu VM obtiene la otra. Eso no es compartir; es una batalla de custodia.

3) Problemas de reset: la GPU no vuelve después de detener/reiniciar la VM

Algunos dispositivos no implementan un correcto Function Level Reset (FLR), o la plataforma no enruta los resets limpiamente. Síntomas:

  • El primer inicio de VM funciona, el segundo inicia con “device is in use” o el invitado ve Code 43 / error de dispositivo.
  • Los logs del host muestran el dispositivo atascado, o VFIO no puede reinicializarlo.

Qué hacer:

  • Asegúrate de pasar todas las funciones.
  • Verifica si el dispositivo soporta reset; algunos requieren quirks específicos del vendedor.
  • En los peores casos, solo un reinicio del host lo resetea de forma fiable. Planifica capacidad y ventanas de cambio en consecuencia.

4) Se capturó la GPU equivocada con vfio-pci

Vincular por device ID (ids=) es fácil. También es tosco. Si tienes dos GPUs idénticas, el host podría vincular ambas a VFIO y pierdes salida de consola—o vinculas la equivocada y te preguntas por qué la VM no ve la tarjeta que instalaste “ayer”.

En sistemas con múltiples GPUs: vincula por dirección PCI usando lógica driver_override en scripts de arranque, o mantiene los IDs de dispositivo únicos usando una tarjeta de host de otra clase.

HBAs, controladores USB, NICs: clases diferentes, trampas distintas

Passthrough de un HBA (controlador SAS/SATA)

Los HBAs suelen ser candidatos excelentes para passthrough porque se comportan como dispositivos PCI honestos y no necesitan tonterías de consola gráfica. La trampa es más simple: accidentalmente intentas pasar el controlador que provee el almacenamiento del host. Linux objetará, y si fuerzas, el host dejará de ser host.

Tarea: confirma qué dispositivos de bloque están en el controlador que piensas extraer.

cr0x@server:~$ lsblk -o NAME,MODEL,SERIAL,HCTL,TYPE,SIZE | head -n 15
NAME   MODEL           SERIAL     HCTL        TYPE  SIZE
sda    ST12000NM0007   ZHZ0AAAA   1:0:0:0     disk 10.9T
sdb    ST12000NM0007   ZHZ0BBBB   1:0:1:0     disk 10.9T
nvme0n1 Samsung SSD    S6E...     -           disk  1.8T
zd0    -               -          -           disk   50G

Significado: Los valores HCTL sugieren discos en un host SCSI (probablemente un HBA). Si ese HBA es tu pool ZFS, no es candidato a passthrough a menos que el host deje de gestionar ese pool.

Decisión: Pasa por passthrough solo un HBA que no aloje el almacenamiento crítico de Proxmox, o acepta que construyes una arquitectura de VM de almacenamiento y diseña en consecuencia.

Passthrough de un controlador USB

Los controladores USB son populares porque ofrecen comportamiento “real” USB (dongles, cascos VR, UPS). El modo fallo es el agrupamiento IOMMU: tu controlador USB comparte un grupo con otros dispositivos de chipset que necesitas. O pasas el controlador y pierdes el teclado que necesitabas para arreglarlo. La comedia es opcional.

Tarea: identifica el controlador y su grupo.

cr0x@server:~$ lspci -nn | grep -i usb
00:14.0 USB controller [0c03]: Intel Corporation Device [8086:7ae0] (rev 11)
cr0x@server:~$ readlink /sys/bus/pci/devices/0000:00:14.0/iommu_group
../../../../kernel/iommu_groups/2

Significado: El Grupo 2 contiene el controlador USB. Ahora debes verificar qué más hay en el grupo 2.

Decisión: Si el grupo 2 incluye otros elementos esenciales del chipset, no lo pases. Añade una tarjeta USB dedicada en su lugar.

Passthrough de una NIC

El passthrough de NIC es excelente para cargas especiales (firewalls, DPDK, baja latencia). También es cómo te cortas la propia rama si pasas la interfaz de gestión. Siempre ten acceso fuera de banda (IPMI/iKVM) antes de experimentar con passthrough de NICs.

Tarea: mapea la dirección PCI de la NIC al nombre de interfaz en Linux, y confirma que no es tu camino de gestión.

cr0x@server:~$ sudo lshw -class network -businfo | head -n 12
Bus info          Device      Class          Description
pci@0000:03:00.0  eno1        network        Ethernet controller
pci@0000:04:00.0  enp4s0      network        Ethernet controller

Significado: Ahora sabes qué función PCI corresponde a qué interfaz.

Decisión: No pases la interfaz que provee tu sesión SSH actual a menos que tengas un camino OOB probado y un plan de reversión.

Tres microhistorias corporativas (cómo esto falla en la práctica)

Incidente: la suposición errónea sobre el “blacklisting”

Una empresa mediana migró algunas cargas intensivas a un clúster Proxmox. Un nodo tenía una GPU de repuesto destinada a una VM Windows que ejecutaba un dongle de licencia CAD y algo de render con GPU. El ingeniero hizo lo que todos hacen al principio: puso en blacklist a nouveau, configuró los IDs de vfio-pci, reinició y celebró cuando lspci mostró VFIO.

Dos semanas después, tras una actualización del kernel, la VM dejó de arrancar. Proxmox reportó que la GPU estaba “in use”. El ingeniero asumió que la configuración “debió revertirse” y la re-aplicó. Siguió roto. Revirtieron el kernel. Funcinó. Culparon al nuevo kernel y abrieron un ticket.

La causa real fue aburrida: el initramfs actualizado contenía de nuevo el controlador GPU y lo cargó temprano. El archivo blacklist existía, pero no se aplicó en el contexto de initramfs debido a cómo fue generado. La GPU fue reclamada antes de que Proxmox tuviera oportunidad. El reinicio había estado escondiendo el problema hasta que la actualización cambió el tiempo de arranque.

La solución no fue heroica. Estandarizaron en: confirmar la presencia del módulo en initramfs, regenerar initramfs tras cambios y verificar vínculos después de cada actualización del kernel. La VM volvió a ser aburrida, que es el estado correcto para infraestructura.

Optimización que salió mal: rebind en tiempo de ejecución para evitar reinicios

Otra organización tenía una sola GPU en un nodo que a veces servía un panel de monitoreo en el host (gráficos de consola) y a veces se pasaba a una VM para experimentos ML cortos. No querían reinicios porque el nodo alojaba otras VMs. Así que crearon un script: detener gestor de sesiones, unbind GPU, bind a VFIO, iniciar VM; luego revertirlo.

Funcionó en pruebas. Luego metió patrones reales de uso: la VM se bloqueaba, reiniciaba rápido y la GPU no se reseteaba limpiamente. El script “re-vinculaba” el dispositivo, pero el hardware estaba en mal estado. A veces el host colgaba al recargar el controlador DRM. A veces la VM arrancaba pero veía una GPU rota. A veces Proxmox se negaba con “device is in use” porque un proceso QEMU obsoleto mantenía abierto el nodo del grupo por unos segundos más de lo esperado.

La lección del postmortem fue dolorosa y predecible: el rebind en tiempo de ejecución es un impuesto a la fiabilidad. Compraron una GPU barata de bajo consumo para la consola del host y dedicaron la buena a passthrough solamente. De repente no había script, ni danza, ni misterio. La “optimización” había estado evitando gasto en hardware a cambio de tiempo de ingeniería y presupuesto de incidentes.

Práctica aburrida pero correcta que salvó el día: validación a nivel de grupo antes de cambios

Un equipo de servicios financieros ejecutaba hosts Proxmox con control de cambios estricto. Tenían un ítem de checklist que fastidiaba a todos: antes de añadir o modificar cualquier asignación hostpci, registraban la membresía completa del grupo IOMMU y confirmaban que nada había cambiado tras actualizaciones de firmware.

Un trimestre, una actualización rutinaria de BIOS cambió el comportamiento de bifurcación PCIe en una subset de nodos. El grupo IOMMU de la GPU empezó a incluir un puente upstream y un controlador USB que antes estaban separados. En los nodos que se actualizaron primero, el passthrough empezó a fallar con “device is in use” y, peor, problemas USB intermitentes en el host.

Como el equipo tenía snapshots antes/después de los grupos como parte del ticket de cambio, el diagnóstico tomó minutos. No perdieron horas desvinculando controladores y culpando a Proxmox. Revirtieron la BIOS en los nodos afectados, planificaron un cambio de layout hardware y evitaron una caída mayor. Todos odiaban el checklist hasta que rindió cuentas, que es como la operación fiable suele funcionar.

Errores comunes: síntoma → causa raíz → arreglo

1) Síntoma: “device is in use” justo al iniciar la VM, siempre

Causa raíz: Controlador del host vinculado (controlador GPU, USB, NIC) en lugar de vfio-pci.

Arreglo: Verifica con lspci -nnk y readlink /sys/bus/pci/devices/.../driver. Configura vfio-pci ids=, blacklist de controladores conflictivos, reconstruye initramfs, reinicia.

2) Síntoma: funciona después de un unbind manual, pero falla tras reiniciar

Causa raíz: Tus cambios no se aplican lo suficientemente temprano; initramfs carga el controlador antes de que la configuración del rootfs esté activa.

Arreglo: Revisa lsinitramfs por el módulo problemático, actualiza initramfs, confirma que los módulos VFIO están incluidos temprano.

3) Síntoma: GPU pasada, pero el invitado no tiene salida o errores de controlador

Causa raíz: Funciones compañeras faltantes (audio, USB-C), firmware equivocado (SeaBIOS vs OVMF), o desajuste de x-vga.

Arreglo: Pasa todas las funciones en el grupo IOMMU; usa OVMF + Q35 para GPUs modernas; añade x-vga=1 cuando corresponda.

4) Síntoma: el primer inicio de VM funciona, el segundo falla sin reinicio del host

Causa raíz: Problemas de reset del dispositivo (sin FLR, enrutamiento de reset defectuoso), o propiedad de grupo por un proceso QEMU persistente.

Arreglo: Confirma que no hay procesos QEMU sobrantes; revisa logs; considera reiniciar el host como el único reset fiable. Prefiere GPUs de grado servidor o dispositivos conocidos por resetear bien cuando importa el uptime.

5) Síntoma: “/dev/vfio/X: Device or resource busy”

Causa raíz: Otro proceso/VM mantiene abierto el nodo del grupo IOMMU, a menudo porque pasaste una función diferente del mismo grupo en otro sitio.

Arreglo: Asegúrate de no dividir un grupo entre VMs; detén la otra VM; vuelve a comprobar la membresía del grupo.

6) Síntoma: caída de red justo después de vincular la NIC a VFIO

Causa raíz: Pasaste la NIC de gestión, o systemd-networkd/ifupdown perdió la interfaz.

Arreglo: Usa acceso OOB, revierte el binding, rediseña con una NIC dedicada para passthrough y una NIC separada de gestión o bond.

7) Síntoma: el host se congela cuando desvinculas el controlador GPU

Causa raíz: La GPU sigue siendo el framebuffer de consola activo o la usa un compositor/gestor de sesiones.

Arreglo: No hagas detach de GPU en tiempo de ejecución en un host que use esa GPU para la consola. Usa una GPU distinta para los gráficos del host o funciona sin cabeza.

Listas de verificación / plan paso a paso

Checklist A: Dispositivo dedicado a passthrough (recomendado)

  1. Elige el dispositivo y lista todas las funciones (lspci -nn para 01:00.0, 01:00.1, etc.).
  2. Revisa los grupos IOMMU y confirma que el aislamiento del grupo es aceptable.
  3. Habilita IOMMU en BIOS y parámetros del kernel (intel_iommu=on o amd_iommu=on).
  4. Carga módulos VFIO temprano vía /etc/modules.
  5. Vincula IDs de dispositivo a vfio-pci usando /etc/modprobe.d/vfio.conf.
  6. Pon en blacklist controladores host conflictivos que reclamarían el dispositivo.
  7. Reconstruye initramfs y reinicia.
  8. Verifica el vínculo tras el arranque usando lspci -nnk y la ruta del controlador en sysfs.
  9. Configura la VM para pasar todas las funciones requeridas; usa OVMF/Q35 para GPUs.
  10. Prueba ciclos de stop/start (al menos 5 ciclos) para detectar problemas de reset antes que los usuarios.

Checklist B: Desacople en tiempo de ejecución (usar con moderación)

  1. Confirma que tienes acceso a consola si pierdes el dispositivo (especialmente NICs).
  2. Detén servicios que podrían tocar el dispositivo (gestor de sesiones, daemons de persistencia, servicios de almacenamiento).
  3. Desvincula todas las funciones que se pasarán.
  4. Establece driver_override a vfio-pci para evitar re-vinculado inmediato.
  5. Vincula a vfio-pci.
  6. Inicia la VM y confirma que la propiedad de /dev/vfio/* es correcta.
  7. Al terminar, revierte con cuidado: detén la VM, desvincula de VFIO, borra driver_override, vincula de nuevo al controlador del host, reinicia servicios.

Plan de reversión (escríbelo antes de empezar)

  • Si el host pierde red: usa la consola fuera de banda, revierte el binding VFIO, reinicia si es necesario.
  • Si la GPU queda bloqueada: reinicio del host es el reset duro; planifícalo.
  • Si los grupos IOMMU cambiaron tras una actualización: revierte firmware/kernel o reubica tarjetas; no luches contra la física.

Preguntas frecuentes (FAQ)

1) ¿Por qué Proxmox dice “device is in use” incluso cuando no hay VM en ejecución?

Porque el controlador del kernel del host la está usando. “In use” suele significar que un controlador está vinculado o que el nodo del grupo IOMMU está mantenido abierto. Revisa lspci -nnk y /sys/bus/pci/devices/.../driver.

2) ¿Es suficiente poner módulos en blacklist?

No de forma fiable. Si el controlador está presente en initramfs, puede cargarse antes de que tu blacklist se aplique desde el root filesystem. Reconstruye initramfs y verifica con lsinitramfs.

3) ¿Necesito pasar la función de audio de la GPU?

Normalmente sí. A menudo está en el mismo grupo IOMMU y comparte el dispositivo físico. Dejarla vinculada a snd_hda_intel en el host es una causa común de “in use” y problemas de controlador en el invitado.

4) ¿Qué indica específicamente “/dev/vfio/10 busy”?

Que el grupo IOMMU 10 ya está abierto por algún proceso—a menudo otra instancia QEMU, a veces una que quedó colgada. También puede ocurrir si divides funciones del grupo entre VMs. Soluciona asegurando que un grupo vaya a una VM (o a ninguna).

5) ¿Puedo hot-unplug un dispositivo PCI del host de forma segura?

Puedes desvincular y volver a vincular controladores, pero “seguro” depende de la clase del dispositivo y si la plataforma soporta reset limpio. Las GPUs son las menos cooperativas; HBAs y NICs suelen comportarse mejor.

6) ¿Debería usar ACS override?

Sólo si entiendes la compensación: puedes estar debilitando las garantías de aislamiento. En labs domésticos es común; en entornos regulados suele ser una excepción de seguridad que requiere aprobación explícita.

7) ¿Por qué se rompe después de actualizaciones del kernel o BIOS?

Las actualizaciones cambian el comportamiento de los controladores, el contenido de initramfs, el orden de enumeración PCIe y el agrupamiento IOMMU. Trata los hosts con passthrough como sistemas donde las actualizaciones deben incluir una validación post-cambio: bindings, grupos y ciclos de start/stop de VM.

8) ¿Cómo evito vincular la GPU equivocada cuando tengo dos tarjetas idénticas?

Evita depender únicamente de vfio-pci ids=. Usa direcciones PCI con lógica driver_override en el arranque, o asegúrate de que el host use una GPU distinta (integrada o barata) para poder vincular por ID sin daños colaterales.

9) ¿Cuál es la diferencia entre “Kernel modules” y “Kernel driver in use” en la salida de lspci?

“Kernel driver in use” es el vínculo activo. “Kernel modules” son controladores que podrían vincularse según modalias. Te interesa “in use”.

10) ¿Necesito OVMF para passthrough de GPU?

Para la mayoría de GPUs modernas y invitados modernos, sí. OVMF (UEFI) con Q35 es la base más predecible. SeaBIOS puede funcionar en casos específicos pero añade complejidad evitable.

Conclusión: pasos siguientes para evitar sorpresas a las 2 a.m.

El error “device is in use” no es aleatorio. Te dice que el host aún posee el hardware. Tu trabajo es hacer la propiedad inequívoca: aislamiento IOMMU correcto, binding VFIO persistente en initramfs y configuraciones de VM que pasen todas las funciones requeridas juntas.

Pasos prácticos siguientes:

  1. Elige un dispositivo de passthrough y haz un inventario completo de sus funciones y membresía del grupo IOMMU.
  2. Haz persistente el binding VFIO (por IDs o por dirección), reconstruye initramfs y verifica tras reiniciar.
  3. Ejecuta ciclos repetidos de parada/arranque de VM para detectar problemas de reset antes que los usuarios.
  4. Escribe un plan de reversión que asuma que en algún momento acabarás desvinculando lo equivocado—porque pasará.

Si haces que el passthrough sea aburrido, lo hiciste bien. Lo aburrido escala.

← Anterior
Glide vs Direct3D: la guerra de APIs que decidió el futuro del gaming
Siguiente →
MySQL vs PostgreSQL: la elección honesta de BD para sitios web (según cuellos de botella reales)

Deja un comentario