Hardware Interrupts y SMP affinity en Linux

Las interrupciones de Hardware (Hardware Interrupts) es el modo que tienen los dispositivos físicos para establecer comunicación con el sistema operativo. Cuando hablamos de comunicación nos referimos a tareas como que las tarjetas de red informan al sistema de que está procesando un paquete, un disco duro que está leyendo un bloque de datos, etc.

Estas interrupciones se envían de forma directa a la CPU o CPUs. Sin entrar más en definiciones técnicas de como funciona internamente sí que os puedo indicar que en sistemas con múltiples CPUs se pueden producir «descompensaciones». Esto significa que una CPU esté procesando muchas más interrupciones de Hardware que otra.

Ver estadísticas de Hardware Interrupts

El filesystem /proc nos ofrece información extendida sobre las interrupciones de hardware y estadísticas en una tabla en la cual vemos una relación del nº de interrupciones por CPU y dispositivo. Ejemplo:

$ cat /proc/interrupts 
           CPU0       CPU1       CPU2       CPU3       
  0:      27292          0          0          0   IO-APIC-edge      timer
  1:        167          1          0          0   IO-APIC-edge      i8042
  7:          0          0          0          0   IO-APIC-edge      parport0
  8:          1          0          0          0   IO-APIC-edge      rtc0
  9:          0          0          0          0   IO-APIC-fasteoi   acpi
 12:        297          2          0          0   IO-APIC-edge      i8042
 14:          0          0          0          0   IO-APIC-edge      ata_piix
 15:         32         23          2         16   IO-APIC-edge      ata_piix
 18: 3273686896          0     522711          0   IO-APIC-fasteoi   eth0
 19:  472943614     285201          0          0   IO-APIC-fasteoi   eth1

Extraído de la página man de /proc:

/proc/interrupts
  This is used to record the number of interrupts per CPU per IO device.  Since Linux  2.6.24,  for  the
  i386  and  x86_64  architectures, at least, this also includes interrupts internal to the system (that
  is, not associated with a device as such), such as  NMI  (nonmaskable  interrupt),  LOC  (local  timer
  interrupt),  and for SMP systems, TLB (TLB flush interrupt), RES (rescheduling interrupt), CAL (remote
  function call interrupt), and possibly others.  Very easy to read formatting, done in ASCII.

Como podéis ver, tenemos las interfaces de red (eth0 y eth1), el ACPI (Advanced Configuration and Power Interface), 8042 (ratón y teclado), etc.

Si os fijáis bien, en la salida anterior del /proc/interrupts se visualiza que las interrupciones del tráfico de red no se distribuyen correctamente entre las CPU. Esto concretamente provocaba que una CPU tuviera una alta utilización de recursos y la otra no:

18: 3273686896          0     522711          0   IO-APIC-fasteoi   eth0
19:  472943614     285201          0          0   IO-APIC-fasteoi   eth1

Usar SMP affinity para controlar las interrupciones de hardware

Para evitar que un Core o CPU se sature y otras estén sin procesar existe la posibilidad de equilibrarlas usando SMP affinity. Este sistema nos permite especificar que ciertas interrupciones sean dirigidas a ciertos cores.

Como habéis podido ver en la salida del /proc/interrupts, la primera columna especifica un número. Este número es el IRQ Number e identifica a cada dispositivo:

$ ls -ltr /proc/irq/
total 0
-rw------- 1 root root 0 2014-09-30 16:51 default_smp_affinity
dr-xr-xr-x 3 root root 0 2014-09-30 16:51 9
dr-xr-xr-x 3 root root 0 2014-09-30 16:51 8
dr-xr-xr-x 2 root root 0 2014-09-30 16:51 7
dr-xr-xr-x 2 root root 0 2014-09-30 16:51 6
dr-xr-xr-x 2 root root 0 2014-09-30 16:51 5
[...]

Dentro de cada uno de esos directorios se encuentra el fichero en el cual se puede especificar el core que manejará sus interrupciones:

$ ls -ltr /proc/irq/2/smp_affinity 
-rw------- 1 root root 0 2014-09-30 16:53 /proc/irq/19/smp_affinity
# cat /proc/irq/19/smp_affinity
f

¿Cómo podríamos distribuir las interrupciones de las interfaces de red del primer ejemplo entre la CPU0 y la CPU1 en lugar de que todas vayan a la CPU0?

Vamos a configurar en la CPU0 las interrupciones de eth0 y en la CPU1 las interrupciones de eth1. Se debe indicar el valor en hexadecimal. En una máquina con 4 CPUs:

        Binario       Hexadecimal
----------------------------------
CPU 0    0001         1 
CPU 1    0010         2
CPU 2    0100         4
CPU 3    1000         8
----------------------------------

Quedaría del siguiente modo:

# echo "1" > /proc/irq/18/smp_affinity
# echo "2" > /proc/irq/19/smp_affinity

Anteriormente estaba con el valor «f», que representa todos los procesadores:

        Binario       Hexadecimal
---------------------------------
CPU 0    0001         1 
CPU 1    0010         2
CPU 2    0100         4
CPU 3    1000         8
---------------------------------
ALL CPUs    1111         f

Se podrían generar todas las combinaciones que estimemos oportunas. Si configuraramos «3» las interrupciones deberían distribuirse entre la CPU0 y la CPU1:

        Binario       Hexadecimal
---------------------------------
CPU 0    0001         1 
CPU 1    0010         2
---------------------------------
SUMA     0011         3

El cambio en los valores es instantaneo así que deberíamos poder verificar la distribución de interrupciones de hardware inmediatamente tras el cambio con la información de /proc/interrupts:

# cat /proc/interrupts
           CPU0       CPU1       CPU2       CPU3       
  0:       7934          0          0          0   IO-APIC-edge      timer
  1:        219          1          0          0   IO-APIC-edge      i8042
  7:          0          0          0          0   IO-APIC-edge      parport0
  8:          1          0          0          0   IO-APIC-edge      rtc0
  9:          0          0          0          0   IO-APIC-fasteoi   acpi
 12:        191          2          0          0   IO-APIC-edge      i8042
 14:          0          0          0          0   IO-APIC-edge      ata_piix
 15:          0         34          8         31   IO-APIC-edge      ata_piix
 18:  489947954         26          0          0   IO-APIC-fasteoi   eth0
 19:       1422 3274725899          0         24   IO-APIC-fasteoi   eth1

En este caso, tras el cambio, de estar un core al 90% de utilización se consiguió que pasara al 50% y el otro que se encontraba al 10% pasara a 40% aproximadamente. Hemos conseguido balancear el tráfico de las interfaces de red procesando las interrupciones de hardware cada una en un core distinto. Ahora el servidor puede procesar más tráfico sin que la CPU sea el cuello de botella.

Como siempre, si algún experto en la materia tiene algo que corregir o ampliar sus comentarios son bienvenidos.

Un comentario en “Hardware Interrupts y SMP affinity en Linux

  1. Pingback: Bitacoras.com

Comments are closed.