Interrupt helpers for kernel tests.
Take a snapshot of /proc/interrupts before and after a workload and compare the counters of the interrupts of interest:
my @irqs = get_device_irqs($controller);
my $before = get_interrupts();
# run the workload
my $after = get_interrupts();
my $delta = get_irq_total($after, @irqs) - get_irq_total($before, @irqs);
my $snapshot = get_interrupts();
Reads /proc/interrupts on the SUT and returns a hash reference with:
cpus: array reference of the CPU numbers of the counter columns. CPU numbers are not always contiguous, for example if CPUs are offline.
irqs: hash reference of the interrupt name (40, NMI, LOC, ...) to a hash reference with counts, the per-CPU counters in the order of cpus, and desc, the rest of the line (chip and name).
Rows like ERR and MIS have one counter, not one per CPU.
my $count = get_irq_total($snapshot, @irqs);
Returns the sum of the counters of all CPUs for the given interrupts of a snapshot from get_interrupts(). Dies if an interrupt is missing.
my $counts = get_irq_per_cpu($snapshot, @irqs);
Returns a hash reference of the CPU number to the sum of the counters of the given interrupts on that CPU. Dies if an interrupt is missing.
my $remapped = get_irq_remapped($snapshot, @irqs);
Returns a hash reference of each given interrupt to 1 if it goes through interrupt remapping (Intel VT-d or AMD-Vi), else 0, from a snapshot from get_interrupts(). Dies if an interrupt is missing.
On x86_64, the chip name of a remapped interrupt in /proc/interrupts starts with IR-, for example IR-PCI-MSIX-0000:02:00.0 or IR-IO-APIC. Other architectures do not use this prefix, so all interrupts are reported as not remapped there.
my @in_use = get_irqs_in_use($snapshot, @irqs);
Returns the given interrupts that have a row in a snapshot from get_interrupts(), in the given order. A driver can reserve more interrupts than it uses: get_device_irqs() returns all reserved interrupts, but only interrupts that the driver requested appear in /proc/interrupts. For example, a network card reserves interrupts for an RDMA driver that is not loaded, and the driver of a network interface that is down requests none.
my @irqs = get_device_irqs($sysfs_path);
Returns the interrupt numbers of a PCI device, for example /sys/devices/pci0000:00/0000:00:01.0. These are the MSI or MSI-X interrupts from msi_irqs if the device uses them, else the legacy interrupt from irq. Dies if the device has no interrupt. These are the interrupts that the driver reserved; see get_irqs_in_use() for the interrupts that it uses.
my $affinity = get_irq_affinity(@irqs);
Returns a hash reference of each given interrupt to the CPUs that it may run on, as the kernel CPU list from /proc/irq/N/smp_affinity_list, for example 0-7,32-39. Reads all interrupts with one command.
my @refused = set_irq_affinity(298 => '0', 299 => '16', ...);
Sets the CPUs that each given interrupt may run on, as a kernel CPU list written to /proc/irq/N/smp_affinity_list, with one command. Returns the interrupts whose affinity the kernel refused, for example interrupts that the kernel manages itself, such as the queue interrupts of NVMe. A running irqbalance can change the affinity again later.