Qué es el MPS

Los dispositivos PCIe transfieren datos en paquetes llamados paquetes de la capa de transacción (TLP). Cada TLP tiene una cabecera y un payload. El tamaño máximo de ese payload es el MaxPayloadSize (MPS).

El MPS se negocia entre un dispositivo y su puente aguas arriba durante el entrenamiento del enlace. El valor negociado es el menor entre lo que admite el dispositivo y lo que permite el puente. Cada puente y conmutador en el camino entre el dispositivo y el complejo raíz tiene su propia capacidad de MPS. El MPS final de cualquier dispositivo lo fija el punto más estrecho de la cadena.

Los valores de MPS habituales son 128, 256 y 512 bytes. Algunos dispositivos admiten 1024 o incluso 4096 bytes, pero en la práctica 256 o 512 es lo típico para controladoras NVMe y tarjetas de red. Las GPU suelen admitir 256 bytes.

Por qué importa

Un MPS mayor significa menos paquetes para la misma cantidad de datos. Una E/S de 4 KB transferida con MPS 128 necesita 32 TLP. La misma transferencia con MPS 512 necesita 8 TLP.

Cada TLP acarrea sobrecarga de protocolo — la cabecera, el CRC, el entramado. Menos TLP significa menos sobrecarga de protocolo por byte transferido. A alto rendimiento, esa diferencia es medible. No dramática. Pero real. Y es la misma sobrecarga, pagada en cada paquete.

El MPS también afecta a lo bien que se aprovecha el enlace PCIe. Payloads más pequeños hacen que el enlace pase más tiempo en cabeceras respecto a datos. Payloads más grandes desplazan la proporción hacia datos útiles.

La misma carga útil de 4 KB cuesta 32 cabeceras de paquete con MPS 128 y 8 con MPS 512MPS 128 — una transferencia de 4 KB se vuelve 32 TLP32 cabeceras × ≈24 B — ≈16 % de los bytes en el cable es sobrecarga de protocoloMPS 512 — los mismos 4 KB se vuelven 8 TLP8 cabeceras × ≈24 B — ≈4 % de los bytes en el cable es sobrecarga de protocolocabecera, número de secuencia, CRC y entramadocarga útilAmbas tiras llevan los mismos 4 KB. Una carga útil mayor no mueve más datos —pasa menos del enlace describiéndolos. La ganancia medida es pequeña.
Ambas tiras llevan los mismos 4 KB. Las barras macizas son las cabeceras por paquete — con MPS 128 hay 32, con MPS 512 solo 8.

El impacto en la latencia es menor que en el rendimiento. Una sola lectura de 4 KB con MPS 128 frente a 512 no mostrará una diferencia de latencia que merezca medirse, porque los TLP van en cauce. Pero fuerza muchas IOPS con muchas transferencias en vuelo y la menor sobrecarga de un MPS mayor se acumula.

El problema bajo passthrough

En hardware nativo, la BIOS fija el MPS durante el POST según la topología PCIe. La BIOS de un servidor moderno suele fijar el MPS al máximo que admite la topología, normalmente 256 o 512 bytes.

Bajo QEMU, el complejo raíz del chipset Q35 virtual tiene su propia capacidad de MPS. Por defecto, presenta un MPS bajo. La política de MPS por defecto del kernel de Linux (pcie_bus_default) fija el MPS de cada dispositivo para que coincida con su puente padre, lo que en una topología virtual significa el valor por defecto del complejo raíz de QEMU. A menudo 128 bytes.

Como tal, un dispositivo capaz de payloads de 512 bytes corre a 128 porque el complejo raíz virtual fijó el techo.

El MPS lo fija el punto más estrecho del camino, que bajo QEMU es el complejo raíz virtualHardware nativo — la BIOS fija el MPS según la topología realNVMeadmite 512Conmutador PCIepermite 512Complejo raízpermite 512MPS = 512 Bel más pequeño del caminoPasado a una VM — el complejo raíz virtual es ahora el punto más estrechoNVMeadmite 512Complejo raíz virtual QEMU Q35presenta 128 por defectoMPS = 128 Bcapacidad del dispositivo sin usarKernel del host con pci=pcie_bus_perfNVMeadmite 512cada dispositivo al máximo de su bus padreMRRS elevado en consecuenciaMPS = 512 Bfijado en el host, no en el invitado
El MPS es el valor más pequeño del camino. Pasar un dispositivo por passthrough inserta el complejo raíz virtual en ese camino, y su conservador valor por defecto se convierte en el techo de todos.

Cómo arreglarlo — lado del host

Dile al kernel que fije el MPS al máximo que admite el bus padre de cada dispositivo:

# Add to GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub
pci=pcie_bus_perf

Esto fija el MPS de cada dispositivo al mayor valor que permite su bus padre. También fija el MRRS (Max Read Request Size) en consecuencia. El código fuente del kernel dice que se asegura de que el MPS de un dispositivo no sea mayor que el de su padre. Eso mantiene la cadena coherente a la vez que consigue los mejores tamaños de transferencia.

Tras reiniciar, verifica el nuevo MPS:

# Check MPS on a specific device
lspci -vv -s XX:00.0 | grep -i "MaxPayload"

Deberías ver MaxPayload 256 bytes o MaxPayload 512 bytes en vez del 128 por defecto.

Las otras opciones del kernel

El kernel ofrece cuatro políticas de MPS, cada una fijada con el parámetro de arranque pci=:

pcie_bus_tune_off — no tocar el MPS en absoluto. Usa lo que fijó la BIOS. En hardware nativo con una buena BIOS, esto suele estar bien. Bajo QEMU, la BIOS es OVMF o SeaBIOS, que puede no optimizar el MPS.

pcie_bus_default — el valor por defecto del kernel. Fija el MPS de cada dispositivo para que coincida con su puente aguas arriba. Conservador y seguro, pero no maximiza el rendimiento.

pcie_bus_safe — fija el MPS al mayor valor que admiten todos los dispositivos del sistema. Útil para sistemas cerrados donde conoces todos los dispositivos y no se conectará nada en caliente. Ligeramente más agresivo que el por defecto.

pcie_bus_perf — fija el MPS por dispositivo al mayor valor que permite el bus padre. Cada dispositivo obtiene el mejor MPS que admite su topología local. Esta es la elección correcta para el passthrough porque optimiza cada camino de forma independiente.

pcie_bus_peer2peer — fija el MPS a 128 bytes en todo. Cada dispositivo habla al menor tamaño común. Se usa cuando los dispositivos necesitan hacer DMA directamente entre sí (GPU a GPU, GPU a NIC vía RDMA). No sirve para el passthrough estándar.

pcie_bus_perf es la correcta para el passthrough.

Cómo arreglarlo — lado del invitado

Puedes fijar pci=pcie_bus_perf también en la configuración de arranque del kernel del invitado. Si tiene algún efecto práctico depende de cómo presente QEMU la topología PCIe virtual. El complejo raíz virtual limita lo que el invitado puede negociar.

En mis pruebas, el arreglo del lado del host es el que se sostiene. El host es dueño del dispositivo físico, y su ajuste de MPS fija el tamaño real de TLP en el cable. El ajuste del invitado solo toca la topología virtual dentro de la VM, y si eso cambia el comportamiento real depende de cómo presente QEMU el camino PCIe para ese dispositivo.

Fíjalo en el host. Fijarlo también en el invitado no hace daño, pero no dependas de eso solo.

Qué es el MRRS

El Max Read Request Size (MRRS) es algo relacionado pero distinto. El MPS limita cuántos datos puede enviar un dispositivo en un TLP. El MRRS limita cuántos datos puede pedir un dispositivo en una petición de lectura.

Un dispositivo con MRRS 4096 puede emitir una sola petición de lectura de 4 KB. La respuesta vuelve en varios TLP, cada uno de hasta el tamaño del MPS. Un MRRS mayor significa que el dispositivo puede pedir más datos por transacción, reduciendo el número de TLP de petición de lectura en el bus.

El MRRS dimensiona la petición, el MPS dimensiona cada paquete de la respuestaControladora NVMeMRRS 4096 Bmemoria del hostvía el complejo raíz1 × petición de lectura — «envíame 4 KB»El MRRS limita cuánto puede pedir una petición8 × TLP de finalización — 512 B cada unoEl MPS limita el tamaño de cada paquete de la respuestaUna petición, muchos paquetes. pci=pcie_bus_perf eleva ambos, así que no hace falta ajustarlos por separado.
Los dos se confunden con facilidad: el MRRS limita cuánto puede pedir un dispositivo en una petición, el MPS limita cómo de grande puede ser cada paquete de la respuesta.

pci=pcie_bus_perf fija tanto el MPS como el MRRS a sus valores óptimos. No necesitas ajustarlos por separado.

Impacto frente a otros ajustes

La diferencia de MPS entre 128 y 512 bytes tiene menos impacto en el rendimiento que la alineación NUMA o el ASPM. Suele ser una mejora de un porcentaje de un solo dígito bajo en el rendimiento. No la verás en pruebas de latencia con profundidades de cola pequeñas.

Pero es una optimización gratis. Un parámetro del kernel, sin inconvenientes, sin riesgo de compatibilidad. No hay razón para no fijarla en cualquier sistema que haga passthrough.

No cuesta nada, y ya has pagado el hardware. Bien puedes tener lo que compraste.

Referencias