Problema

En entornos de virtualización con GPU passthrough es frecuente observar dos síntomas que aparecen de forma intermitente:

  1. Arranques de la VM mucho más lentos de lo habitual (30 s → 80 s o más).
  2. Congelación de la consola noVNC (y del thumbnail de Proxmox) en una única línea de texto durante varios segundos, mientras que la VM sigue funcionando y el acceso por SSH permanece operativo.

Estos síntomas aparecen solo cuando la GPU dedicada está conectada a la VM; si se arranca sin la tarjeta, el tiempo de boot vuelve a la normalidad y la consola noVNC nunca se bloquea. La aleatoriedad es típica: el mismo guest, mismo kernel y misma configuración pueden producir un arranque “bueno” o “malo” sin que el administrador pueda predecir cuál será.

Causa

El origen está en la emulación del VGA estándar (std) de QEMU. Cuando la VM arranca, QEMU reserva un framebuffer lineal de 16 MiB y lo mapea como RAM accesible directamente por el guest. Mientras ese mapeo está activo, las escrituras de la consola son simples accesos a memoria y no generan sobrecarga.

Durante el proceso de bind del driver de la GPU pasante (por ejemplo i915 para tarjetas Intel), QEMU ejecuta una transición interna de su state machine VGA. En ciertos boots la máquina de estados cae en modo legacy y elimina el mapeo del framebuffer lineal, dejando solo la zona de 64 KB en 0xa0000. Cada actualización de la consola pasa entonces a ser una operación de MMIO emulada, que QEMU procesa a ~160 000 escrituras por segundo. Redibujar una pantalla de 1280 × 800 × 4 bytes requiere ~4 MiB, lo que se traduce en un retardo de ~12 s por refresco. El resultado es la congelación observada y el aumento del tiempo total de arranque.

Factores que favorecen la caída al modo legacy:

  • Orden de bind del driver: si el driver de la GPU se enlaza justo cuando QEMU está cambiando de modo VGA, la transición puede fallar.
  • Versión del QEMU/KVM: el bug está presente en varias versiones de la rama 11.x usadas por Proxmox 9.x.
  • Presencia de MTRRs o parámetros como i915.disable_display=1: no evitan la condición porque el problema está en la capa de emulación, no en el driver del guest.
  • Uso de vga: virtio: introduce un DRM interno que puede bloquear el arranque antes de que la GPU se active, empeorando la situación.

Solución

La solución consiste en eliminar la emulación VGA estándar y sustituirla por un dispositivo de pantalla lineal que nunca entra en modo legacy: bochs-display. Este dispositivo expone únicamente un framebuffer lineal, sin registros VGA heredados ni máquina de estados. El guest Linux ya incluye el driver bochs-drm, por lo que no se requieren cambios adicionales en el sistema invitado.

Pasos generales

  1. Desactivar la VGA estándar (-vga none).
  2. Añadir bochs-display mediante la opción -device bochs-display.
  3. Re‑exponer el socket VNC porque Proxmox crea el socket VNC solo cuando hay una VGA activa. Se hace añadiendo -vnc unix:/var/run/qemu-server/<VMID>.vnc,password=on a la línea de argumentos.

Con esta configuración el framebuffer permanece mapeado durante todo el arranque, eliminando los 12 s de “congelación” y reduciendo el tiempo total de boot a valores normales (≈15 s en el caso probado).

Alternativas

  • Desactivar la consola por completo (-vga none sin bochs-display). La VM sigue arrancando rápido, pero se pierde la consola noVNC. Útil solo si se dispone de acceso SSH permanente.
  • Forzar la GPU a no usar el driver de pantalla (i915.disable_display=1). No soluciona el problema de la consola, pero evita que el driver intente usar la tarjeta para salida de vídeo, reduciendo la probabilidad de que el bind interfiera con la VGA.
  • Actualizar QEMU a una versión que incluya el parche del state machine. Hasta la fecha del post, el bug persiste en la rama 11.x, por lo que la solución de bochs-display sigue siendo la más fiable.

Cuándo aplicar esta solución

Aplicable cuando se cumplen los siguientes criterios:

  • La VM usa GPU passthrough (Intel, AMD o NVIDIA) y la tarjeta no tiene monitor conectado.
  • Se observa incremento aleatorio del tiempo de arranque y/o congelación de la consola noVNC.
  • El guest sigue respondiendo por SSH, lo que indica que el problema está en la capa de consola, no en el propio sistema operativo.

No aplicar si:

  • La VM necesita una salida VGA tradicional para dispositivos que sólo aceptan BIOS VGA (por ejemplo, sistemas operativos antiguos sin soporte DRM).
  • No se usa noVNC ni la consola web de Proxmox (por ejemplo, entornos de automatización sin interacción humana). En ese caso, simplemente desactivar la VGA es suficiente.

Código

# Reemplaza <VMID> por el ID de tu máquina virtual
qm set <VMID> -vga none \
    -args '-device bochs-display -vnc unix:/var/run/qemu-server/<VMID>.vnc,password=on'

Para volver a la configuración original:

qm set <VMID> -vga std -delete args

Verificación

  1. Reinicia la VM y mide el tiempo de boot con systemd-analyze time. Debería estar cerca de los 15 s habituales.
  2. Observa la consola noVNC durante los primeros 30 s. No debe haber congelaciones ni frames estáticos.
  3. Comprueba el mapeo del framebuffer desde el monitor QEMU:
    qm monitor <VMID> info mtree -f | grep vga.vram
    
    Debería aparecer una línea con size=0x1000000 (16 MiB). No debe quedar solo la entrada de 64 KB.
  4. Ejecuta una carga ligera (por ejemplo while true; do date >> /tmp/heartbeat; sleep 0.5; done &) y verifica que el archivo se actualiza durante todo el arranque, confirmando que el guest no está bloqueado.

Notas adicionales

  • Persistencia del argumento: Proxmox no muestra bochs-display en el menú de pantalla, por lo que la línea -args debe mantenerse en la configuración de la VM. Tras una actualización mayor de Proxmox, revisa que la opción siga presente.
  • Tarjetas Intel Arc (A310/A380) pueden quedar en un estado inconsistente al intentar leer el ROM de expansión a través de VFIO. Añadir rombar=0 a la línea hostpci0 evita que OVMF acceda al ROM durante el arranque y elimina arranques que se quedan en pantalla negra.
  • Reinicios en frío del host (desconectar la alimentación) son a veces necesarios para limpiar completamente el estado de la GPU después de un fallo de FLR (Function Level Reset). Un simple reboot del host no siempre restablece la tarjeta.
  • Monitoreo de contadores KVM (/sys/kernel/debug/kvm/*) puede ayudar a confirmar que las escrituras MMIO han desaparecido después de aplicar bochs-display.
  • Reportar upstream: el comportamiento del state machine VGA está ya reportado a QEMU; aportar logs de info mtree y contadores de KVM ayuda a acelerar el parche oficial.