Interrupts, and how to divide them between cores

Symetric MultiProcessing (SMP) are becoming increasingly common in embedded systems. Many chip manufacturers such as Texas Instruments and Freescale offers ARM multicores, even FPGA manufacturers like Xilinx and Altera has System-on-Chip with multiple ARM cores. One benefit with SoC is that it's even possible to add soft cores in the programmable logic if it's necessary.

The trend is clear, multiple cores is here and it's not likely to be fewer of them.

But how do we share resources between the cores? One way is to use cgroups (Control Groups) that was merged into the kernel version 2.6.24. Cgroups let you divide resources like cores and memory to specific task groups. More about that in another post.

It's also possible to tell which processor core that is allowed to handle a specific interrupt. This is done in procfs. There are a few entries in procfs related to interrupt-handling. The first interesting entry is /proc/interrupts that is used to record the number of interrupts per CPU.

Beside the the counts of hard IRQ numbers, it also includes interrupts internal to the system that is not associated with a device. Examples on these are NMI (nonmaskable interrupts) and LOC (Local timer interrupt). It also gives us the name of the registred IRQ handler.

Example output:

# cat /proc/interrupts
           CPU0       CPU1
 34:          0          0       GIC  sdma
 35:          0          0       GIC  VPU_JPG_IRQ
 37:          0          0       GIC  imx-ipuv3
 38:          0          0       GIC  imx-ipuv3
 39:          0          0       GIC  imx-ipuv3
400:          0          0      GPIO  pfuze
461:          0          0      GPIO  btn volume-up
150:      19293          0       GIC  enet
IPI0:          0       6028  Timer broadcast interrupts
IPI1:       1953      31365  Rescheduling interrupts
IPI2:          0          0  Function call interrupts
IPI3:       1825       1792  Single function call interrupts
IPI4:          0          0  CPU stop interrupts
LOC:      25302        130  Local timer interrupts

SMP affinity is controlled by manipulating files in the /proc/irq directory.

In /proc/irq is the file default_smp_affinity that specifies the default affinity mask that applies to all non-active IRQs. Once the IRQ is allocated/activated its affinity bitmask will be set to the default mask. The default mask is set so all available cpu cores is allowed to handle the interrupt.

In /proc/irq is also directories that correspond to the IRQ present in the system. These directories has a few entries where two of them are of interest; smp_affinity_list and smp_affinity.

Here is the entries for IRQ 150, which correspond to the ethernet controller (look at the output from /proc/interrupts):

# ls -1 /proc/irq/150
affinity_hint
effective_affinity
effective_affinity_list
node
smp_affinity
smp_affinity_list
spurious

smp_affinity_list contains the available processor cores that are able to handle the interrupt, and the smp_affinity is the bitmask of processor cores that is allowed to handle it.

The current values are:

# cat /proc/irq/150/smp_affinity_list
0-1
# cat /proc/irq/150/smp_affinity
3

This tells us that processor core 0 and 1 is able to handle the interrupt and processor 0 and 1 (bit 0 and 1 gives us 3 dec) is allowed to handle it.

If we look at the output from /proc/interrupts, we see that all interrupts (19293) has been handled by the CPU0. If we just want CPU1 to handle these interrupts, set smp_affinty to 2:

# echo 2 > /proc/irq/150/smp_affinity

Then look at /proc/interrupts again:

# cat /proc/interrupts
           CPU0       CPU1
150:      20967         12       GIC  enet

The CPU1 interrupt counter is ticking and the interrupt is now only served by CPU1.

Resources

Documentation/IRQ-affinity.txt