How to tune IRQ affinity¶
IRQ (Interrupt Request) affinity refers to assigning interrupt service routines (ISRs) to specific processor cores in a computer system. Having IRQ affinity rightly set up becomes crucial for improved performance and responsiveness in a real-time system. By tuning IRQ affinity, you dedicate specific interrupts to particular CPU cores, preventing interruptions from being handled by multiple cores simultaneously. The tuning enhances efficiency, reduces latency, and ensures more predictable and consistent processing times for time-sensitive tasks in real-time systems. Proper IRQ affinity configuration helps to decrease contention, manage resource usage, and ultimately improve the overall responsiveness and reliability of the system, especially in scenarios where time precision is critical.
The most common kernel parameters to handle IRQ interrupts are:
kthread_cpus which defines the CPUs that kernel threads are allowed to run on
isolcpus used to specify CPUs to be isolated from the general SMP balancing and scheduler algorithms.
The IRQ affinity on other hand is a way to tune the system interruptions without modifying the kernel boot parameters. This is the focus of this document. With IRQ affinity, you can set a default set of CPUs which are permitted to handle incoming IRQs.
First check the interruptions sources in the system, all the IRQs are listed in
the /proc/interrupts
file.
cat /proc/interrupts
Its useful to monitor the IRQs while tuning the affinity. You can use the
watch
command to monitor the IRQs in real-time:
watch -n 1 cat /proc/interrupts
First you can list the isolated CPUs:
cat /sys/devices/system/cpu/isolated
An empty output means that no core is isolated.
Then you can list all the available CPUs:
$ cat /sys/devices/system/cpu/present
0-19
In this case, the system has 20 CPUs available.
The best way to tune a system is to isolate one or more CPUs to be used to run the real-time application and the others to handle the IRQs and kthreads.
First check which IRQ is being handled by the CPU, or sets of CPUs that you want to isolate. This can do either manually checking:
$ cat /proc/irq/<IRQ-NUMBER>/smp_affinity
ffffff
OBS: The smp_affinity
file is a bitmask, where each bit represents a CPU, where
1
means that the CPU is allowed to handle the IRQ and 0
means that the CPU
is not allowed to handle the IRQ. An output of ffffff
means that all CPUs are
allowed to handle the IRQ.
Or you can use the check_irqs.sh
script to list the all the IRQs
associated with a given CPU:
$ ./check_irqs.sh 13
IRQ 0 is associated with Core 13. Affinity Mask: fffff
IRQ 1 is associated with Core 13. Affinity Mask: fffff
IRQ 10 is associated with Core 13. Affinity Mask: fffff
IRQ 11 is associated with Core 13. Affinity Mask: fffff
IRQ 12 is associated with Core 13. Affinity Mask: fffff
IRQ 120 is associated with Core 13. Affinity Mask: fffff
IRQ 121 is associated with Core 13. Affinity Mask: fffff
IRQ 13 is associated with Core 13. Affinity Mask: fffff
IRQ 14 is associated with Core 13. Affinity Mask: fffff
IRQ 141 is associated with Core 13. Affinity Mask: 02000
IRQ 15 is associated with Core 13. Affinity Mask: fffff
IRQ 150 is associated with Core 13. Affinity Mask: fffff
IRQ 16 is associated with Core 13. Affinity Mask: fffff
IRQ 164 is associated with Core 13. Affinity Mask: 02000
IRQ 167 is associated with Core 13. Affinity Mask: fffff
IRQ 17 is associated with Core 13. Affinity Mask: fffff
IRQ 2 is associated with Core 13. Affinity Mask: fffff
IRQ 3 is associated with Core 13. Affinity Mask: fffff
IRQ 4 is associated with Core 13. Affinity Mask: fffff
IRQ 5 is associated with Core 13. Affinity Mask: fffff
IRQ 6 is associated with Core 13. Affinity Mask: fffff
IRQ 7 is associated with Core 13. Affinity Mask: fffff
IRQ 8 is associated with Core 13. Affinity Mask: fffff
IRQ 9 is associated with Core 13. Affinity Mask: fffff
Then you can rewrite the smp_affinity
file to set the IRQ to be handled by the
CPUs you want. Since kernel 3.0 it’s possible to use the
/proc/irq/<IRQ-NUMBER>/smp_affinity_list
, based on the previous output, if you
want to set the IRQ 16 to be handled by the CPUs 0-12 and 14-19 (excluding the
CPU 13), you can run:
echo 0-12,14-19 > /proc/irq/0/smp_affinity_list
$ cat /proc/irq/0/smp_affinity_list
fdfff
Note
The changes made on the /proc
filesystem are not persistent, meaning that
the changes will be lost after a reboot. To make the changes persistent, you
can set the irqaffinity
parameter as a persistent parameter as
described in How to modify kernel boot parameters.
For example, to isolate the CPU 13 in a system with 20 cpus and leave the IRQs
to be handled by the CPUs 0-12 and 14-19, you can add the following:
irqaffinity=0-12,14-19
Then do this process for all the IRQs that are being handled by the CPUs that you want to isolate.
Warning
It’s not allowed to turn off all CPUs for a given IRQ, meaning that you
should ensure every IRQ is handled by at least one CPU. In other words, the
smp_affinity
mask should never be 0.
Now you can run your real-time application in the isolated CPUs and check if the IRQs are being handled by the CPUs that you want.
taskset -c <CPU-NUM[s]> <COMMAND-TO-REAL-TIME-APP>
Or attaching to an already running process:
taskset -pc <CPU_NUM[s]> <PID>
Then, you can check if th application is correctly running on the designated CPU cores:
ps -eo psr,tid,pid,comm,%cpu,priority,nice -T | grep <PID>
It’s also important to disable the irqbalance
service, which is responsible for
distributing IRQs across all available cores. To do so, you can run:
systemctl disable irqbalance
systemctl stop irqbalance
systemctl status irqbalance
Also, it’s useful to keep the systemd
services separated from the real-time
application. You can do this by setting the CPUAffinity
parameter in the
/etc/systemd/system.conf
file to the cores you want to isolate. For example:
$ cat /etc/systemd/system.conf | grep CPUAffinity
CPUAffinity=0,1