Por qué querrías uno
QEMU puede emular una controladora NVMe de verdad — no un dispositivo paravirtual que necesita un driver que tú suministras, sino una controladora NVMe PCIe que un invitado reconoce como un SSD normal y conduce con el soporte NVMe que ya tiene.
Ese es todo el atractivo, y vale más de lo que suena.
Linux lleva años con un driver nvme de serie. Windows entrega stornvme desde Windows 8.1 y Server 2012 R2. Así que un invitado arranca, enumera una controladora NVMe PCIe, carga su propio driver y encuentra un disco. Sin ISO de VirtIO, sin inyección de drivers en el momento de la instalación, y sin pantalla de «no se han encontrado unidades» a mitad de un instalador de Windows.
Cualquiera que se haya quedado mirando esa pantalla, con la ISO de VirtIO montada y el instalador todavía insistiendo en que no hay discos, verá el atractivo de inmediato.
La segunda razón es que se comporta como NVMe hasta arriba. nvme-cli funciona. Los namespaces son reales. Los formatos LBA, los bytes de metadatos y la información de protección son todos configurables. Lo que lo hace un sitio muy bueno para practicar las operaciones que no deberías estar practicando en hardware que guarda datos.
Cómo añadir uno
Proxmox no tiene casilla en la GUI ni clave de configuración para esto. Es un dispositivo QEMU en bruto, así que va en args: en /etc/pve/qemu-server/<vmid>.conf.
La documentación de QEMU da el par mínimo: un disco de respaldo sin interfaz, y la controladora que lo consume. Editado directamente en /etc/pve/qemu-server/<vmid>.conf, sin comillas:
args: -drive file=/var/lib/vz/images/100/nvm.img,if=none,id=nvmidentifier -device nvme,serial=LAB-NVME-01,drive=nvmidentifier
if=none importa: le dice a QEMU que no adjunte el disco a una controladora por defecto, porque la línea -device nvme va a reclamarlo. El id= del disco y el drive= del dispositivo tienen que coincidir. Ese emparejamiento es lo que une las dos mitades.
El serial= es obligatorio; QEMU se niega a arrancar la VM sin uno. Elige algo que reconozcas, porque es exactamente lo que el invitado devuelve en nvme list y smartctl, y «cuál de estas cuatro unidades virtuales idénticas es cuál» es una pregunta que acabarás haciéndote.
Entrecomillar: la parte que pilla a todos
Si entrecomillas esa cadena depende de dónde la estés escribiendo, y equivocarte de sentido es la razón más común de que uno de estos falle al primer intento.
Proxmox almacena el valor de args: y luego lo parte con Text::ParseWords::shellwords. Así que en el fichero de configuración, las comillas se respetan y se quitan. Una cadena entrecomillada por completo se convierte en un único argumento:
# WRONG in the config file — collapses to one argv element QEMU cannot parse
args: "-drive file=…,if=none,id=nvmidentifier -device nvme,serial=…,drive=nvmidentifier"
Pasada por shellwords, eso da exactamente un elemento. Sin comillas, la misma línea da los cuatro que QEMU de verdad necesita: -drive, su bloque de parámetros, -device, su bloque de parámetros.
En la línea de comandos es al revés, porque ahí estás entrecomillando para tu shell, no para Proxmox. Aquí las comillas son obligatorias, y lo que se almacena en la configuración es el valor sin comillas:
qm set 100 --args "-drive file=/var/lib/vz/images/100/nvm.img,if=none,id=nvmidentifier -device nvme,serial=LAB-NVME-01,drive=nvmidentifier"
Los dos son correctos. Solo que no son intercambiables. Si construyes la línea con qm set, comprueba el resultado con qm config 100 después y lo verás almacenado desnudo. Esa es la forma que quiere el fichero de configuración.
Crea la imagen de respaldo primero si no existe:
qemu-img create -f raw /var/lib/vz/images/100/nvm.img 32G
Más de un namespace
Para cualquier cosa más allá de un solo disco, separa la controladora de sus namespaces:
-device nvme,id=nvme-ctrl-0,serial=deadbeef
-drive file=nvm-1.img,if=none,id=nvm-1
-device nvme-ns,drive=nvm-1
Los identificadores de namespace se asignan desde el 1 hacia arriba automáticamente. Esta es la configuración que hace el dispositivo genuinamente útil para aprender, porque la gestión de namespaces es la parte de NVMe que la mayoría de la gente nunca llega a tocar.
Un namespace virtual 4Kn
El namespace toma las propiedades habituales de tamaño de bloque, y QEMU deriva el tamaño de datos LBA directamente de ellas. hw/nvme/ns.c calcula el exponente de formato como ds = 31 - clz32(ns->blkconf.logical_block_size). Así que esto te da un namespace 4K-nativo como es debido:
-device nvme-ns,drive=nvm-1,logical_block_size=4096,physical_block_size=4096
El dispositivo de namespace también acepta ms para bytes de metadatos por LBA, mset para LBA extendidos, y pi y pif para el tipo de información de protección y el formato de guarda.
Eso es un laboratorio completo para todo lo de el artículo de 4Kn y 512e — bloques lógicos de 512 bytes frente a 4096 bytes, formatos con metadatos, T10-PI — en un dispositivo que puedes destruir tan a menudo como quieras.
Comprueba que quedó
Desde dentro del invitado:
lsblk -o NAME,MODEL,SIZE,LOG-SEC,PHY-SEC
nvme list
nvme id-ns -H /dev/nvme0n1 | grep -i "lbaf\|data size"
Deberías ver un namespace NVMe real, con los tamaños de bloque que pediste.
Qué renuncias
Tres cosas, y las dos primeras no son compromisos de rendimiento. Son eliminaciones de capacidad. Conócelas antes de poner nada en el dispositivo.
1. La migración en vivo queda fuera
Esto no es una limitación de Proxmox ni un descuido. QEMU declara el dispositivo inmigrable en el propio modelo del dispositivo. De hw/nvme/ctrl.c en QEMU 10.2:
static const VMStateDescription nvme_vmstate = {
.name = "nvme",
.unmigratable = 1,
};
Tres líneas, y la del medio es toda la historia. La controladora no tiene estado de migración, así que QEMU rechaza la migración en vez de intentarla. Ese es el fallo correcto. Consigues un error, no un invitado que reanuda en otro nodo con un disco confundido.
Hay una segunda razón, independiente, de que no pueda funcionar: Proxmox no sabe que el disco existe. Aunque QEMU pudiera mover el estado del dispositivo, nada en la lógica de migración de PVE dispondría que el volumen de respaldo estuviera disponible en el destino.
Conviene vigilarlo, sin embargo: la rama de desarrollo de QEMU ha reemplazado el flag general por una función nvme_set_migration_blockers() que permite la migración y la bloquea solo para funciones concretas. Más de un namespace, por ejemplo, donde el comentario señala «we don’t handle this in migration code yet» — todavía no lo manejamos en el código de migración. Eso no ha aparecido en una release hasta el 10.2 incluido, así que no te ayuda hoy, pero esta restricción parece probable que se ablande. Comprueba tu propia versión de QEMU en vez de fiarte de un artículo.
2. Las copias de Proxmox no lo verán
vzdump y Proxmox Backup Server hacen copia de los volúmenes que aparecen en la configuración de la VM como discos — scsi0, virtio0, y demás. Un disco adjuntado a través de args: no es uno de esos. Es un dispositivo QEMU en bruto del que PVE no sabe nada.
Así que la copia corre, informa de éxito, y no contiene el dispositivo.
Ese modo de fallo es peor que un error, porque nada te lo dice. Lo mismo aplica en todo: sin instantáneas de PVE, sin redimensionar el disco desde la GUI, sin Mover disco, sin contabilidad en la vista de almacenamiento. Si creaste el volumen a través de PVE y luego lo desadjuntaste, puede que PVE tampoco lo limpie. Un huérfano esperando a confundir a alguien más adelante.
Si van a vivir datos en uno de estos, hazles copia desde dentro del invitado, y anota en algún sitio que el hipervisor no lo está cubriendo.
3. No es más rápido que VirtIO SCSI
Este sorprende a la gente, porque «NVMe» se lee como una función de rendimiento. Aquí no lo es.
VirtIO SCSI y VirtIO block son paravirtuales: el driver del invitado y el hipervisor comparten un búfer en anillo diseñado para exactamente este trabajo, y el invitado sabe que está hablando con un hipervisor.
La controladora NVMe emulada es lo opuesto por diseño. Presenta registros NVMe reales, así que el invitado la programa como si fuera hardware. Cada escritura de doorbell es un acceso MMIO que se atrapa en el hipervisor. Correcto, y más caro por IO que poner un descriptor en un anillo.
La propia documentación de QEMU es franca también sobre las asperezas del dispositivo: la fusión de interrupciones «is not supported and is disabled by default» — no se soporta y está desactivada por defecto — y los números de contabilidad en la página de log SMART/Health «are reset when the device is power cycled» — se reinician cuando se apaga y enciende el dispositivo.
Nada de eso lo hace lento en términos absolutos. Es perfectamente usable. Solo significa que nunca deberías elegirlo esperando más rendimiento del que te da VirtIO SCSI. Elígelo por el driver, o por la semántica NVMe.
Dónde se gana de verdad su sitio
- Instalar un invitado sin medios VirtIO. Un instalador de Windows que no puede ver un disco VirtIO SCSI verá uno NVMe, porque el driver ya está en la imagen. Instala sobre él, y luego decide si cambiar a VirtIO después.
- Appliances e imágenes que no controlas. Cualquier cosa entregada como una imagen fija que carece de drivers VirtIO, y que preferirías no reconstruir.
- Aprender y trabajo de laboratorio.
nvme format --lbaf, la creación y adjunción de namespaces, los metadatos y la información de protección — las operaciones que son destructivas y dependientes del fabricante en hardware real son gratis aquí. Esta es la forma más segura de construir la memoria muscular antes de tocar un disco que importa. - Reproducir la topología de otro. Si estás depurando la disposición NVMe de un cliente, una controladora emulada con namespaces y tamaños de bloque coincidentes es un bucle mucho más rápido que pedir prestado su hardware.
Qué usar en su lugar en producción
Para una VM que necesita rendimiento, funciones de PVE, y una vida tranquila: VirtIO SCSI single, con iothread=1, discard=on y ssd=1, en cache=none. Ese es el arreglo que mantiene funcionando la migración en vivo, las copias, las instantáneas y la vista de almacenamiento, todo.
Para una VM que necesita ese último puñado de por ciento y puede renunciar a esas funciones a propósito, la respuesta no es un dispositivo NVMe emulado. Es passthrough real, con sus propios compromisos duros, cubierto en el artículo del impuesto del IOMMU.
El dispositivo NVMe emulado no está en ninguno de los dos bandos. Por eso, es una herramienta de compatibilidad y de laboratorio, y es muy bueno siendo eso.
Úsalo para el trabajo en el que es bueno y no te fallará. Pídele que sea una función de rendimiento y te fallará muy rápido.
Referencias
- QEMU — emulación NVMe — la sintaxis
-drive/-device nvme,nvme-nspara varios namespaces, los parámetros de namespacems/mset/pi/pif, y las limitaciones declaradas sobre fusión de interrupciones y contabilidad SMART - Fuente de QEMU —
hw/nvme/ctrl.c— la declaraciónnvme_vmstatecon.unmigratable = 1en la release 10.2 - Fuente de QEMU —
hw/nvme/ns.c— el namespace derivando su formato LBA delogical_block_size - Proxmox VE — copia y restauración — qué cubre
vzdump, y las opciones de copia por volumen que existen para los discos que PVE gestiona - Proxmox VE — máquinas virtuales Qemu/KVM — VirtIO SCSI,
iothread,discardy las opciones de disco soportadas