Hoy voy a hablar sobre las luces rojas y verdes parpadeantes y sobre cómo nos llevaron a una larga investigación de rendimiento que terminó en lo más profundo de los controladores PCI Express en el kernel de Linux.
(Además, si aún no lo has visto, echa un vistazo a La segunda parte de la serie de blogs Road to All-Flash., por la gente que construyó la plataforma que estoy discutiendo en este post.)
La balada de las luces parpadeantes.
Hace aproximadamente un año estaba trabajando en nuestro nuevo Plataforma all-flash NVMe y surgió un problema extraño.
Cuando fallaba un SSD, no podíamos encender el pequeño LED rojo en la unidad para indicar cuál debía ser reemplazado. Esto se debe a que una unidad NVMe está conectada (más o menos) directamente a la CPU a través de PCI-Express, a diferencia de las unidades SATA o SAS, que se conectan a un controlador independiente que sabe cómo hacer cosas como encender y apagar las luces.
Esto puede parecer trivial, pero tener retroalimentación visual en el centro de datos es importante para los administradores de almacenamiento que tienen que ir y reemplazar la unidad dañada (y no reemplazar la incorrecta). En el mejor de los casos, la unidad está tan muerta que el LED está apagado. En el peor de los casos, el administrador de almacenamiento se enfrenta a una peligrosa misión: Imposible elección. (“¡Corta el cable rojo!” “¿Qué cable rojo? Hay veinticuatro cables, ¡y todos son verdes!)
Afortunadamente, este problema tiene solución, y esa solución se llama Intel Volume Management Device (VMD). VMD es una función en el complejo raíz de ciertos procesadores Xeon que puede actuar como delegado de los eventos NVMe y los enruta a los controladores de software. Además de saber cómo encender y apagar las luces, también admite conexiones en caliente más confiables: ¡todos ganan!
Bueno, no exactamente.
Los chicos de juegos de PC tenían razón. LEDs do impacto en el rendimiento.
Cuando habilitamos el uso de VMD, las cosas se volvieron más lentas. UNA montón más lento. Nuestros puntos de referencia de rendimiento mostraron regresiones del 50 por ciento o peor: una de las cargas de trabajo más afectadas alcanzó anteriormente velocidades de alrededor de 15 GB / s, pero ahora luchó por alcanzar los 6 GB / s.
Inicialmente, nos preocupaba que algo sobre la forma en que funciona VMD limitara fundamentalmente el rendimiento que podríamos obtener de los SSD. VMD actúa como una especie de intermediario, y uno de sus efectos es el alias de una serie de dispositivos de almacenamiento detrás de un conjunto limitado y limitado de vectores de interrupción. Sin VMD, cada unidad tiene sus propios vectores de interrupción que no tiene que compartir. Sospechamos que la contención sobre estos recursos de interrupción era lo que nos estaba frenando.
Al final resultó que estábamos casi tenía razón.
Mientras investigamos los datos de perf, nosotros también nos contactamos con algunas de las personas muy inteligentes de Intel para ayudarnos a solucionar el problema. Su asistencia demostró ser invaluable para identificar al verdadero culpable en este misterio.
Los promedios pueden ser engañosos, y otros hechos obvios
Una de las primeras cosas que analizamos fue la latencia de solicitud de E / S promedio para las unidades en ambas configuraciones: VMD apagado y VMD encendido. Para nuestra sorpresa, no hubo una gran diferencia en la latencia promedio. Fue considerablemente más alto con VMD activado, pero solo un poco. Gráficos como este (de datos capturados durante una prueba de escritura) eran típicos:
Un microsegundos 10-15 adicionales por solicitud no es bueno, pero no es suficiente para explicar las pérdidas de rendimiento del 50-60%, incluso si estuviéramos totalmente vinculados a la latencia.
Mientras tanto, los ingenieros de Intel estaban examinando su código de controlador. Había un par de problemas menores que conocían y, de hecho, ya los habían solucionado en versiones posteriores del kernel a la que estábamos usando, por lo que nos proporcionaron parches, creamos un módulo de kernel personalizado y nos pusimos en marcha. pero estas correcciones mejoraron el rendimiento solo ligeramente.
Hubo otro problema que encontraron: el controlador VMD no estaba considerando adecuadamente la afinidad de CPU deseada de los dispositivos al asignar vectores de interrupción. El parche para abordar esto también agregó una opción de controlador, max_vec, que gobierna el número máximo de vectores de interrupción que VMD intentará asignar para cada dispositivo conectado a él. El valor predeterminado había sido anteriormente 4.
Otro parche, otra ronda de reconstrucción del controlador y otra serie de pruebas y, para nuestra satisfacción, el rendimiento fue considerablemente mejor. Pero también había algo peculiar. Cuando probamos varios valores para max_vec, descubrimos que el rendimiento disminuyó estrictamente a medida que aumentaba el valor:
Rendimiento de prueba vs. max_vec | ||
max_vec | escribir | read |
2 | 8,080 MB / s | 15,460 MB / s |
4 | 5,540 MB / s | 13,670 MB / s |
8 | 4,540 MB / s | 13,430 MB / s |
Esto fue inesperado. Finalmente, decidimos revisar los datos. Claramente había algo que nos faltaba. Comencé a analizar los datos de iostat a partir de una serie de pruebas de rendimiento, y pronto descubrí la pieza que faltaba: la unidades No fueron un poco más lentos. Exactamente one conducir fue bastante más lento:
Cuando le mostré a un ingeniero de Intel esta trama, tuvo uno de esos momentos “eureka”. Después de todo, el problema no estaba en VMD, estaba en un controlador para un dispositivo PCIe completamente separado, un módulo de administración integrado en un conmutador Microsemi PCIe.
¿Recuerda cómo un VMD actúa como un intermediario y administra sus dispositivos conectados a través de un conjunto compartido de vectores de interrupción? Cuando el VMD recibe una interrupción en uno de esos vectores, no necesariamente sabe qué dispositivo es el objetivo real. Así que en realidad debe invocar los controladores de interrupción para all Los dispositivos que comparten ese vector. Si uno de esos controladores de interrupción fuera más lento que los otros, el resto simplemente se vería obligado a esperar.
Eso es exactamente lo que estaba pasando. La razón por la que el aumento de max_vec más allá de 2 hizo que las cosas se pusieran mucho peor fue que la asignación de más vectores de interrupción a cada dispositivo aumentaba la probabilidad de que uno (o más) de los SSD terminaran compartiendo un vector con el interruptor Microsemi. Además, debido a que una sola operación de escritura en el sistema de archivos de Qumulo se borrará codificada en múltiples unidades de almacenamiento para la protección de datos, si solo un disco involucrado en una escritura es lento, toda la escritura será lenta.
Aquí hay una versión condensada del controlador de interrupciones ofensivo, que se encuentra en drivers / pci / switch / switchtec.c en la fuente del kernel de Linux:
static irqreturn_t switchtec_event_isr (int irq, void * dev)
{
struct switchtec_dev * stdev = dev;
u32 reg;
/ *… * /
reg = ioread32 (& stdev-> mmio_part_cfg-> mrpc_comp_hdr);
si (registrador y SWITCHTEC_EVENT_OCCURRED) {
/ *… * /
iowrite32 (reg, & stdev-> mmio_part_cfg-> mrpc_comp_hdr);
}
/ *… * /
}
Verifique esas llamadas a ioread32 y iowrite32, dirigidas a una dirección de E / S asignada en memoria en el propio dispositivo de conmutación. Como parte del manejo de una interrupción, este controlador realiza la E / S real a través del bus PCIe (!).
Si hubiera un solo mandamiento para escribir controladores de dispositivo, un contendiente fuerte sería "No deberás hacer más trabajo del que es absolutamente necesario en un controlador de interrupciones". Tal vez, esperar por I / O no es un gran problema para este dispositivo , pero se convierte en un gran problema cuando termina compartiendo un vector de interrupción con algo extremadamente sensible a la latencia.
Afortunadamente, la solución a todo este problema fue simple: no cargue el módulo del kernel de switchtec. No necesitábamos ninguna de sus funciones, y sin ese controlador de interrupciones lento en la mezcla, estábamos de nuevo en funcionamiento a toda velocidad. con Nuestras luces parpadeantes, coloridas.
La moraleja de la historia
Los valores atípicos pueden ser de importancia crítica.
Esta es una de esas cosas que casi todo el mundo "sabe", pero es fácil de olvidar, especialmente cuando se persigue una hipótesis. Después de todo, los promedios son útiles: ese número puede decir mucho sobre un fenómeno. ¡Pero no extraiga demasiadas conclusiones de la media a menos que comprenda la distribución subyacente!