Skip to contentSkip to conversation

Improve /proc/interrupts further

PATCH Applied Thomas Gleixner 5 participants 47 messages May 17 – May 30

Subsystems (21)
Arm (32-bit) x86 PowerPC perf events +17 more subsystems
lore
Share & tools
lore

Thomas Gleixner

author maintainer
#0 lore raw

v6 cover Improve /proc/interrupts further

This is a follow up to v5 which can be found here:

https://lore.kernel.org/[email protected]

The v1 cover letter contains a full analysis, explanation and numbers:

https://lore.kernel.org/[email protected]

TLDR:

- The performance of reading of /proc/interrupts has been improved piecewise over the years, but most of the low hanging fruit has been left on the table.

Changes vs. V5:

- Rebased against v7.1-rc2

- Addressed some formatting/alignment details - Radu, Michael

- Fixed some 0-day fallout vs. various Kconfig combinations

- Picked up tags where appropriate

Delta patch against v5 is below.

The series applies on top of v7.1-rc2 and is also available via git:

git://git.kernel.org/pub/scm/linux/kernel/git/tglx/devel.git irq-proc-v6

Thanks,

tglx
---

arch/alpha/kernel/irq.c +4 -4
index c67047c5d830..4a6a8b1d5a8b 100644
@@ -72,16 +72,16 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 	int j;
 
 #ifdef CONFIG_SMP
-	seq_puts(p, "IPI: ");
+	seq_puts(p, " IPI: ");
 	for_each_online_cpu(j)
 		seq_printf(p, "%10lu ", cpu_data[j].ipi_count);
 	seq_putc(p, '\n');
 #endif
-	seq_puts(p, "PMI: ");
+	seq_puts(p, " PMI: ");
 	for_each_online_cpu(j)
 		seq_printf(p, "%10lu ", per_cpu(irq_pmi_count, j));
-	seq_puts(p, "          Performance Monitoring\n");
-	seq_printf(p, "ERR: %10lu\n", irq_err_count);
+	seq_puts(p, " Performance Monitoring\n");
+	seq_printf(p, " ERR: %10lu\n", irq_err_count);
 	return 0;
 }
 
arch/arm/kernel/smp.c +1 -2
index 4e8e89a26ca3..b5fb4697bc3f 100644
@@ -551,8 +551,7 @@ void show_ipi_list(struct seq_file *p, int prec)
 		if (!ipi_desc[i])
 			continue;
 
-		seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i,
-			   prec >= 4 ? " " : "");
+		seq_printf(p, "%*s%u:", prec - 1, "IPI", i);
 
 		for_each_online_cpu(cpu)
 			seq_printf(p, "%10u ", irq_desc_kstat_cpu(ipi_desc[i], cpu));
arch/arm64/kernel/smp.c +2 -3
index 1aa324104afb..1d0e0e6a5b92 100644
@@ -833,11 +833,10 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 	unsigned int cpu, i;
 
 	for (i = 0; i < MAX_IPI; i++) {
-		seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i,
-			   prec >= 4 ? " " : "");
+		seq_printf(p, "%*s%u: ", prec - 1, "IPI", i);
 		for_each_online_cpu(cpu)
 			seq_printf(p, "%10u ", irq_desc_kstat_cpu(get_ipi_desc(cpu, i), cpu));
-		seq_printf(p, "      %s\n", ipi_types[i]);
+		seq_printf(p, " %s\n", ipi_types[i]);
 	}
 
 	seq_printf(p, "%*s: %10lu\n", prec, "Err", irq_err_count);
arch/loongarch/kernel/smp.c +1 -1
index 64a048f1b880..50922610758b 100644
@@ -88,7 +88,7 @@ void show_ipi_list(struct seq_file *p, int prec)
 	unsigned int cpu, i;
 
 	for (i = 0; i < NR_IPI; i++) {
-		seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i, prec >= 4 ? " " : "");
+		seq_printf(p, "%*s%u:", prec - 1, "IPI", i);
 		for_each_online_cpu(cpu)
 			seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, cpu).ipi_irqs[i], 10);
 		seq_printf(p, " LoongArch  %d  %s\n", i + 1, ipi_types[i]);
arch/riscv/kernel/smp.c +1 -2
index 5ed5095320e6..fa66f9c97d74 100644
@@ -226,8 +226,7 @@ void show_ipi_stats(struct seq_file *p, int prec)
 	unsigned int cpu, i;
 
 	for (i = 0; i < IPI_MAX; i++) {
-		seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i,
-			   prec >= 4 ? " " : "");
+		seq_printf(p, "%*s%u:", prec - 1, "IPI", i);
 		for_each_online_cpu(cpu)
 			seq_printf(p, "%10u ", irq_desc_kstat_cpu(ipi_desc[i], cpu));
 		seq_printf(p, " %s\n", ipi_names[i]);
arch/sh/kernel/irq.c +1 -1
index 9022d8af9d68..03c39b5da50f 100644
@@ -46,7 +46,7 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 	seq_printf(p, "%*s:", prec, "NMI");
 	for_each_online_cpu(j)
 		seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat.__nmi_count, j), 10);
-	seq_printf(p, "  Non-maskable interrupts\n");
+	seq_printf(p, " Non-maskable interrupts\n");
 
 	seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count));
 
arch/sparc/kernel/irq_32.c +6 -6
index 5210991429d5..22db727652ba 100644
@@ -199,19 +199,19 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 	int j;
 
 #ifdef CONFIG_SMP
-	seq_printf(p, "RES:");
+	seq_printf(p, "%*s:", prec, "RES");
 	for_each_online_cpu(j)
 		seq_put_decimal_ull_width(p, " ", cpu_data(j).irq_resched_count, 10);
-	seq_printf(p, "     IPI rescheduling interrupts\n");
-	seq_printf(p, "CAL:");
+	seq_printf(p, " IPI rescheduling interrupts\n");
+	seq_printf(p, "%*s:", prec, "CAL");
 	for_each_online_cpu(j)
 		seq_put_decimal_ull_width(p, " ", cpu_data(j).irq_call_count, 10);
-	seq_printf(p, "     IPI function call interrupts\n");
+	seq_printf(p, " IPI function call interrupts\n");
 #endif
-	seq_printf(p, "NMI:");
+	seq_printf(p, "%*s:", prec, "NMI");
 	for_each_online_cpu(j)
 		seq_put_decimal_ull_width(p, " ", cpu_data(j).counter, 10);
-	seq_printf(p, "     Non-maskable interrupts\n");
+	seq_printf(p, " Non-maskable interrupts\n");
 	return 0;
 }
 
arch/sparc/kernel/irq_64.c +2 -2
index c5466a9fd560..3f55c69d5f3b 100644
@@ -303,10 +303,10 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 {
 	int j;
 
-	seq_printf(p, "NMI:");
+	seq_printf(p, "%*s:", prec, "NMI");
 	for_each_online_cpu(j)
 		seq_put_decimal_ull_width(p, " ", cpu_data(j).__nmi_count, 10);
-	seq_printf(p, "     Non-maskable interrupts\n");
+	seq_printf(p, " Non-maskable interrupts\n");
 	return 0;
 }
 
arch/um/kernel/irq.c +2 -2
index 5929d498b65f..ddfd6e9bd8c7 100644
@@ -716,12 +716,12 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 	seq_printf(p, "%*s: ", prec, "RES");
 	for_each_online_cpu(cpu)
 		seq_printf(p, "%10u ", irq_stats(cpu)->irq_resched_count);
-	seq_puts(p, "  Rescheduling interrupts\n");
+	seq_puts(p, " Rescheduling interrupts\n");
 
 	seq_printf(p, "%*s: ", prec, "CAL");
 	for_each_online_cpu(cpu)
 		seq_printf(p, "%10u ", irq_stats(cpu)->irq_call_count);
-	seq_puts(p, "  Function call interrupts\n");
+	seq_puts(p, " Function call interrupts\n");
 #endif
 
 	return 0;
arch/x86/kernel/irq.c +25 -25

79 lines not on this page · raw message

arch/xtensa/kernel/irq.c +1 -1
index b1e410f6b5ab..6f01f530868b 100644
@@ -59,7 +59,7 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 	seq_printf(p, "%*s:", prec, "NMI");
 	for_each_online_cpu(cpu)
 		seq_printf(p, " %10lu", per_cpu(nmi_count, cpu));
-	seq_puts(p, "   Non-maskable interrupts\n");
+	seq_puts(p, " Non-maskable interrupts\n");
 #endif
 	return 0;
 }
kernel/irq/debugfs.h new file +44 -0

44 lines not on this page · raw message

kernel/irq/internals.h +1 -39
index 7fbf003c6e93..f9c099d45a64 100644
@@ -12,6 +12,7 @@
 #include <linux/rcuref.h>
 #include <linux/sched/clock.h>
 
+#include "debugfs.h"
 #include "proc.h"
 
 #ifdef CONFIG_SPARSE_IRQ
@@ -394,42 +395,3 @@ static inline struct irq_data *irqd_get_parent_data(struct irq_data *irqd)
 	return NULL;
 #endif
 }
-
-#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
-#include <linux/debugfs.h>
-
-struct irq_bit_descr {
-	unsigned int	mask;
-	char		*name;
-};
-
-#define BIT_MASK_DESCR(m)	{ .mask = m, .name = #m }
-
-void irq_debug_show_bits(struct seq_file *m, int ind, unsigned int state,
-			 const struct irq_bit_descr *sd, int size);
-
-void irq_add_debugfs_entry(unsigned int irq, struct irq_desc *desc);
-static inline void irq_remove_debugfs_entry(struct irq_desc *desc)
-{
-	debugfs_remove(desc->debugfs_file);
-	kfree(desc->dev_name);
-}
-void irq_debugfs_copy_devname(int irq, struct device *dev);
-# ifdef CONFIG_IRQ_DOMAIN
-void irq_domain_debugfs_init(struct dentry *root);
-# else
-static inline void irq_domain_debugfs_init(struct dentry *root)
-{
-}
-# endif
-#else /* CONFIG_GENERIC_IRQ_DEBUGFS */
-static inline void irq_add_debugfs_entry(unsigned int irq, struct irq_desc *d)
-{
-}
-static inline void irq_remove_debugfs_entry(struct irq_desc *d)
-{
-}
-static inline void irq_debugfs_copy_devname(int irq, struct device *dev)
-{
-}
-#endif /* CONFIG_GENERIC_IRQ_DEBUGFS */
kernel/irq/irqdomain.c +1 -1
index 9f524ed709b8..f15c9f1223bb 100644
@@ -2084,7 +2084,7 @@ static void irq_domain_free_one_irq(struct irq_domain *domain, unsigned int virq
 #endif	/* CONFIG_IRQ_DOMAIN_HIERARCHY */
 
 #ifdef CONFIG_GENERIC_IRQ_DEBUGFS
-#include "internals.h"
+#include "debugfs.h"
 
 static struct dentry *domain_dir;
 
kernel/irq/proc.c +3 -2
index 3bd394aa7617..ca535472e657 100644
@@ -465,7 +465,7 @@ static struct irq_proc_constraints {
 	unsigned int	num_prec;
 	unsigned int	chip_width;
 } irq_proc_constraints __read_mostly = {
-	.num_prec	= 3,
+	.num_prec	= 4,
 	.chip_width	= 8,
 };
 
@@ -477,7 +477,7 @@ void irq_proc_calc_prec(void)
 {
 	unsigned int prec, n;
 
-	for (prec = 3, n = 1000; prec < 10 && n <= total_nr_irqs; ++prec)
+	for (prec = 4, n = 10000; prec < 10 && n <= total_nr_irqs; ++prec)
 		n *= 10;
 
 	guard(raw_spinlock_irqsave)(&irq_proc_constraints_lock);
@@ -498,6 +498,7 @@ void irq_proc_update_chip(const struct irq_chip *chip)
 		WRITE_ONCE(irq_proc_constraints.chip_width, len);
 }
 
+/* Same as seq_put_decimal_ull_width(p, " ", cnt, 10) */
 #define ZSTR1 "          0"
 #define ZSTR1_LEN	(sizeof(ZSTR1) - 1)
 #define ZSTR16		ZSTR1 ZSTR1 ZSTR1 ZSTR1 ZSTR1 ZSTR1 ZSTR1 ZSTR1 \
kernel/irq/proc.h +4 -0
index ec9173d573f9..0631d57fbfb7 100644
@@ -1,4 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _KERNEL_IRQ_PROC_H
+#define _KERNEL_IRQ_PROC_H
 
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_GENERIC_IRQ_SHOW)
 void irq_proc_calc_prec(void);
@@ -7,3 +9,5 @@ void irq_proc_update_chip(const struct irq_chip *chip);
 static inline void irq_proc_calc_prec(void) { }
 static inline void irq_proc_update_chip(const struct irq_chip *chip) { }
 #endif
+
+#endif
scripts/gdb/linux/interrupts.py +13 -17
index cf0a02c8124d..a68ae91b4531 100644
@@ -90,6 +90,13 @@ def show_irq_desc(prec, chip_width, irq):
 
     return text
 
+def show_irq_err_count(prec):
+    cnt = utils.gdb_eval_or_none("irq_err_count")
+    text = ""
+    if cnt is not None:
+        text += "%*s: %10u\n" % (prec, "ERR", cnt['counter'])
+    return text
+
 def x86_show_irqstat(prec, pfx, idx, desc):
     irq_stat = gdb.parse_and_eval("&irq_stat.counts[%d]" %idx)
     text = "%*s: " % (prec, pfx)
@@ -124,34 +131,23 @@ def arm_common_show_interrupts(prec):
     if nr_ipi is None or ipi_desc is None or ipi_types is None:
         return text
 
-    if prec >= 4:
-        sep = " "
-    else:
-        sep = ""
-
     for ipi in range(nr_ipi):
-        text += "%*s%u:%s" % (prec - 1, "IPI", ipi, sep)
+        text += "%*s%u: " % (prec - 1, "IPI", ipi)
         desc = ipi_desc[ipi].cast(irq_desc_type.get_type().pointer())
         if desc == 0:
             continue
         for cpu in cpus.each_online_cpu():
-            text += "%10u" % (cpus.per_cpu(desc['kstat_irqs'], cpu)['cnt'])
-        text += "      %s" % (ipi_types[ipi].string())
+            text += "%10u " % (cpus.per_cpu(desc['kstat_irqs'], cpu)['cnt'])
+        text += "%s" % (ipi_types[ipi].string())
         text += "\n"
     return text
 
 def aarch64_show_interrupts(prec):
+    # Does not work for ARM64 as "ipi_desc" is not available there
     text = arm_common_show_interrupts(prec)
     text += "%*s: %10lu\n" % (prec, "ERR", gdb.parse_and_eval("irq_err_count"))
     return text
 
-def show_irq_err_count(prec):
-    cnt = utils.gdb_eval_or_none("irq_err_count")
-    text = ""
-    if cnt is not None:
-        text += "%*s: %10u\n" % (prec, "ERR", cnt['counter'])
-    return text
-
 def arch_show_interrupts(prec):
     text = ""
     if utils.is_target_arch("x86"):
@@ -181,8 +177,8 @@ class LxInterruptList(gdb.Command):
             prec = int(constr['num_prec'])
             chip_width = int(constr['chip_width'])
         else:
-            prec = 3
-            j = 1000
+            prec = 4
+            j = 10000
             while prec < 10 and j <= nr_irqs:
                 prec += 1
                 j *= 10
author maintainer
#1 lore raw

PATCH v6 1/16 x86/irq: Optimize interrupts decimals printing

From: Dmitry Ilvokhin <[email protected]>

Monitoring tools periodically scan /proc/interrupts to export metrics as a timeseries for future analysis and investigation.

In large fleets, /proc/interrupts is polled (often every few seconds) on every machine. The cumulative overhead adds up quickly across thousands of nodes, so reducing the cost of generating these stats does have a measurable operational impact. With the ongoing trend toward higher core counts per machine, this cost becomes even more noticeable over time, since interrupt counters are per-CPU. In Meta's fleet, we have observed this overhead at scale.

Although a binary /proc interface would be a better long-term solution due to lower formatting (kernel side) and parsing (userspace side) overhead, the text interface will remain in use for some time, even if better solutions will be available. Optimizing the /proc/interrupts printing code is therefore still beneficial.

Function seq_printf() supports rich format string for decimals printing, but it doesn't required for printing /proc/interrupts per CPU counters, seq_put_decimal_ull_width() function can be used instead to print per CPU counters, because very limited formatting is required for this case. Similar optimization idea is already used in show_interrupts().

As a side effect this aligns the x86 decriptions with the generic interrupts event descriptions.

Performance counter stats (truncated) for 'sh -c cat /proc/interrupts

Before:

      3.42 msec task-clock        #    0.802 CPUs utilized   ( +-  0.05% )
         1      context-switches  #  291.991 /sec            ( +-  0.74% )
         0      cpu-migrations    #    0.000 /sec
       343      page-faults       #  100.153 K/sec           ( +-  0.01% )
 8,932,242      instructions      #    1.66  insn per cycle  ( +-  0.34% )
 5,374,427      cycles            #    1.569 GHz             ( +-  0.04% )
 1,483,154      branches          #  433.068 M/sec           ( +-  0.22% )
    28,768      branch-misses     #    1.94% of all branches ( +-  0.31% )

0.00427182 +- 0.00000215 seconds time elapsed ( +- 0.05% )

After:

      2.39 msec task-clock        #    0.796 CPUs utilized   ( +-  0.06% )
         1      context-switches  #  418.541 /sec            ( +-  0.70% )
         0      cpu-migrations    #    0.000 /sec
       343      page-faults       #  143.560 K/sec           ( +-  0.01% )
 7,020,982      instructions      #    1.30  insn per cycle  ( +-  0.52% )
 5,397,266      cycles            #    2.259 GHz             ( +-  0.06% )
 1,569,648      branches          #  656.962 M/sec           ( +-  0.08% )
    25,419      branch-misses     #    1.62% of all branches ( +-  0.72% )

0.00299996 +- 0.00000206 seconds time elapsed ( +- 0.07% )

Relative speed up in time elapsed is around 29%.

[ tglx: Fixed it up so it applies to current mainline ]

---
Changes v2:
- Expanded commit message: add more rationale for the proposed change.
- Renamed helper put_spaced_decimal() -> put_decimal() primarely to make checkpatch.pl --strict pass.

 arch/x86/kernel/irq.c |  112 ++++++++++++++++++++++++++------------------------
 1 file changed, 59 insertions(+), 53 deletions(-)
arch/x86/kernel/irq.c +59 -53
@@ -62,6 +62,18 @@ void ack_bad_irq(unsigned int irq)
 	apic_eoi();
 }
 
+/*
+ * A helper routine for putting space and decimal number without overhead
+ * from rich format of printf().
+ */
+static void put_decimal(struct seq_file *p, unsigned long long num)
+{
+	const char *delimiter = " ";
+	unsigned int width = 10;
+
+	seq_put_decimal_ull_width(p, delimiter, num, width);
+}
+
 #define irq_stats(x)		(&per_cpu(irq_stat, x))
 /*
  * /proc/interrupts printing for arch specific interrupts
@@ -70,103 +82,101 @@ int arch_show_interrupts(struct seq_file
 {
 	int j;
 
-	seq_printf(p, "%*s: ", prec, "NMI");
+	seq_printf(p, "%*s:", prec, "NMI");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->__nmi_count);
+		put_decimal(p, irq_stats(j)->__nmi_count);
 	seq_puts(p, "  Non-maskable interrupts\n");
 #ifdef CONFIG_X86_LOCAL_APIC
-	seq_printf(p, "%*s: ", prec, "LOC");
+	seq_printf(p, "%*s:", prec, "LOC");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->apic_timer_irqs);
+		put_decimal(p, irq_stats(j)->apic_timer_irqs);
 	seq_puts(p, "  Local timer interrupts\n");
 
-	seq_printf(p, "%*s: ", prec, "SPU");
+	seq_printf(p, "%*s:", prec, "SPU");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->irq_spurious_count);
+		put_decimal(p, irq_stats(j)->irq_spurious_count);
 	seq_puts(p, "  Spurious interrupts\n");
-	seq_printf(p, "%*s: ", prec, "PMI");
+	seq_printf(p, "%*s:", prec, "PMI");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->apic_perf_irqs);
+		put_decimal(p, irq_stats(j)->apic_perf_irqs);
 	seq_puts(p, "  Performance monitoring interrupts\n");
-	seq_printf(p, "%*s: ", prec, "IWI");
+	seq_printf(p, "%*s:", prec, "IWI");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->apic_irq_work_irqs);
+		put_decimal(p, irq_stats(j)->apic_irq_work_irqs);
 	seq_puts(p, "  IRQ work interrupts\n");
-	seq_printf(p, "%*s: ", prec, "RTR");
+	seq_printf(p, "%*s:", prec, "RTR");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->icr_read_retry_count);
+		put_decimal(p, irq_stats(j)->icr_read_retry_count);
 	seq_puts(p, "  APIC ICR read retries\n");
 	if (x86_platform_ipi_callback) {
-		seq_printf(p, "%*s: ", prec, "PLT");
+		seq_printf(p, "%*s:", prec, "PLT");
 		for_each_online_cpu(j)
-			seq_printf(p, "%10u ", irq_stats(j)->x86_platform_ipis);
+			put_decimal(p, irq_stats(j)->x86_platform_ipis);
 		seq_puts(p, "  Platform interrupts\n");
 	}
 #endif
 #ifdef CONFIG_SMP
-	seq_printf(p, "%*s: ", prec, "RES");
+	seq_printf(p, "%*s:", prec, "RES");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->irq_resched_count);
+		put_decimal(p, irq_stats(j)->irq_resched_count);
 	seq_puts(p, "  Rescheduling interrupts\n");
-	seq_printf(p, "%*s: ", prec, "CAL");
+	seq_printf(p, "%*s:", prec, "CAL");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->irq_call_count);
+		put_decimal(p, irq_stats(j)->irq_call_count);
 	seq_puts(p, "  Function call interrupts\n");
-	seq_printf(p, "%*s: ", prec, "TLB");
+	seq_printf(p, "%*s:", prec, "TLB");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->irq_tlb_count);
+		put_decimal(p, irq_stats(j)->irq_tlb_count);
 	seq_puts(p, "  TLB shootdowns\n");
 #endif
 #ifdef CONFIG_X86_THERMAL_VECTOR
-	seq_printf(p, "%*s: ", prec, "TRM");
+	seq_printf(p, "%*s:", prec, "TRM");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->irq_thermal_count);
+		put_decimal(p, irq_stats(j)->irq_thermal_count);
 	seq_puts(p, "  Thermal event interrupts\n");
 #endif
 #ifdef CONFIG_X86_MCE_THRESHOLD
-	seq_printf(p, "%*s: ", prec, "THR");
+	seq_printf(p, "%*s:", prec, "THR");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->irq_threshold_count);
+		put_decimal(p, irq_stats(j)->irq_threshold_count);
 	seq_puts(p, "  Threshold APIC interrupts\n");
 #endif
 #ifdef CONFIG_X86_MCE_AMD
-	seq_printf(p, "%*s: ", prec, "DFR");
+	seq_printf(p, "%*s:", prec, "DFR");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->irq_deferred_error_count);
+		put_decimal(p, irq_stats(j)->irq_deferred_error_count);
 	seq_puts(p, "  Deferred Error APIC interrupts\n");
 #endif
 #ifdef CONFIG_X86_MCE
-	seq_printf(p, "%*s: ", prec, "MCE");
+	seq_printf(p, "%*s:", prec, "MCE");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", per_cpu(mce_exception_count, j));
+		put_decimal(p, per_cpu(mce_exception_count, j));
 	seq_puts(p, "  Machine check exceptions\n");
-	seq_printf(p, "%*s: ", prec, "MCP");
+	seq_printf(p, "%*s:", prec, "MCP");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", per_cpu(mce_poll_count, j));
+		put_decimal(p, per_cpu(mce_poll_count, j));
 	seq_puts(p, "  Machine check polls\n");
 #endif
 #ifdef CONFIG_X86_HV_CALLBACK_VECTOR
 	if (test_bit(HYPERVISOR_CALLBACK_VECTOR, system_vectors)) {
-		seq_printf(p, "%*s: ", prec, "HYP");
+		seq_printf(p, "%*s:", prec, "HYP");
 		for_each_online_cpu(j)
-			seq_printf(p, "%10u ",
-				   irq_stats(j)->irq_hv_callback_count);
+			put_decimal(p, irq_stats(j)->irq_hv_callback_count);
 		seq_puts(p, "  Hypervisor callback interrupts\n");
 	}
 #endif
 #if IS_ENABLED(CONFIG_HYPERV)
 	if (test_bit(HYPERV_REENLIGHTENMENT_VECTOR, system_vectors)) {
-		seq_printf(p, "%*s: ", prec, "HRE");
+		seq_printf(p, "%*s:", prec, "HRE");
 		for_each_online_cpu(j)
-			seq_printf(p, "%10u ",
-				   irq_stats(j)->irq_hv_reenlightenment_count);
+			put_decimal(p,
+				    irq_stats(j)->irq_hv_reenlightenment_count);
 		seq_puts(p, "  Hyper-V reenlightenment interrupts\n");
 	}
 	if (test_bit(HYPERV_STIMER0_VECTOR, system_vectors)) {
-		seq_printf(p, "%*s: ", prec, "HVS");
+		seq_printf(p, "%*s:", prec, "HVS");
 		for_each_online_cpu(j)
-			seq_printf(p, "%10u ",
-				   irq_stats(j)->hyperv_stimer0_count);
+			put_decimal(p, irq_stats(j)->hyperv_stimer0_count);
 		seq_puts(p, "  Hyper-V stimer0 interrupts\n");
 	}
 #endif
@@ -175,35 +185,31 @@ int arch_show_interrupts(struct seq_file
 	seq_printf(p, "%*s: %10u\n", prec, "MIS", atomic_read(&irq_mis_count));
 #endif
 #if IS_ENABLED(CONFIG_KVM)
-	seq_printf(p, "%*s: ", prec, "PIN");
+	seq_printf(p, "%*s:", prec, "PIN");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->kvm_posted_intr_ipis);
+		put_decimal(p, irq_stats(j)->kvm_posted_intr_ipis);
 	seq_puts(p, "  Posted-interrupt notification event\n");
 
-	seq_printf(p, "%*s: ", prec, "NPI");
+	seq_printf(p, "%*s:", prec, "NPI");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ",
-			   irq_stats(j)->kvm_posted_intr_nested_ipis);
+		put_decimal(p, irq_stats(j)->kvm_posted_intr_nested_ipis);
 	seq_puts(p, "  Nested posted-interrupt event\n");
 
-	seq_printf(p, "%*s: ", prec, "PIW");
+	seq_printf(p, "%*s:", prec, "PIW");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ",
-			   irq_stats(j)->kvm_posted_intr_wakeup_ipis);
+		put_decimal(p, irq_stats(j)->kvm_posted_intr_wakeup_ipis);
 	seq_puts(p, "  Posted-interrupt wakeup event\n");
 #endif
 #ifdef CONFIG_GUEST_PERF_EVENTS
-	seq_printf(p, "%*s: ", prec, "VPMI");
+	seq_printf(p, "%*s:", prec, "VPMI");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ",
-			   irq_stats(j)->perf_guest_mediated_pmis);
+		put_decimal(p, irq_stats(j)->perf_guest_mediated_pmis);
 	seq_puts(p, " Perf Guest Mediated PMI\n");
 #endif
 #ifdef CONFIG_X86_POSTED_MSI
-	seq_printf(p, "%*s: ", prec, "PMN");
+	seq_printf(p, "%*s:", prec, "PMN");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ",
-			   irq_stats(j)->posted_msi_notification_count);
+		put_decimal(p, irq_stats(j)->posted_msi_notification_count);
 	seq_puts(p, "  Posted MSI notification event\n");
 #endif
 	return 0;
#2 lore raw

[tip: irq/core] x86/irq: Optimize interrupts decimals printing

The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     115bbf0c1b60cb7bed348c64694eb88e21e7d458
Gitweb:        https://git.kernel.org/tip/115bbf0c1b60cb7bed348c64694eb88e21e7d458
Author:        Dmitry Ilvokhin <[email protected]>
AuthorDate:    Sun, 17 May 2026 22:01:33 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Tue, 26 May 2026 16:21:11 +02:00

x86/irq: Optimize interrupts decimals printing

Monitoring tools periodically scan /proc/interrupts to export metrics as a timeseries for future analysis and investigation.

In large fleets, /proc/interrupts is polled (often every few seconds) on every machine. The cumulative overhead adds up quickly across thousands of nodes, so reducing the cost of generating these stats does have a measurable operational impact. With the ongoing trend toward higher core counts per machine, this cost becomes even more noticeable over time, since interrupt counters are per-CPU. In Meta's fleet, we have observed this overhead at scale.

Although a binary /proc interface would be a better long-term solution due to lower formatting (kernel side) and parsing (userspace side) overhead, the text interface will remain in use for some time, even if better solutions will be available. Optimizing the /proc/interrupts printing code is therefore still beneficial.

Function seq_printf() supports rich format string for decimals printing, but it doesn't required for printing /proc/interrupts per CPU counters, seq_put_decimal_ull_width() function can be used instead to print per CPU counters, because very limited formatting is required for this case. Similar optimization idea is already used in show_interrupts().

As a side effect this aligns the x86 decriptions with the generic interrupts event descriptions.

Performance counter stats (truncated) for 'sh -c cat /proc/interrupts

Before:

      3.42 msec task-clock        #    0.802 CPUs utilized   ( +-  0.05% )
         1      context-switches  #  291.991 /sec            ( +-  0.74% )
         0      cpu-migrations    #    0.000 /sec
       343      page-faults       #  100.153 K/sec           ( +-  0.01% )
 8,932,242      instructions      #    1.66  insn per cycle  ( +-  0.34% )
 5,374,427      cycles            #    1.569 GHz             ( +-  0.04% )
 1,483,154      branches          #  433.068 M/sec           ( +-  0.22% )
    28,768      branch-misses     #    1.94% of all branches ( +-  0.31% )

0.00427182 +- 0.00000215 seconds time elapsed ( +- 0.05% )

After:

      2.39 msec task-clock        #    0.796 CPUs utilized   ( +-  0.06% )
         1      context-switches  #  418.541 /sec            ( +-  0.70% )
         0      cpu-migrations    #    0.000 /sec
       343      page-faults       #  143.560 K/sec           ( +-  0.01% )
 7,020,982      instructions      #    1.30  insn per cycle  ( +-  0.52% )
 5,397,266      cycles            #    2.259 GHz             ( +-  0.06% )
 1,569,648      branches          #  656.962 M/sec           ( +-  0.08% )
    25,419      branch-misses     #    1.62% of all branches ( +-  0.72% )

0.00299996 +- 0.00000206 seconds time elapsed ( +- 0.07% )

Relative speed up in time elapsed is around 29%.

[ tglx: Fixed it up so it applies to current mainline ]

---
 arch/x86/kernel/irq.c | 112 +++++++++++++++++++++--------------------
 1 file changed, 59 insertions(+), 53 deletions(-)
arch/x86/kernel/irq.c +59 -53
index ec77be2..963690b 100644
@@ -62,6 +62,18 @@ void ack_bad_irq(unsigned int irq)
 	apic_eoi();
 }
 
+/*
+ * A helper routine for putting space and decimal number without overhead
+ * from rich format of printf().
+ */
+static void put_decimal(struct seq_file *p, unsigned long long num)
+{
+	const char *delimiter = " ";
+	unsigned int width = 10;
+
+	seq_put_decimal_ull_width(p, delimiter, num, width);
+}
+
 #define irq_stats(x)		(&per_cpu(irq_stat, x))
 /*
  * /proc/interrupts printing for arch specific interrupts
@@ -70,103 +82,101 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 {
 	int j;
 
-	seq_printf(p, "%*s: ", prec, "NMI");
+	seq_printf(p, "%*s:", prec, "NMI");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->__nmi_count);
+		put_decimal(p, irq_stats(j)->__nmi_count);
 	seq_puts(p, "  Non-maskable interrupts\n");
 #ifdef CONFIG_X86_LOCAL_APIC
-	seq_printf(p, "%*s: ", prec, "LOC");
+	seq_printf(p, "%*s:", prec, "LOC");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->apic_timer_irqs);
+		put_decimal(p, irq_stats(j)->apic_timer_irqs);
 	seq_puts(p, "  Local timer interrupts\n");
 
-	seq_printf(p, "%*s: ", prec, "SPU");
+	seq_printf(p, "%*s:", prec, "SPU");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->irq_spurious_count);
+		put_decimal(p, irq_stats(j)->irq_spurious_count);
 	seq_puts(p, "  Spurious interrupts\n");
-	seq_printf(p, "%*s: ", prec, "PMI");
+	seq_printf(p, "%*s:", prec, "PMI");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->apic_perf_irqs);
+		put_decimal(p, irq_stats(j)->apic_perf_irqs);
 	seq_puts(p, "  Performance monitoring interrupts\n");
-	seq_printf(p, "%*s: ", prec, "IWI");
+	seq_printf(p, "%*s:", prec, "IWI");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->apic_irq_work_irqs);
+		put_decimal(p, irq_stats(j)->apic_irq_work_irqs);
 	seq_puts(p, "  IRQ work interrupts\n");
-	seq_printf(p, "%*s: ", prec, "RTR");
+	seq_printf(p, "%*s:", prec, "RTR");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->icr_read_retry_count);
+		put_decimal(p, irq_stats(j)->icr_read_retry_count);
 	seq_puts(p, "  APIC ICR read retries\n");
 	if (x86_platform_ipi_callback) {
-		seq_printf(p, "%*s: ", prec, "PLT");
+		seq_printf(p, "%*s:", prec, "PLT");
 		for_each_online_cpu(j)
-			seq_printf(p, "%10u ", irq_stats(j)->x86_platform_ipis);
+			put_decimal(p, irq_stats(j)->x86_platform_ipis);
 		seq_puts(p, "  Platform interrupts\n");
 	}
 #endif
 #ifdef CONFIG_SMP
-	seq_printf(p, "%*s: ", prec, "RES");
+	seq_printf(p, "%*s:", prec, "RES");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->irq_resched_count);
+		put_decimal(p, irq_stats(j)->irq_resched_count);
 	seq_puts(p, "  Rescheduling interrupts\n");
-	seq_printf(p, "%*s: ", prec, "CAL");
+	seq_printf(p, "%*s:", prec, "CAL");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->irq_call_count);
+		put_decimal(p, irq_stats(j)->irq_call_count);
 	seq_puts(p, "  Function call interrupts\n");
-	seq_printf(p, "%*s: ", prec, "TLB");
+	seq_printf(p, "%*s:", prec, "TLB");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->irq_tlb_count);
+		put_decimal(p, irq_stats(j)->irq_tlb_count);
 	seq_puts(p, "  TLB shootdowns\n");
 #endif
 #ifdef CONFIG_X86_THERMAL_VECTOR
-	seq_printf(p, "%*s: ", prec, "TRM");
+	seq_printf(p, "%*s:", prec, "TRM");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->irq_thermal_count);
+		put_decimal(p, irq_stats(j)->irq_thermal_count);
 	seq_puts(p, "  Thermal event interrupts\n");
 #endif
 #ifdef CONFIG_X86_MCE_THRESHOLD
-	seq_printf(p, "%*s: ", prec, "THR");
+	seq_printf(p, "%*s:", prec, "THR");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->irq_threshold_count);
+		put_decimal(p, irq_stats(j)->irq_threshold_count);
 	seq_puts(p, "  Threshold APIC interrupts\n");
 #endif
 #ifdef CONFIG_X86_MCE_AMD
-	seq_printf(p, "%*s: ", prec, "DFR");
+	seq_printf(p, "%*s:", prec, "DFR");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->irq_deferred_error_count);
+		put_decimal(p, irq_stats(j)->irq_deferred_error_count);
 	seq_puts(p, "  Deferred Error APIC interrupts\n");
 #endif
 #ifdef CONFIG_X86_MCE
-	seq_printf(p, "%*s: ", prec, "MCE");
+	seq_printf(p, "%*s:", prec, "MCE");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", per_cpu(mce_exception_count, j));
+		put_decimal(p, per_cpu(mce_exception_count, j));
 	seq_puts(p, "  Machine check exceptions\n");
-	seq_printf(p, "%*s: ", prec, "MCP");
+	seq_printf(p, "%*s:", prec, "MCP");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", per_cpu(mce_poll_count, j));
+		put_decimal(p, per_cpu(mce_poll_count, j));
 	seq_puts(p, "  Machine check polls\n");
 #endif
 #ifdef CONFIG_X86_HV_CALLBACK_VECTOR
 	if (test_bit(HYPERVISOR_CALLBACK_VECTOR, system_vectors)) {
-		seq_printf(p, "%*s: ", prec, "HYP");
+		seq_printf(p, "%*s:", prec, "HYP");
 		for_each_online_cpu(j)
-			seq_printf(p, "%10u ",
-				   irq_stats(j)->irq_hv_callback_count);
+			put_decimal(p, irq_stats(j)->irq_hv_callback_count);
 		seq_puts(p, "  Hypervisor callback interrupts\n");
 	}
 #endif
 #if IS_ENABLED(CONFIG_HYPERV)
 	if (test_bit(HYPERV_REENLIGHTENMENT_VECTOR, system_vectors)) {
-		seq_printf(p, "%*s: ", prec, "HRE");
+		seq_printf(p, "%*s:", prec, "HRE");
 		for_each_online_cpu(j)
-			seq_printf(p, "%10u ",
-				   irq_stats(j)->irq_hv_reenlightenment_count);
+			put_decimal(p,
+				    irq_stats(j)->irq_hv_reenlightenment_count);
 		seq_puts(p, "  Hyper-V reenlightenment interrupts\n");
 	}
 	if (test_bit(HYPERV_STIMER0_VECTOR, system_vectors)) {
-		seq_printf(p, "%*s: ", prec, "HVS");
+		seq_printf(p, "%*s:", prec, "HVS");
 		for_each_online_cpu(j)
-			seq_printf(p, "%10u ",
-				   irq_stats(j)->hyperv_stimer0_count);
+			put_decimal(p, irq_stats(j)->hyperv_stimer0_count);
 		seq_puts(p, "  Hyper-V stimer0 interrupts\n");
 	}
 #endif
@@ -175,35 +185,31 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 	seq_printf(p, "%*s: %10u\n", prec, "MIS", atomic_read(&irq_mis_count));
 #endif
 #if IS_ENABLED(CONFIG_KVM)
-	seq_printf(p, "%*s: ", prec, "PIN");
+	seq_printf(p, "%*s:", prec, "PIN");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", irq_stats(j)->kvm_posted_intr_ipis);
+		put_decimal(p, irq_stats(j)->kvm_posted_intr_ipis);
 	seq_puts(p, "  Posted-interrupt notification event\n");
 
-	seq_printf(p, "%*s: ", prec, "NPI");
+	seq_printf(p, "%*s:", prec, "NPI");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ",
-			   irq_stats(j)->kvm_posted_intr_nested_ipis);
+		put_decimal(p, irq_stats(j)->kvm_posted_intr_nested_ipis);
 	seq_puts(p, "  Nested posted-interrupt event\n");
 
-	seq_printf(p, "%*s: ", prec, "PIW");
+	seq_printf(p, "%*s:", prec, "PIW");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ",
-			   irq_stats(j)->kvm_posted_intr_wakeup_ipis);
+		put_decimal(p, irq_stats(j)->kvm_posted_intr_wakeup_ipis);
 	seq_puts(p, "  Posted-interrupt wakeup event\n");
 #endif
 #ifdef CONFIG_GUEST_PERF_EVENTS
-	seq_printf(p, "%*s: ", prec, "VPMI");
+	seq_printf(p, "%*s:", prec, "VPMI");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ",
-			   irq_stats(j)->perf_guest_mediated_pmis);
+		put_decimal(p, irq_stats(j)->perf_guest_mediated_pmis);
 	seq_puts(p, " Perf Guest Mediated PMI\n");
 #endif
 #ifdef CONFIG_X86_POSTED_MSI
-	seq_printf(p, "%*s: ", prec, "PMN");
+	seq_printf(p, "%*s:", prec, "PMN");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ",
-			   irq_stats(j)->posted_msi_notification_count);
+		put_decimal(p, irq_stats(j)->posted_msi_notification_count);
 	seq_puts(p, "  Posted MSI notification event\n");
 #endif
 	return 0;
author maintainer
#3 lore raw

PATCH v6 2/16 genirq/proc: Avoid formatting zero counts in /proc/interrupts

In reply to Thomas Gleixner

From: Thomas Gleixner <[email protected]>

A large portion of interrupt count entries are zero. There is no point in formatting the zero value as it is way cheeper to just emit a constant string.

Collect the number of consecutive zero counts and emit them in one go before a non-zero count and at the end of the line.

---
V2: Use sizeof() for ZSTR1_LEN - Dmitry

---
 include/linux/interrupt.h |    1 +
 kernel/irq/proc.c         |   43 ++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 39 insertions(+), 5 deletions(-)
include/linux/interrupt.h +1 -0
@@ -864,6 +864,7 @@ static inline void init_irq_proc(void)
 struct seq_file;
 int show_interrupts(struct seq_file *p, void *v);
 int arch_show_interrupts(struct seq_file *p, int prec);
+void irq_proc_emit_counts(struct seq_file *p, unsigned int __percpu *cnts);
 
 extern int early_irq_init(void);
 extern int arch_probe_nr_irqs(void);
kernel/irq/proc.c +38 -5

55 lines not on this page · raw message

reviewer Reviewed-by on v6 2/16
#4 lore raw

Re: [patch V6 02/16] genirq/proc: Avoid formatting zero counts in /proc/interrupts

Hi Thomas.

Quoted 5 lines · Thomas Gleixner

From: Thomas Gleixner <[email protected]>

A large portion of interrupt count entries are zero. There is no point in formatting the zero value as it is way cheeper to just emit a constant string.

nit: s/cheeper/cheaper

Quoted 27 lines

Collect the number of consecutive zero counts and emit them in one go before a non-zero count and at the end of the line.

---
V2: Use sizeof() for ZSTR1_LEN - Dmitry

---
  include/linux/interrupt.h |    1 +
  kernel/irq/proc.c         |   43 ++++++++++++++++++++++++++++++++++++++-----
  2 files changed, 39 insertions(+), 5 deletions(-)
include/linux/interrupt.h +1 -0
@@ -864,6 +864,7 @@ static inline void init_irq_proc(void)
  struct seq_file;
  int show_interrupts(struct seq_file *p, void *v);
  int arch_show_interrupts(struct seq_file *p, int prec);
+void irq_proc_emit_counts(struct seq_file *p, unsigned int __percpu *cnts);
 
  extern int early_irq_init(void);
  extern int arch_probe_nr_irqs(void);

kernel/irq/proc.c int __weak arch_show_interrupts(struct s · line 453

Quoted 4 lines
kernel/irq/proc.c +1 -0
@@ -450,6 +450,43 @@ int __weak arch_show_interrupts(struct s
  # define ACTUAL_NR_IRQS irq_get_nr_irqs()
  #endif
 
+/* Same as seq_put_decimal_ull_width(p, " ", cnt, 10) */

nit: is this comment still valid to be here?

Quoted 43 lines
kernel/irq/proc.c +36 -0
+#define ZSTR1 "          0"
+#define ZSTR1_LEN	(sizeof(ZSTR1) - 1)
+#define ZSTR16		ZSTR1 ZSTR1 ZSTR1 ZSTR1 ZSTR1 ZSTR1 ZSTR1 ZSTR1 \
+			ZSTR1 ZSTR1 ZSTR1 ZSTR1 ZSTR1 ZSTR1 ZSTR1 ZSTR1
+#define ZSTR256		ZSTR16 ZSTR16 ZSTR16 ZSTR16 ZSTR16 ZSTR16 ZSTR16 ZSTR16 \
+			ZSTR16 ZSTR16 ZSTR16 ZSTR16 ZSTR16 ZSTR16 ZSTR16 ZSTR16
+
+static inline void irq_proc_emit_zero_counts(struct seq_file *p, unsigned int zeros)
+{
+	if (!zeros)
+		return;
+
+	for (unsigned int n = min(zeros, 256); n; zeros -= n, n = min(zeros, 256))
+		seq_write(p, ZSTR256, n * ZSTR1_LEN);
+}
+
+static inline unsigned int irq_proc_emit_count(struct seq_file *p, unsigned int cnt,
+					       unsigned int zeros)
+{
+	if (!cnt)
+		return zeros + 1;
+
+	irq_proc_emit_zero_counts(p, zeros);
+	seq_put_decimal_ull_width(p, " ", cnt, 10);
+	return 0;
+}
+
+void irq_proc_emit_counts(struct seq_file *p, unsigned int __percpu *cnts)
+{
+	unsigned int cpu, zeros = 0;
+
+	for_each_online_cpu(cpu)
+		zeros = irq_proc_emit_count(p, per_cpu(*cnts, cpu), zeros);
+	irq_proc_emit_zero_counts(p, zeros);
+}
+
  int show_interrupts(struct seq_file *p, void *v)
  {
  	const unsigned int nr_irqs = irq_get_nr_irqs();
@@ -485,11 +522,7 @@ int show_interrupts(struct seq_file *p,
  		return 0;
 

kernel/irq/proc.c int show_interrupts(struct seq_file *p, · line 528

Quoted 10 lines
kernel/irq/proc.c +1 -5
  	seq_printf(p, "%*d:", prec, i);
-	for_each_online_cpu(j) {
-		unsigned int cnt = desc->kstat_irqs ? per_cpu(desc->kstat_irqs->cnt, j) : 0;
-
-		seq_put_decimal_ull_width(p, " ", cnt, 10);
-	}
+	irq_proc_emit_counts(p, &desc->kstat_irqs->cnt);
  	seq_putc(p, ' ');
 
  	guard(raw_spinlock_irq)(&desc->lock);

Other than nits, feel free to include

#5 lore raw

[tip: irq/core] genirq/proc: Avoid formatting zero counts in /proc/interrupts

In reply to Thomas Gleixner
The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     95c33a64f203be444954a1e1d855a4820c4f0efa
Gitweb:        https://git.kernel.org/tip/95c33a64f203be444954a1e1d855a4820c4f0efa
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Sun, 17 May 2026 22:01:38 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Tue, 26 May 2026 16:21:11 +02:00

genirq/proc: Avoid formatting zero counts in /proc/interrupts

A large portion of interrupt count entries are zero. There is no point in formatting the zero value as it is way cheeper to just emit a constant string.

Collect the number of consecutive zero counts and emit them in one go before a non-zero count and at the end of the line.

---
 include/linux/interrupt.h |  1 +-
 kernel/irq/proc.c         | 43 +++++++++++++++++++++++++++++++++-----
 2 files changed, 39 insertions(+), 5 deletions(-)
include/linux/interrupt.h +1 -0
index 6cd26ff..3bf969a 100644
@@ -864,6 +864,7 @@ static inline void init_irq_proc(void)
 struct seq_file;
 int show_interrupts(struct seq_file *p, void *v);
 int arch_show_interrupts(struct seq_file *p, int prec);
+void irq_proc_emit_counts(struct seq_file *p, unsigned int __percpu *cnts);
 
 extern int early_irq_init(void);
 extern int arch_probe_nr_irqs(void);
kernel/irq/proc.c +38 -5

55 lines not on this page · raw message

author maintainer
#6 lore raw

PATCH v6 3/16 genirq/proc: Utilize irq_desc::tot_count to avoid evaluation

In reply to Thomas Gleixner

From: Thomas Gleixner <[email protected]>

Interrupts which are not marked per CPU increment not only the per CPU statistics, but also the accumulation counter irq_desc::tot_count.

Change the counter to type unsigned long so it does not produce sporadic zeros due to wrap arounds on 64-bit machines and do a quick check for non per CPU interrupts. If the counter is zero, then simply emit a full set of zero strings. That spares the evaluation of the per CPU counters completely for interrupts with zero events.

---
 include/linux/irqdesc.h |    6 +++---
 kernel/irq/proc.c       |   11 ++++++++++-
 2 files changed, 13 insertions(+), 4 deletions(-)
include/linux/irqdesc.h +3 -3
@@ -52,8 +52,8 @@ struct irq_redirect {
  * @depth:		disable-depth, for nested irq_disable() calls
  * @wake_depth:		enable depth, for multiple irq_set_irq_wake() callers
  * @tot_count:		stats field for non-percpu irqs
- * @irq_count:		stats field to detect stalled irqs
  * @last_unhandled:	aging timer for unhandled count
+ * @irq_count:		stats field to detect stalled irqs
  * @irqs_unhandled:	stats field for spurious unhandled interrupts
  * @threads_handled:	stats field for deferred spurious detection of threaded handlers
  * @threads_handled_last: comparator field for deferred spurious detection of threaded handlers
@@ -87,9 +87,9 @@ struct irq_desc {
 	unsigned int		core_internal_state__do_not_mess_with_it;
 	unsigned int		depth;		/* nested irq disables */
 	unsigned int		wake_depth;	/* nested wake enables */
-	unsigned int		tot_count;
-	unsigned int		irq_count;	/* For detecting broken IRQs */
+	unsigned long		tot_count;
 	unsigned long		last_unhandled;	/* Aging timer for unhandled count */
+	unsigned int		irq_count;	/* For detecting broken IRQs */
 	unsigned int		irqs_unhandled;
 	atomic_t		threads_handled;
 	int			threads_handled_last;
kernel/irq/proc.c +10 -1
@@ -522,7 +522,16 @@ int show_interrupts(struct seq_file *p,
 		return 0;
 
 	seq_printf(p, "%*d:", prec, i);
-	irq_proc_emit_counts(p, &desc->kstat_irqs->cnt);
+
+	/*
+	 * Always output per CPU interrupts. Output device interrupts only when
+	 * desc::tot_count is not zero.
+	 */
+	if (irq_settings_is_per_cpu(desc) || irq_settings_is_per_cpu_devid(desc) ||
+	    data_race(desc->tot_count))
+		irq_proc_emit_counts(p, &desc->kstat_irqs->cnt);
+	else
+		irq_proc_emit_zero_counts(p, num_online_cpus());
 	seq_putc(p, ' ');
 
 	guard(raw_spinlock_irq)(&desc->lock);
#7 lore raw

[tip: irq/core] genirq/proc: Utilize irq_desc::tot_count to avoid evaluation

The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     0179464391af9a01b911f441d2dda42ea253dfbd
Gitweb:        https://git.kernel.org/tip/0179464391af9a01b911f441d2dda42ea253dfbd
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Sun, 17 May 2026 22:01:43 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Tue, 26 May 2026 16:21:11 +02:00

genirq/proc: Utilize irq_desc::tot_count to avoid evaluation

Interrupts which are not marked per CPU increment not only the per CPU statistics, but also the accumulation counter irq_desc::tot_count.

Change the counter to type unsigned long so it does not produce sporadic zeros due to wrap arounds on 64-bit machines and do a quick check for non per CPU interrupts. If the counter is zero, then simply emit a full set of zero strings. That spares the evaluation of the per CPU counters completely for interrupts with zero events.

---
 include/linux/irqdesc.h |  6 +++---
 kernel/irq/proc.c       | 11 ++++++++++-
 2 files changed, 13 insertions(+), 4 deletions(-)
include/linux/irqdesc.h +3 -3
index dae9a9b..1058786 100644
@@ -52,8 +52,8 @@ struct irq_redirect {
  * @depth:		disable-depth, for nested irq_disable() calls
  * @wake_depth:		enable depth, for multiple irq_set_irq_wake() callers
  * @tot_count:		stats field for non-percpu irqs
- * @irq_count:		stats field to detect stalled irqs
  * @last_unhandled:	aging timer for unhandled count
+ * @irq_count:		stats field to detect stalled irqs
  * @irqs_unhandled:	stats field for spurious unhandled interrupts
  * @threads_handled:	stats field for deferred spurious detection of threaded handlers
  * @threads_handled_last: comparator field for deferred spurious detection of threaded handlers
@@ -87,9 +87,9 @@ struct irq_desc {
 	unsigned int		core_internal_state__do_not_mess_with_it;
 	unsigned int		depth;		/* nested irq disables */
 	unsigned int		wake_depth;	/* nested wake enables */
-	unsigned int		tot_count;
-	unsigned int		irq_count;	/* For detecting broken IRQs */
+	unsigned long		tot_count;
 	unsigned long		last_unhandled;	/* Aging timer for unhandled count */
+	unsigned int		irq_count;	/* For detecting broken IRQs */
 	unsigned int		irqs_unhandled;
 	atomic_t		threads_handled;
 	int			threads_handled_last;
kernel/irq/proc.c +10 -1
index 378b523..5a1805b 100644
@@ -523,7 +523,16 @@ int show_interrupts(struct seq_file *p, void *v)
 		return 0;
 
 	seq_printf(p, "%*d:", prec, i);
-	irq_proc_emit_counts(p, &desc->kstat_irqs->cnt);
+
+	/*
+	 * Always output per CPU interrupts. Output device interrupts only when
+	 * desc::tot_count is not zero.
+	 */
+	if (irq_settings_is_per_cpu(desc) || irq_settings_is_per_cpu_devid(desc) ||
+	    data_race(desc->tot_count))
+		irq_proc_emit_counts(p, &desc->kstat_irqs->cnt);
+	else
+		irq_proc_emit_zero_counts(p, num_online_cpus());
 	seq_putc(p, ' ');
 
 	guard(raw_spinlock_irq)(&desc->lock);
author maintainer
#8 lore raw

PATCH v6 4/16 x86/irq: Make irqstats array based

In reply to Thomas Gleixner

From: Thomas Gleixner <[email protected]>

Having the x86 specific interrupt statistics as a data structure with individual members instead of an array is just stupid as it requires endless copy and paste in arch_show_interrupts() and arch_irq_stat_cpu(), where the latter does not even take the latest interrupt additions into account. The resulting #ifdef orgy is just disgusting.

Convert it to an array of counters, which does not make a difference in the actual interrupt hotpath increment as the array index is constant and therefore not any different than the member based access.

But in arch_show_interrupts() and arch_irq_stat_cpu() this just turns into a loop, which reduces the text size by ~2k (~12%):

   text	   data	    bss	    dec	    hex	filename
  19643	  15250	    904	  35797	   8bd5	../build/arch/x86/kernel/irq.o
  17355	  15250	    904	  33509	   82e5	../build/arch/x86/kernel/irq.o

Adding a new vector or software counter only requires to update the table and everything just works. Using the core provided emit function which speeds up 0 outputs makes it significantly faster.

---
V6: Fix the text alignment of VPMI - Michael
V5: Move the inc and enable function to the next patch
Use COUNT and not VECTOR for the AMD deferred part
V4: Make irq_stats_init() a late initcall so that platform IPI and posted
vector initialization has been done
Supress AMD/HYGON specific vectors when CPU is from a different vendor

V3: Add the missing #ifdeffery - 0-day
Add the alignment back - Radu
Address dyslexia for real - Michael

V2: Simplified and extended vector skip mechanism
Fixup the typoes - Micheal, Dmitry
Added the lost precision back for ERR/MIS - Dmitry

---
 arch/x86/events/amd/core.c          |    2 
 arch/x86/events/amd/ibs.c           |    2 
 arch/x86/events/core.c              |    2 
 arch/x86/events/intel/core.c        |    2 
 arch/x86/events/intel/knc.c         |    2 
 arch/x86/events/intel/p4.c          |    2 
 arch/x86/events/zhaoxin/core.c      |    2 
 arch/x86/hyperv/hv_init.c           |    2 
 arch/x86/include/asm/hardirq.h      |   77 ++++++----
 arch/x86/include/asm/mce.h          |    3 
 arch/x86/kernel/apic/apic.c         |    4 
 arch/x86/kernel/apic/ipi.c          |    2 
 arch/x86/kernel/cpu/acrn.c          |    2 
 arch/x86/kernel/cpu/mce/amd.c       |    2 
 arch/x86/kernel/cpu/mce/core.c      |    8 -
 arch/x86/kernel/cpu/mce/threshold.c |    2 
 arch/x86/kernel/cpu/mshyperv.c      |    4 
 arch/x86/kernel/irq.c               |  259 +++++++++++++-----------------------
 arch/x86/kernel/irq_work.c          |    2 
 arch/x86/kernel/kvm.c               |    2 
 arch/x86/kernel/nmi.c               |    4 
 arch/x86/kernel/smp.c               |    6 
 arch/x86/mm/tlb.c                   |    2 
 arch/x86/xen/enlighten_hvm.c        |    2 
 arch/x86/xen/enlighten_pv.c         |    2 
 arch/x86/xen/smp.c                  |    6 
 arch/x86/xen/smp_pv.c               |    2 
 27 files changed, 174 insertions(+), 233 deletions(-)
arch/x86/events/amd/core.c +1 -1
@@ -1032,7 +1032,7 @@ static int amd_pmu_v2_handle_irq(struct
 	 * Unmasking the LVTPC is not required as the Mask (M) bit of the LVT
 	 * PMI entry is not set by the local APIC when a PMC overflow occurs
 	 */
-	inc_irq_stat(apic_perf_irqs);
+	inc_perf_irq_stat();
 
 done:
 	cpuc->enabled = pmu_enabled;
arch/x86/events/amd/ibs.c +1 -1
@@ -1600,7 +1600,7 @@ perf_ibs_nmi_handler(unsigned int cmd, s
 	handled += perf_ibs_handle_irq(&perf_ibs_op, regs);
 
 	if (handled)
-		inc_irq_stat(apic_perf_irqs);
+		inc_perf_irq_stat();
 
 	perf_sample_event_took(sched_clock() - stamp);
 
arch/x86/events/core.c +1 -1
@@ -1750,7 +1750,7 @@ int x86_pmu_handle_irq(struct pt_regs *r
 	}
 
 	if (handled)
-		inc_irq_stat(apic_perf_irqs);
+		inc_perf_irq_stat();
 
 	return handled;
 }
arch/x86/events/intel/core.c +1 -1
@@ -3504,7 +3504,7 @@ static int handle_pmi_common(struct pt_r
 	int bit;
 	int handled = 0;
 
-	inc_irq_stat(apic_perf_irqs);
+	inc_perf_irq_stat();
 
 	/*
 	 * Ignore a range of extra bits in status that do not indicate
arch/x86/events/intel/knc.c +1 -1
@@ -238,7 +238,7 @@ static int knc_pmu_handle_irq(struct pt_
 		goto done;
 	}
 
-	inc_irq_stat(apic_perf_irqs);
+	inc_perf_irq_stat();
 
 	for_each_set_bit(bit, (unsigned long *)&status, X86_PMC_IDX_MAX) {
 		struct perf_event *event = cpuc->events[bit];
arch/x86/events/intel/p4.c +1 -1
@@ -1077,7 +1077,7 @@ static int p4_pmu_handle_irq(struct pt_r
 	}
 
 	if (handled)
-		inc_irq_stat(apic_perf_irqs);
+		inc_perf_irq_stat();
 
 	/*
 	 * When dealing with the unmasking of the LVTPC on P4 perf hw, it has
arch/x86/events/zhaoxin/core.c +1 -1
@@ -373,7 +373,7 @@ static int zhaoxin_pmu_handle_irq(struct
 	else
 		zhaoxin_pmu_ack_status(status);
 
-	inc_irq_stat(apic_perf_irqs);
+	inc_perf_irq_stat();
 
 	/*
 	 * CondChgd bit 63 doesn't mean any overflow status. Ignore
arch/x86/hyperv/hv_init.c +1 -1
@@ -219,7 +219,7 @@ static inline bool hv_reenlightenment_av
 DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_reenlightenment)
 {
 	apic_eoi();
-	inc_irq_stat(irq_hv_reenlightenment_count);
+	inc_irq_stat(HYPERV_REENLIGHTENMENT);
 	schedule_delayed_work(&hv_reenlightenment_work, HZ/10);
 }
 
arch/x86/include/asm/hardirq.h +47 -30

113 lines not on this page · raw message

arch/x86/include/asm/mce.h +0 -3
@@ -291,9 +291,6 @@ bool mce_is_memory_error(struct mce *m);
 bool mce_is_correctable(struct mce *m);
 bool mce_usable_address(struct mce *m);
 
-DECLARE_PER_CPU(unsigned, mce_exception_count);
-DECLARE_PER_CPU(unsigned, mce_poll_count);
-
 typedef DECLARE_BITMAP(mce_banks_t, MAX_NR_BANKS);
 DECLARE_PER_CPU(mce_banks_t, mce_poll_banks);
 
arch/x86/kernel/apic/apic.c +2 -2
@@ -1045,7 +1045,7 @@ static void local_apic_timer_interrupt(v
 	/*
 	 * the NMI deadlock-detector uses this.
 	 */
-	inc_irq_stat(apic_timer_irqs);
+	inc_irq_stat(APIC_TIMER);
 
 	evt->event_handler(evt);
 }
@@ -2114,7 +2114,7 @@ static noinline void handle_spurious_int
 
 	trace_spurious_apic_entry(vector);
 
-	inc_irq_stat(irq_spurious_count);
+	inc_irq_stat(SPURIOUS);
 
 	/*
 	 * If this is a spurious interrupt then do not acknowledge
arch/x86/kernel/apic/ipi.c +1 -1
@@ -120,7 +120,7 @@ u32 apic_mem_wait_icr_idle_timeout(void)
 	for (cnt = 0; cnt < 1000; cnt++) {
 		if (!(apic_read(APIC_ICR) & APIC_ICR_BUSY))
 			return 0;
-		inc_irq_stat(icr_read_retry_count);
+		inc_irq_stat(ICR_READ_RETRY);
 		udelay(100);
 	}
 	return APIC_ICR_BUSY;
arch/x86/kernel/cpu/acrn.c +1 -1
@@ -52,7 +52,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_acrn_hv_ca
 	 * HYPERVISOR_CALLBACK_VECTOR.
 	 */
 	apic_eoi();
-	inc_irq_stat(irq_hv_callback_count);
+	inc_irq_stat(HYPERVISOR_CALLBACK);
 
 	if (acrn_intr_handler)
 		acrn_intr_handler();
arch/x86/kernel/cpu/mce/amd.c +1 -1
@@ -850,7 +850,7 @@ bool amd_mce_usable_address(struct mce *
 DEFINE_IDTENTRY_SYSVEC(sysvec_deferred_error)
 {
 	trace_deferred_error_apic_entry(DEFERRED_ERROR_VECTOR);
-	inc_irq_stat(irq_deferred_error_count);
+	inc_irq_stat(DEFERRED_ERROR);
 	deferred_error_int_vector();
 	trace_deferred_error_apic_exit(DEFERRED_ERROR_VECTOR);
 	apic_eoi();
arch/x86/kernel/cpu/mce/core.c +2 -6
@@ -67,8 +67,6 @@ static DEFINE_MUTEX(mce_sysfs_mutex);
 
 #define SPINUNIT		100	/* 100ns */
 
-DEFINE_PER_CPU(unsigned, mce_exception_count);
-
 DEFINE_PER_CPU_READ_MOSTLY(unsigned int, mce_num_banks);
 
 DEFINE_PER_CPU_READ_MOSTLY(struct mce_bank[MAX_NR_BANKS], mce_banks_array);
@@ -716,8 +714,6 @@ static noinstr void mce_read_aux(struct
 	}
 }
 
-DEFINE_PER_CPU(unsigned, mce_poll_count);
-
 /*
  * We have three scenarios for checking for Deferred errors:
  *
@@ -820,7 +816,7 @@ void machine_check_poll(enum mcp_flags f
 	struct mce *m;
 	int i;
 
-	this_cpu_inc(mce_poll_count);
+	inc_irq_stat(MCE_POLL);
 
 	mce_gather_info(&err, NULL);
 	m = &err.m;
@@ -1595,7 +1591,7 @@ noinstr void do_machine_check(struct pt_
 	 */
 	lmce = 1;
 
-	this_cpu_inc(mce_exception_count);
+	inc_irq_stat(MCE_EXCEPTION);
 
 	mce_gather_info(&err, regs);
 	m = &err.m;
arch/x86/kernel/cpu/mce/threshold.c +1 -1
@@ -37,7 +37,7 @@ void (*mce_threshold_vector)(void) = def
 DEFINE_IDTENTRY_SYSVEC(sysvec_threshold)
 {
 	trace_threshold_apic_entry(THRESHOLD_APIC_VECTOR);
-	inc_irq_stat(irq_threshold_count);
+	inc_irq_stat(THRESHOLD_APIC);
 	mce_threshold_vector();
 	trace_threshold_apic_exit(THRESHOLD_APIC_VECTOR);
 	apic_eoi();
arch/x86/kernel/cpu/mshyperv.c +2 -2
@@ -154,7 +154,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_cal
 {
 	struct pt_regs *old_regs = set_irq_regs(regs);
 
-	inc_irq_stat(irq_hv_callback_count);
+	inc_irq_stat(HYPERVISOR_CALLBACK);
 	if (mshv_handler)
 		mshv_handler();
 
@@ -193,7 +193,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_sti
 {
 	struct pt_regs *old_regs = set_irq_regs(regs);
 
-	inc_irq_stat(hyperv_stimer0_count);
+	inc_irq_stat(HYPERV_STIMER0);
 	if (hv_stimer0_handler)
 		hv_stimer0_handler();
 	add_interrupt_randomness(HYPERV_STIMER0_VECTOR);
arch/x86/kernel/irq.c +95 -164

353 lines not on this page · raw message

arch/x86/kernel/irq_work.c +1 -1
@@ -18,7 +18,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_irq_work)
 {
 	apic_eoi();
 	trace_irq_work_entry(IRQ_WORK_VECTOR);
-	inc_irq_stat(apic_irq_work_irqs);
+	inc_irq_stat(IRQ_WORK);
 	irq_work_run();
 	trace_irq_work_exit(IRQ_WORK_VECTOR);
 }
arch/x86/kernel/kvm.c +1 -1
@@ -304,7 +304,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_kvm_asyncp
 
 	apic_eoi();
 
-	inc_irq_stat(irq_hv_callback_count);
+	inc_irq_stat(HYPERVISOR_CALLBACK);
 
 	if (__this_cpu_read(async_pf_enabled)) {
 		token = __this_cpu_read(apf_reason.token);
arch/x86/kernel/nmi.c +2 -2
@@ -576,7 +576,7 @@ DEFINE_IDTENTRY_RAW(exc_nmi)
 
 	irq_state = irqentry_nmi_enter(regs);
 
-	inc_irq_stat(__nmi_count);
+	inc_irq_stat(NMI);
 
 	if (IS_ENABLED(CONFIG_NMI_CHECK_CPU) && ignore_nmis) {
 		WRITE_ONCE(nsp->idt_ignored, nsp->idt_ignored + 1);
@@ -725,7 +725,7 @@ DEFINE_FREDENTRY_NMI(exc_nmi)
 
 	irq_state = irqentry_nmi_enter(regs);
 
-	inc_irq_stat(__nmi_count);
+	inc_irq_stat(NMI);
 	default_do_nmi(regs);
 
 	irqentry_nmi_exit(regs, irq_state);
arch/x86/kernel/smp.c +3 -3
@@ -250,7 +250,7 @@ DEFINE_IDTENTRY_SYSVEC_SIMPLE(sysvec_res
 {
 	apic_eoi();
 	trace_reschedule_entry(RESCHEDULE_VECTOR);
-	inc_irq_stat(irq_resched_count);
+	inc_irq_stat(RESCHEDULE);
 	scheduler_ipi();
 	trace_reschedule_exit(RESCHEDULE_VECTOR);
 }
@@ -259,7 +259,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_call_funct
 {
 	apic_eoi();
 	trace_call_function_entry(CALL_FUNCTION_VECTOR);
-	inc_irq_stat(irq_call_count);
+	inc_irq_stat(CALL_FUNCTION);
 	generic_smp_call_function_interrupt();
 	trace_call_function_exit(CALL_FUNCTION_VECTOR);
 }
@@ -268,7 +268,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_call_funct
 {
 	apic_eoi();
 	trace_call_function_single_entry(CALL_FUNCTION_SINGLE_VECTOR);
-	inc_irq_stat(irq_call_count);
+	inc_irq_stat(CALL_FUNCTION);
 	generic_smp_call_function_single_interrupt();
 	trace_call_function_single_exit(CALL_FUNCTION_SINGLE_VECTOR);
 }
arch/x86/mm/tlb.c +1 -1
@@ -1123,7 +1123,7 @@ static void flush_tlb_func(void *info)
 	VM_WARN_ON(!irqs_disabled());
 
 	if (!local) {
-		inc_irq_stat(irq_tlb_count);
+		inc_irq_stat(TLB);
 		count_vm_tlb_event(NR_TLB_REMOTE_FLUSH_RECEIVED);
 	}
 
arch/x86/xen/enlighten_hvm.c +1 -1
@@ -125,7 +125,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_xen_hvm_ca
 	if (xen_percpu_upcall)
 		apic_eoi();
 
-	inc_irq_stat(irq_hv_callback_count);
+	inc_irq_stat(HYPERVISOR_CALLBACK);
 
 	xen_evtchn_do_upcall();
 
arch/x86/xen/enlighten_pv.c +1 -1
@@ -728,7 +728,7 @@ static void __xen_pv_evtchn_do_upcall(st
 {
 	struct pt_regs *old_regs = set_irq_regs(regs);
 
-	inc_irq_stat(irq_hv_callback_count);
+	inc_irq_stat(HYPERVISOR_CALLBACK);
 
 	xen_evtchn_do_upcall();
 
arch/x86/xen/smp.c +3 -3
@@ -23,7 +23,7 @@ static irqreturn_t xen_call_function_sin
  */
 static irqreturn_t xen_reschedule_interrupt(int irq, void *dev_id)
 {
-	inc_irq_stat(irq_resched_count);
+	inc_irq_stat(RESCHEDULE);
 	scheduler_ipi();
 
 	return IRQ_HANDLED;
@@ -254,7 +254,7 @@ void xen_send_IPI_allbutself(int vector)
 static irqreturn_t xen_call_function_interrupt(int irq, void *dev_id)
 {
 	generic_smp_call_function_interrupt();
-	inc_irq_stat(irq_call_count);
+	inc_irq_stat(CALL_FUNCTION);
 
 	return IRQ_HANDLED;
 }
@@ -262,7 +262,7 @@ static irqreturn_t xen_call_function_int
 static irqreturn_t xen_call_function_single_interrupt(int irq, void *dev_id)
 {
 	generic_smp_call_function_single_interrupt();
-	inc_irq_stat(irq_call_count);
+	inc_irq_stat(CALL_FUNCTION);
 
 	return IRQ_HANDLED;
 }
arch/x86/xen/smp_pv.c +1 -1
@@ -400,7 +400,7 @@ static void xen_pv_stop_other_cpus(int w
 static irqreturn_t xen_irq_work_interrupt(int irq, void *dev_id)
 {
 	irq_work_run();
-	inc_irq_stat(apic_irq_work_irqs);
+	inc_irq_stat(IRQ_WORK);
 
 	return IRQ_HANDLED;
 }
#9 lore raw

[tip: irq/core] x86/irq: Make irqstats array based

The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     2b57c69917eeba3ee657f252257e37f31916ba2a
Gitweb:        https://git.kernel.org/tip/2b57c69917eeba3ee657f252257e37f31916ba2a
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Sun, 17 May 2026 22:01:48 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Tue, 26 May 2026 16:21:12 +02:00

x86/irq: Make irqstats array based

Having the x86 specific interrupt statistics as a data structure with individual members instead of an array is just stupid as it requires endless copy and paste in arch_show_interrupts() and arch_irq_stat_cpu(), where the latter does not even take the latest interrupt additions into account. The resulting #ifdef orgy is just disgusting.

Convert it to an array of counters, which does not make a difference in the actual interrupt hotpath increment as the array index is constant and therefore not any different than the member based access.

But in arch_show_interrupts() and arch_irq_stat_cpu() this just turns into a loop, which reduces the text size by ~2k (~12%):

   text	   data	    bss	    dec	    hex	filename
  19643	  15250	    904	  35797	   8bd5	../build/arch/x86/kernel/irq.o
  17355	  15250	    904	  33509	   82e5	../build/arch/x86/kernel/irq.o

Adding a new vector or software counter only requires to update the table and everything just works. Using the core provided emit function which speeds up 0 outputs makes it significantly faster.

---
 arch/x86/events/amd/core.c          |   2 +-
 arch/x86/events/amd/ibs.c           |   2 +-
 arch/x86/events/core.c              |   2 +-
 arch/x86/events/intel/core.c        |   2 +-
 arch/x86/events/intel/knc.c         |   2 +-
 arch/x86/events/intel/p4.c          |   2 +-
 arch/x86/events/zhaoxin/core.c      |   2 +-
 arch/x86/hyperv/hv_init.c           |   2 +-
 arch/x86/include/asm/hardirq.h      |  77 ++++----
 arch/x86/include/asm/mce.h          |   3 +-
 arch/x86/kernel/apic/apic.c         |   4 +-
 arch/x86/kernel/apic/ipi.c          |   2 +-
 arch/x86/kernel/cpu/acrn.c          |   2 +-
 arch/x86/kernel/cpu/mce/amd.c       |   2 +-
 arch/x86/kernel/cpu/mce/core.c      |   8 +-
 arch/x86/kernel/cpu/mce/threshold.c |   2 +-
 arch/x86/kernel/cpu/mshyperv.c      |   4 +-
 arch/x86/kernel/irq.c               | 259 +++++++++------------------
 arch/x86/kernel/irq_work.c          |   2 +-
 arch/x86/kernel/kvm.c               |   2 +-
 arch/x86/kernel/nmi.c               |   4 +-
 arch/x86/kernel/smp.c               |   6 +-
 arch/x86/mm/tlb.c                   |   2 +-
 arch/x86/xen/enlighten_hvm.c        |   2 +-
 arch/x86/xen/enlighten_pv.c         |   2 +-
 arch/x86/xen/smp.c                  |   6 +-
 arch/x86/xen/smp_pv.c               |   2 +-
 27 files changed, 174 insertions(+), 233 deletions(-)
arch/x86/events/amd/core.c +1 -1
index 0c92ed5..305774b 100644
@@ -1032,7 +1032,7 @@ static int amd_pmu_v2_handle_irq(struct pt_regs *regs)
 	 * Unmasking the LVTPC is not required as the Mask (M) bit of the LVT
 	 * PMI entry is not set by the local APIC when a PMC overflow occurs
 	 */
-	inc_irq_stat(apic_perf_irqs);
+	inc_perf_irq_stat();
 
 done:
 	cpuc->enabled = pmu_enabled;
arch/x86/events/amd/ibs.c +1 -1
index e0bd505..ed18a6d 100644
@@ -1600,7 +1600,7 @@ perf_ibs_nmi_handler(unsigned int cmd, struct pt_regs *regs)
 	handled += perf_ibs_handle_irq(&perf_ibs_op, regs);
 
 	if (handled)
-		inc_irq_stat(apic_perf_irqs);
+		inc_perf_irq_stat();
 
 	perf_sample_event_took(sched_clock() - stamp);
 
arch/x86/events/core.c +1 -1
index 810ab21..244ba20 100644
@@ -1750,7 +1750,7 @@ int x86_pmu_handle_irq(struct pt_regs *regs)
 	}
 
 	if (handled)
-		inc_irq_stat(apic_perf_irqs);
+		inc_perf_irq_stat();
 
 	return handled;
 }
arch/x86/events/intel/core.c +1 -1
index d9488ad..2d0802c 100644
@@ -3504,7 +3504,7 @@ static int handle_pmi_common(struct pt_regs *regs, u64 status)
 	int bit;
 	int handled = 0;
 
-	inc_irq_stat(apic_perf_irqs);
+	inc_perf_irq_stat();
 
 	/*
 	 * Ignore a range of extra bits in status that do not indicate
arch/x86/events/intel/knc.c +1 -1
index e614baf..e887adc 100644
@@ -238,7 +238,7 @@ again:
 		goto done;
 	}
 
-	inc_irq_stat(apic_perf_irqs);
+	inc_perf_irq_stat();
 
 	for_each_set_bit(bit, (unsigned long *)&status, X86_PMC_IDX_MAX) {
 		struct perf_event *event = cpuc->events[bit];
arch/x86/events/intel/p4.c +1 -1
index 02bfdb7..5368dc3 100644
@@ -1077,7 +1077,7 @@ static int p4_pmu_handle_irq(struct pt_regs *regs)
 	}
 
 	if (handled)
-		inc_irq_stat(apic_perf_irqs);
+		inc_perf_irq_stat();
 
 	/*
 	 * When dealing with the unmasking of the LVTPC on P4 perf hw, it has
arch/x86/events/zhaoxin/core.c +1 -1
index 4bdfcf0..4bc177b 100644
@@ -373,7 +373,7 @@ again:
 	else
 		zhaoxin_pmu_ack_status(status);
 
-	inc_irq_stat(apic_perf_irqs);
+	inc_perf_irq_stat();
 
 	/*
 	 * CondChgd bit 63 doesn't mean any overflow status. Ignore
arch/x86/hyperv/hv_init.c +1 -1
index 323adc9..55a8b6d 100644
@@ -219,7 +219,7 @@ static inline bool hv_reenlightenment_available(void)
 DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_reenlightenment)
 {
 	apic_eoi();
-	inc_irq_stat(irq_hv_reenlightenment_count);
+	inc_irq_stat(HYPERV_REENLIGHTENMENT);
 	schedule_delayed_work(&hv_reenlightenment_work, HZ/10);
 }
 
arch/x86/include/asm/hardirq.h +47 -30

113 lines not on this page · raw message

arch/x86/include/asm/mce.h +0 -3
index 0175d39..e575b70 100644
@@ -291,9 +291,6 @@ bool mce_is_memory_error(struct mce *m);
 bool mce_is_correctable(struct mce *m);
 bool mce_usable_address(struct mce *m);
 
-DECLARE_PER_CPU(unsigned, mce_exception_count);
-DECLARE_PER_CPU(unsigned, mce_poll_count);
-
 typedef DECLARE_BITMAP(mce_banks_t, MAX_NR_BANKS);
 DECLARE_PER_CPU(mce_banks_t, mce_poll_banks);
 
arch/x86/kernel/apic/apic.c +2 -2
index 6399049..6599e80 100644
@@ -1045,7 +1045,7 @@ static void local_apic_timer_interrupt(void)
 	/*
 	 * the NMI deadlock-detector uses this.
 	 */
-	inc_irq_stat(apic_timer_irqs);
+	inc_irq_stat(APIC_TIMER);
 
 	evt->event_handler(evt);
 }
@@ -2114,7 +2114,7 @@ static noinline void handle_spurious_interrupt(u8 vector)
 
 	trace_spurious_apic_entry(vector);
 
-	inc_irq_stat(irq_spurious_count);
+	inc_irq_stat(SPURIOUS);
 
 	/*
 	 * If this is a spurious interrupt then do not acknowledge
arch/x86/kernel/apic/ipi.c +1 -1
index 98a57cb..3635c4d 100644
@@ -120,7 +120,7 @@ u32 apic_mem_wait_icr_idle_timeout(void)
 	for (cnt = 0; cnt < 1000; cnt++) {
 		if (!(apic_read(APIC_ICR) & APIC_ICR_BUSY))
 			return 0;
-		inc_irq_stat(icr_read_retry_count);
+		inc_irq_stat(ICR_READ_RETRY);
 		udelay(100);
 	}
 	return APIC_ICR_BUSY;
arch/x86/kernel/cpu/acrn.c +1 -1
index 2c5b51a..dc119af 100644
@@ -52,7 +52,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_acrn_hv_callback)
 	 * HYPERVISOR_CALLBACK_VECTOR.
 	 */
 	apic_eoi();
-	inc_irq_stat(irq_hv_callback_count);
+	inc_irq_stat(HYPERVISOR_CALLBACK);
 
 	if (acrn_intr_handler)
 		acrn_intr_handler();
arch/x86/kernel/cpu/mce/amd.c +1 -1
index 6605a02..222fa9c 100644
@@ -850,7 +850,7 @@ bool amd_mce_usable_address(struct mce *m)
 DEFINE_IDTENTRY_SYSVEC(sysvec_deferred_error)
 {
 	trace_deferred_error_apic_entry(DEFERRED_ERROR_VECTOR);
-	inc_irq_stat(irq_deferred_error_count);
+	inc_irq_stat(DEFERRED_ERROR);
 	deferred_error_int_vector();
 	trace_deferred_error_apic_exit(DEFERRED_ERROR_VECTOR);
 	apic_eoi();
arch/x86/kernel/cpu/mce/core.c +2 -6
index 8dd424a..77cad8d 100644
@@ -67,8 +67,6 @@ static DEFINE_MUTEX(mce_sysfs_mutex);
 
 #define SPINUNIT		100	/* 100ns */
 
-DEFINE_PER_CPU(unsigned, mce_exception_count);
-
 DEFINE_PER_CPU_READ_MOSTLY(unsigned int, mce_num_banks);
 
 DEFINE_PER_CPU_READ_MOSTLY(struct mce_bank[MAX_NR_BANKS], mce_banks_array);
@@ -716,8 +714,6 @@ static noinstr void mce_read_aux(struct mce_hw_err *err, int i)
 	}
 }
 
-DEFINE_PER_CPU(unsigned, mce_poll_count);
-
 /*
  * We have three scenarios for checking for Deferred errors:
  *
@@ -820,7 +816,7 @@ void machine_check_poll(enum mcp_flags flags, mce_banks_t *b)
 	struct mce *m;
 	int i;
 
-	this_cpu_inc(mce_poll_count);
+	inc_irq_stat(MCE_POLL);
 
 	mce_gather_info(&err, NULL);
 	m = &err.m;
@@ -1595,7 +1591,7 @@ noinstr void do_machine_check(struct pt_regs *regs)
 	 */
 	lmce = 1;
 
-	this_cpu_inc(mce_exception_count);
+	inc_irq_stat(MCE_EXCEPTION);
 
 	mce_gather_info(&err, regs);
 	m = &err.m;
arch/x86/kernel/cpu/mce/threshold.c +1 -1
index 0d13c9f..6c370d5 100644
@@ -37,7 +37,7 @@ void (*mce_threshold_vector)(void) = default_threshold_interrupt;
 DEFINE_IDTENTRY_SYSVEC(sysvec_threshold)
 {
 	trace_threshold_apic_entry(THRESHOLD_APIC_VECTOR);
-	inc_irq_stat(irq_threshold_count);
+	inc_irq_stat(THRESHOLD_APIC);
 	mce_threshold_vector();
 	trace_threshold_apic_exit(THRESHOLD_APIC_VECTOR);
 	apic_eoi();
arch/x86/kernel/cpu/mshyperv.c +2 -2
index b5b6a58..9381102 100644
@@ -154,7 +154,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_callback)
 {
 	struct pt_regs *old_regs = set_irq_regs(regs);
 
-	inc_irq_stat(irq_hv_callback_count);
+	inc_irq_stat(HYPERVISOR_CALLBACK);
 	if (mshv_handler)
 		mshv_handler();
 
@@ -193,7 +193,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_stimer0)
 {
 	struct pt_regs *old_regs = set_irq_regs(regs);
 
-	inc_irq_stat(hyperv_stimer0_count);
+	inc_irq_stat(HYPERV_STIMER0);
 	if (hv_stimer0_handler)
 		hv_stimer0_handler();
 	add_interrupt_randomness(HYPERV_STIMER0_VECTOR);
arch/x86/kernel/irq.c +95 -164

353 lines not on this page · raw message

arch/x86/kernel/irq_work.c +1 -1
index b0a24de..308c624 100644
@@ -18,7 +18,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_irq_work)
 {
 	apic_eoi();
 	trace_irq_work_entry(IRQ_WORK_VECTOR);
-	inc_irq_stat(apic_irq_work_irqs);
+	inc_irq_stat(IRQ_WORK);
 	irq_work_run();
 	trace_irq_work_exit(IRQ_WORK_VECTOR);
 }
arch/x86/kernel/kvm.c +1 -1
index 29226d1..d1f3f32 100644
@@ -304,7 +304,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_kvm_asyncpf_interrupt)
 
 	apic_eoi();
 
-	inc_irq_stat(irq_hv_callback_count);
+	inc_irq_stat(HYPERVISOR_CALLBACK);
 
 	if (__this_cpu_read(async_pf_enabled)) {
 		token = __this_cpu_read(apf_reason.token);
arch/x86/kernel/nmi.c +2 -2
index 3d239ed..a7f56e7 100644
@@ -576,7 +576,7 @@ nmi_restart:
 
 	irq_state = irqentry_nmi_enter(regs);
 
-	inc_irq_stat(__nmi_count);
+	inc_irq_stat(NMI);
 
 	if (IS_ENABLED(CONFIG_NMI_CHECK_CPU) && ignore_nmis) {
 		WRITE_ONCE(nsp->idt_ignored, nsp->idt_ignored + 1);
@@ -725,7 +725,7 @@ DEFINE_FREDENTRY_NMI(exc_nmi)
 
 	irq_state = irqentry_nmi_enter(regs);
 
-	inc_irq_stat(__nmi_count);
+	inc_irq_stat(NMI);
 	default_do_nmi(regs);
 
 	irqentry_nmi_exit(regs, irq_state);
arch/x86/kernel/smp.c +3 -3
index cbf95fe..985103c 100644
@@ -250,7 +250,7 @@ DEFINE_IDTENTRY_SYSVEC_SIMPLE(sysvec_reschedule_ipi)
 {
 	apic_eoi();
 	trace_reschedule_entry(RESCHEDULE_VECTOR);
-	inc_irq_stat(irq_resched_count);
+	inc_irq_stat(RESCHEDULE);
 	scheduler_ipi();
 	trace_reschedule_exit(RESCHEDULE_VECTOR);
 }
@@ -259,7 +259,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_call_function)
 {
 	apic_eoi();
 	trace_call_function_entry(CALL_FUNCTION_VECTOR);
-	inc_irq_stat(irq_call_count);
+	inc_irq_stat(CALL_FUNCTION);
 	generic_smp_call_function_interrupt();
 	trace_call_function_exit(CALL_FUNCTION_VECTOR);
 }
@@ -268,7 +268,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_call_function_single)
 {
 	apic_eoi();
 	trace_call_function_single_entry(CALL_FUNCTION_SINGLE_VECTOR);
-	inc_irq_stat(irq_call_count);
+	inc_irq_stat(CALL_FUNCTION);
 	generic_smp_call_function_single_interrupt();
 	trace_call_function_single_exit(CALL_FUNCTION_SINGLE_VECTOR);
 }
arch/x86/mm/tlb.c +1 -1
index af43d17..4c045f8 100644
@@ -1123,7 +1123,7 @@ static void flush_tlb_func(void *info)
 	VM_WARN_ON(!irqs_disabled());
 
 	if (!local) {
-		inc_irq_stat(irq_tlb_count);
+		inc_irq_stat(TLB);
 		count_vm_tlb_event(NR_TLB_REMOTE_FLUSH_RECEIVED);
 	}
 
arch/x86/xen/enlighten_hvm.c +1 -1
index 2f9fa27..6c4eac4 100644
@@ -125,7 +125,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_xen_hvm_callback)
 	if (xen_percpu_upcall)
 		apic_eoi();
 
-	inc_irq_stat(irq_hv_callback_count);
+	inc_irq_stat(HYPERVISOR_CALLBACK);
 
 	xen_evtchn_do_upcall();
 
arch/x86/xen/enlighten_pv.c +1 -1
index ed2d7a3..ea19428 100644
@@ -728,7 +728,7 @@ static void __xen_pv_evtchn_do_upcall(struct pt_regs *regs)
 {
 	struct pt_regs *old_regs = set_irq_regs(regs);
 
-	inc_irq_stat(irq_hv_callback_count);
+	inc_irq_stat(HYPERVISOR_CALLBACK);
 
 	xen_evtchn_do_upcall();
 
arch/x86/xen/smp.c +3 -3
index 05f92c8..05ee0d3 100644
@@ -23,7 +23,7 @@ static irqreturn_t xen_call_function_single_interrupt(int irq, void *dev_id);
  */
 static irqreturn_t xen_reschedule_interrupt(int irq, void *dev_id)
 {
-	inc_irq_stat(irq_resched_count);
+	inc_irq_stat(RESCHEDULE);
 	scheduler_ipi();
 
 	return IRQ_HANDLED;
@@ -254,7 +254,7 @@ void xen_send_IPI_allbutself(int vector)
 static irqreturn_t xen_call_function_interrupt(int irq, void *dev_id)
 {
 	generic_smp_call_function_interrupt();
-	inc_irq_stat(irq_call_count);
+	inc_irq_stat(CALL_FUNCTION);
 
 	return IRQ_HANDLED;
 }
@@ -262,7 +262,7 @@ static irqreturn_t xen_call_function_interrupt(int irq, void *dev_id)
 static irqreturn_t xen_call_function_single_interrupt(int irq, void *dev_id)
 {
 	generic_smp_call_function_single_interrupt();
-	inc_irq_stat(irq_call_count);
+	inc_irq_stat(CALL_FUNCTION);
 
 	return IRQ_HANDLED;
 }
arch/x86/xen/smp_pv.c +1 -1
index db9b8e2..c2812f8 100644
@@ -400,7 +400,7 @@ static void xen_pv_stop_other_cpus(int wait)
 static irqreturn_t xen_irq_work_interrupt(int irq, void *dev_id)
 {
 	irq_work_run();
-	inc_irq_stat(apic_irq_work_irqs);
+	inc_irq_stat(IRQ_WORK);
 
 	return IRQ_HANDLED;
 }
#10 lore raw

Re: [patch V6 04/16] x86/irq: Make irqstats array based

In reply to Thomas Gleixner

arch/x86/kernel/irq.c void ack_bad_irq(unsigned int irq) · line 164

Quoted 9 lines · Thomas Gleixner
arch/x86/kernel/irq.c +9 -0
+int arch_show_interrupts(struct seq_file *p, int prec)
+{
+	const struct irq_stat_info *info = irq_stat_info;
+
+	for (unsigned int i = 0; i < ARRAY_SIZE(irq_stat_info); i++, info++) {
+		if (info->skip_vector)
+			continue;
+
+		seq_printf(p, "%*s:", prec, info->symbol);
                               ^^^^   ^^^^

Ideally, this would be called "width" instead of "prec" since this isn't a precision specifier "%.*s", it's a width "%*s" specifier.

This was introduced in 2009 in commit 7a81d9a7da03 ("x86: smarten /proc/interrupts output"). It's done consistenly everywhere and it's pretty clear that width is intended.

Static checkers complain because people use precision specifiers for to not write beyond the end of the output buffer and this looks so suspicious.

regards,
dan carpenter

Quoted 9 lines
arch/x86/kernel/irq.c +9 -0
+		irq_proc_emit_counts(p, &irq_stat.counts[i]);
+		seq_puts(p, info->text);
+	}
+
+	seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count));
+	if (IS_ENABLED(CONFIG_X86_IO_APIC))
+		seq_printf(p, "%*s: %10u\n", prec, "MIS", atomic_read(&irq_mis_count));
+	return 0;
+}
author maintainer
#11 lore raw

PATCH v6 5/16 x86/irq: Suppress unlikely interrupt stats by default

In reply to Thomas Gleixner

From: Thomas Gleixner <[email protected]>

Unlikely interrupt counters like the spurious vector and the synthetic APIC ICR read retry show up in /proc/interrupts with all counts 0 most of the time.

As these are events which should never happen, suppress them by default and enable them for output when they actually happen.

This requires a seperate bitmap as the description array is marked __ro_after_init. With that bitmap in place it becomes RO data.

---
V5: Move irq_stat_inc_and_enable() here
V4: Fix the bad idea of writing to __ro_after_init marked data
V3: New patch

---
 arch/x86/include/asm/hardirq.h |    1 +
 arch/x86/kernel/apic/apic.c    |    2 +-
 arch/x86/kernel/apic/ipi.c     |    2 +-
 arch/x86/kernel/irq.c          |   38 ++++++++++++++++++++++++++++----------
 4 files changed, 31 insertions(+), 12 deletions(-)
arch/x86/include/asm/hardirq.h +1 -0
@@ -68,6 +68,7 @@ DECLARE_PER_CPU_ALIGNED(struct pi_desc,
 #define __ARCH_IRQ_STAT
 
 #define inc_irq_stat(index)	this_cpu_inc(irq_stat.counts[IRQ_COUNT_##index])
+void irq_stat_inc_and_enable(enum irq_stat_counts which);
 
 #ifdef CONFIG_X86_LOCAL_APIC
 #define inc_perf_irq_stat()	inc_irq_stat(APIC_PERF)
arch/x86/kernel/apic/apic.c +1 -1
@@ -2114,7 +2114,7 @@ static noinline void handle_spurious_int
 
 	trace_spurious_apic_entry(vector);
 
-	inc_irq_stat(SPURIOUS);
+	irq_stat_inc_and_enable(IRQ_COUNT_SPURIOUS);
 
 	/*
 	 * If this is a spurious interrupt then do not acknowledge
arch/x86/kernel/apic/ipi.c +1 -1
@@ -120,7 +120,7 @@ u32 apic_mem_wait_icr_idle_timeout(void)
 	for (cnt = 0; cnt < 1000; cnt++) {
 		if (!(apic_read(APIC_ICR) & APIC_ICR_BUSY))
 			return 0;
-		inc_irq_stat(ICR_READ_RETRY);
+		irq_stat_inc_and_enable(IRQ_COUNT_ICR_READ_RETRY);
 		udelay(100);
 	}
 	return APIC_ICR_BUSY;
arch/x86/kernel/irq.c +28 -10
@@ -68,19 +68,24 @@ struct irq_stat_info {
 	const char	*text;
 };
 
+#define DEFAULT_SUPPRESSED_VECTOR	UINT_MAX
+
 #define ISS(idx, sym, txt) [IRQ_COUNT_##idx] = { .symbol = sym, .text = txt }
 
 #define ITS(idx, sym, txt) [IRQ_COUNT_##idx] =				\
 	{ .skip_vector = idx## _VECTOR, .symbol = sym, .text = txt }
 
-static struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] __ro_after_init = {
+#define IDS(idx, sym, txt) [IRQ_COUNT_##idx] =				\
+	{ .skip_vector = DEFAULT_SUPPRESSED_VECTOR, .symbol = sym, .text = txt }
+
+static const struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] = {
 	ISS(NMI,			"NMI",	"  Non-maskable interrupts\n"),
 #ifdef CONFIG_X86_LOCAL_APIC
 	ISS(APIC_TIMER,			"LOC",	"  Local timer interrupts\n"),
-	ISS(SPURIOUS,			"SPU",	"  Spurious interrupts\n"),
+	IDS(SPURIOUS,			"SPU",	"  Spurious interrupts\n"),
 	ISS(APIC_PERF,			"PMI",	"  Performance monitoring interrupts\n"),
 	ISS(IRQ_WORK,			"IWI",	"  IRQ work interrupts\n"),
-	ISS(ICR_READ_RETRY,		"RTR",	"  APIC ICR read retries\n"),
+	IDS(ICR_READ_RETRY,		"RTR",	"  APIC ICR read retries\n"),
 	ISS(X86_PLATFORM_IPI,		"PLT",	"  Platform interrupts\n"),
 #endif
 #ifdef CONFIG_SMP
@@ -121,34 +126,47 @@ static struct irq_stat_info irq_stat_inf
 #endif
 };
 
+static DECLARE_BITMAP(irq_stat_count_show, IRQ_COUNT_MAX) __read_mostly;
+
 static int __init irq_init_stats(void)
 {
-	struct irq_stat_info *info = irq_stat_info;
+	const struct irq_stat_info *info = irq_stat_info;
 
 	for (unsigned int i = 0; i < ARRAY_SIZE(irq_stat_info); i++, info++) {
-		if (info->skip_vector && test_bit(info->skip_vector, system_vectors))
-			info->skip_vector = 0;
+		if (!info->skip_vector || (info->skip_vector != DEFAULT_SUPPRESSED_VECTOR &&
+					   test_bit(info->skip_vector, system_vectors)))
+			set_bit(i, irq_stat_count_show);
 	}
 
 #ifdef CONFIG_X86_LOCAL_APIC
 	if (!x86_platform_ipi_callback)
-		irq_stat_info[IRQ_COUNT_X86_PLATFORM_IPI].skip_vector = 1;
+		clear_bit(IRQ_COUNT_X86_PLATFORM_IPI, irq_stat_count_show);
 #endif
 
 #ifdef CONFIG_X86_POSTED_MSI
 	if (!posted_msi_enabled())
-		irq_stat_info[IRQ_COUNT_POSTED_MSI_NOTIFICATION].skip_vector = 1;
+		clear_bit(IRQ_COUNT_POSTED_MSI_NOTIFICATION, irq_stat_count_show);
 #endif
 
 #ifdef CONFIG_X86_MCE_AMD
 	if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD &&
 	    boot_cpu_data.x86_vendor != X86_VENDOR_HYGON)
-		irq_stat_info[IRQ_COUNT_DEFERRED_ERROR].skip_vector = 1;
+		clear_bit(IRQ_COUNT_DEFERRED_ERROR, irq_stat_count_show);
 #endif
 	return 0;
 }
 late_initcall(irq_init_stats);
 
+/*
+ * Used for default enabled counters to increment the stats and to enable the
+ * entry for /proc/interrupts output.
+ */
+void irq_stat_inc_and_enable(enum irq_stat_counts which)
+{
+	this_cpu_inc(irq_stat.counts[which]);
+	set_bit(which, irq_stat_count_show);
+}
+
 #ifdef CONFIG_PROC_FS
 /*
  * /proc/interrupts printing for arch specific interrupts
@@ -158,7 +176,7 @@ int arch_show_interrupts(struct seq_file
 	const struct irq_stat_info *info = irq_stat_info;
 
 	for (unsigned int i = 0; i < ARRAY_SIZE(irq_stat_info); i++, info++) {
-		if (info->skip_vector)
+		if (!test_bit(i, irq_stat_count_show))
 			continue;
 
 		seq_printf(p, "%*s:", prec, info->symbol);
#12 lore raw

Re: [patch V6 05/16] x86/irq: Suppress unlikely interrupt stats by default

Quoted 142 lines · Thomas Gleixner

From: Thomas Gleixner <[email protected]>

Unlikely interrupt counters like the spurious vector and the synthetic APIC ICR read retry show up in /proc/interrupts with all counts 0 most of the time.

As these are events which should never happen, suppress them by default and enable them for output when they actually happen.

This requires a seperate bitmap as the description array is marked __ro_after_init. With that bitmap in place it becomes RO data.

---
V5: Move irq_stat_inc_and_enable() here
V4: Fix the bad idea of writing to __ro_after_init marked data
V3: New patch

---
  arch/x86/include/asm/hardirq.h |    1 +
  arch/x86/kernel/apic/apic.c    |    2 +-
  arch/x86/kernel/apic/ipi.c     |    2 +-
  arch/x86/kernel/irq.c          |   38 ++++++++++++++++++++++++++++----------
  4 files changed, 31 insertions(+), 12 deletions(-)
arch/x86/include/asm/hardirq.h +1 -0
@@ -68,6 +68,7 @@ DECLARE_PER_CPU_ALIGNED(struct pi_desc,
  #define __ARCH_IRQ_STAT
 
  #define inc_irq_stat(index)	this_cpu_inc(irq_stat.counts[IRQ_COUNT_##index])
+void irq_stat_inc_and_enable(enum irq_stat_counts which);
 
  #ifdef CONFIG_X86_LOCAL_APIC
  #define inc_perf_irq_stat()	inc_irq_stat(APIC_PERF)
arch/x86/kernel/apic/apic.c +1 -1
@@ -2114,7 +2114,7 @@ static noinline void handle_spurious_int
 
  	trace_spurious_apic_entry(vector);
 
-	inc_irq_stat(SPURIOUS);
+	irq_stat_inc_and_enable(IRQ_COUNT_SPURIOUS);
 
  	/*
  	 * If this is a spurious interrupt then do not acknowledge
arch/x86/kernel/apic/ipi.c +1 -1
@@ -120,7 +120,7 @@ u32 apic_mem_wait_icr_idle_timeout(void)
  	for (cnt = 0; cnt < 1000; cnt++) {
  		if (!(apic_read(APIC_ICR) & APIC_ICR_BUSY))
  			return 0;
-		inc_irq_stat(ICR_READ_RETRY);
+		irq_stat_inc_and_enable(IRQ_COUNT_ICR_READ_RETRY);
  		udelay(100);
  	}
  	return APIC_ICR_BUSY;
arch/x86/kernel/irq.c +27 -9
@@ -68,19 +68,24 @@ struct irq_stat_info {
  	const char	*text;
  };
 
+#define DEFAULT_SUPPRESSED_VECTOR	UINT_MAX
+
  #define ISS(idx, sym, txt) [IRQ_COUNT_##idx] = { .symbol = sym, .text = txt }
 
  #define ITS(idx, sym, txt) [IRQ_COUNT_##idx] =				\
  	{ .skip_vector = idx## _VECTOR, .symbol = sym, .text = txt }
 
-static struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] __ro_after_init = {
+#define IDS(idx, sym, txt) [IRQ_COUNT_##idx] =				\
+	{ .skip_vector = DEFAULT_SUPPRESSED_VECTOR, .symbol = sym, .text = txt }
+
+static const struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] = {
  	ISS(NMI,			"NMI",	"  Non-maskable interrupts\n"),
  #ifdef CONFIG_X86_LOCAL_APIC
  	ISS(APIC_TIMER,			"LOC",	"  Local timer interrupts\n"),
-	ISS(SPURIOUS,			"SPU",	"  Spurious interrupts\n"),
+	IDS(SPURIOUS,			"SPU",	"  Spurious interrupts\n"),
  	ISS(APIC_PERF,			"PMI",	"  Performance monitoring interrupts\n"),
  	ISS(IRQ_WORK,			"IWI",	"  IRQ work interrupts\n"),
-	ISS(ICR_READ_RETRY,		"RTR",	"  APIC ICR read retries\n"),
+	IDS(ICR_READ_RETRY,		"RTR",	"  APIC ICR read retries\n"),
  	ISS(X86_PLATFORM_IPI,		"PLT",	"  Platform interrupts\n"),
  #endif
  #ifdef CONFIG_SMP
@@ -121,34 +126,47 @@ static struct irq_stat_info irq_stat_inf
  #endif
  };
 
+static DECLARE_BITMAP(irq_stat_count_show, IRQ_COUNT_MAX) __read_mostly;
+
  static int __init irq_init_stats(void)
  {
-	struct irq_stat_info *info = irq_stat_info;
+	const struct irq_stat_info *info = irq_stat_info;
 
  	for (unsigned int i = 0; i < ARRAY_SIZE(irq_stat_info); i++, info++) {
-		if (info->skip_vector && test_bit(info->skip_vector, system_vectors))
-			info->skip_vector = 0;
+		if (!info->skip_vector || (info->skip_vector != DEFAULT_SUPPRESSED_VECTOR &&
+					   test_bit(info->skip_vector, system_vectors)))
+			set_bit(i, irq_stat_count_show);
  	}
 
  #ifdef CONFIG_X86_LOCAL_APIC
  	if (!x86_platform_ipi_callback)
-		irq_stat_info[IRQ_COUNT_X86_PLATFORM_IPI].skip_vector = 1;
+		clear_bit(IRQ_COUNT_X86_PLATFORM_IPI, irq_stat_count_show);
  #endif
 
  #ifdef CONFIG_X86_POSTED_MSI
  	if (!posted_msi_enabled())
-		irq_stat_info[IRQ_COUNT_POSTED_MSI_NOTIFICATION].skip_vector = 1;
+		clear_bit(IRQ_COUNT_POSTED_MSI_NOTIFICATION, irq_stat_count_show);
  #endif
 
  #ifdef CONFIG_X86_MCE_AMD
  	if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD &&
  	    boot_cpu_data.x86_vendor != X86_VENDOR_HYGON)
-		irq_stat_info[IRQ_COUNT_DEFERRED_ERROR].skip_vector = 1;
+		clear_bit(IRQ_COUNT_DEFERRED_ERROR, irq_stat_count_show);
  #endif
  	return 0;
  }
  late_initcall(irq_init_stats);
 
+/*
+ * Used for default enabled counters to increment the stats and to enable the
+ * entry for /proc/interrupts output.
+ */
+void irq_stat_inc_and_enable(enum irq_stat_counts which)
+{
+	this_cpu_inc(irq_stat.counts[which]);
+	set_bit(which, irq_stat_count_show);
+}
+
  #ifdef CONFIG_PROC_FS
  /*
   * /proc/interrupts printing for arch specific interrupts

arch/x86/kernel/irq.c int arch_show_interrupts(struct seq_file · line 180

Quoted 6 lines
arch/x86/kernel/irq.c +1 -1
@@ -158,7 +176,7 @@ int arch_show_interrupts(struct seq_file
  	const struct irq_stat_info *info = irq_stat_info;
 
  	for (unsigned int i = 0; i < ARRAY_SIZE(irq_stat_info); i++, info++) {
-		if (info->skip_vector)
+		if (!test_bit(i, irq_stat_count_show))
  			continue;

If this is done, then those lines will be absent in /proc/interrupts right?

If there was a tool expecting these entries, it will fail get to see that entry. Is that ok?

Quoted 3 lines
arch/x86/kernel/irq.c +0 -0
  		seq_printf(p, "%*s:", prec, info->symbol);
 
author maintainer
#13 lore raw

Re: [patch V6 05/16] x86/irq: Suppress unlikely interrupt stats by default

Quoted 10 lines · Shrikanth Hegde

arch/x86/kernel/irq.c int arch_show_interrupts(struct seq_file · line 180

Quoted 4 lines · Thomas Gleixner
  	for (unsigned int i = 0; i < ARRAY_SIZE(irq_stat_info); i++, info++) {
arch/x86/kernel/irq.c +1 -1
-		if (info->skip_vector)
+		if (!test_bit(i, irq_stat_count_show))
  			continue;

If this is done, then those lines will be absent in /proc/interrupts right?

If there was a tool expecting these entries, it will fail get to see that entry. Is that ok?

I checked the obvious tools and they don't care. They have no expectations of what's there as that varies due to Kconfig settings, architectures etc.

Thanks,

tglx

#14 lore raw

Re: [patch V6 05/16] x86/irq: Suppress unlikely interrupt stats by default

In reply to Thomas Gleixner
Quoted 122 lines · Thomas Gleixner

From: Thomas Gleixner <[email protected]>

Unlikely interrupt counters like the spurious vector and the synthetic APIC ICR read retry show up in /proc/interrupts with all counts 0 most of the time.

As these are events which should never happen, suppress them by default and enable them for output when they actually happen.

This requires a seperate bitmap as the description array is marked __ro_after_init. With that bitmap in place it becomes RO data.

---
V5: Move irq_stat_inc_and_enable() here
V4: Fix the bad idea of writing to __ro_after_init marked data
V3: New patch

---
  arch/x86/include/asm/hardirq.h |    1 +
  arch/x86/kernel/apic/apic.c    |    2 +-
  arch/x86/kernel/apic/ipi.c     |    2 +-
  arch/x86/kernel/irq.c          |   38 ++++++++++++++++++++++++++++----------
  4 files changed, 31 insertions(+), 12 deletions(-)
arch/x86/include/asm/hardirq.h +1 -0
@@ -68,6 +68,7 @@ DECLARE_PER_CPU_ALIGNED(struct pi_desc,
  #define __ARCH_IRQ_STAT
 
  #define inc_irq_stat(index)	this_cpu_inc(irq_stat.counts[IRQ_COUNT_##index])
+void irq_stat_inc_and_enable(enum irq_stat_counts which);
 
  #ifdef CONFIG_X86_LOCAL_APIC
  #define inc_perf_irq_stat()	inc_irq_stat(APIC_PERF)
arch/x86/kernel/apic/apic.c +1 -1
@@ -2114,7 +2114,7 @@ static noinline void handle_spurious_int
 
  	trace_spurious_apic_entry(vector);
 
-	inc_irq_stat(SPURIOUS);
+	irq_stat_inc_and_enable(IRQ_COUNT_SPURIOUS);
 
  	/*
  	 * If this is a spurious interrupt then do not acknowledge
arch/x86/kernel/apic/ipi.c +1 -1
@@ -120,7 +120,7 @@ u32 apic_mem_wait_icr_idle_timeout(void)
  	for (cnt = 0; cnt < 1000; cnt++) {
  		if (!(apic_read(APIC_ICR) & APIC_ICR_BUSY))
  			return 0;
-		inc_irq_stat(ICR_READ_RETRY);
+		irq_stat_inc_and_enable(IRQ_COUNT_ICR_READ_RETRY);
  		udelay(100);
  	}
  	return APIC_ICR_BUSY;
arch/x86/kernel/irq.c +16 -9
@@ -68,19 +68,24 @@ struct irq_stat_info {
  	const char	*text;
  };
 
+#define DEFAULT_SUPPRESSED_VECTOR	UINT_MAX
+
  #define ISS(idx, sym, txt) [IRQ_COUNT_##idx] = { .symbol = sym, .text = txt }
 
  #define ITS(idx, sym, txt) [IRQ_COUNT_##idx] =				\
  	{ .skip_vector = idx## _VECTOR, .symbol = sym, .text = txt }
 
-static struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] __ro_after_init = {
+#define IDS(idx, sym, txt) [IRQ_COUNT_##idx] =				\
+	{ .skip_vector = DEFAULT_SUPPRESSED_VECTOR, .symbol = sym, .text = txt }
+
+static const struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] = {
  	ISS(NMI,			"NMI",	"  Non-maskable interrupts\n"),
  #ifdef CONFIG_X86_LOCAL_APIC
  	ISS(APIC_TIMER,			"LOC",	"  Local timer interrupts\n"),
-	ISS(SPURIOUS,			"SPU",	"  Spurious interrupts\n"),
+	IDS(SPURIOUS,			"SPU",	"  Spurious interrupts\n"),
  	ISS(APIC_PERF,			"PMI",	"  Performance monitoring interrupts\n"),
  	ISS(IRQ_WORK,			"IWI",	"  IRQ work interrupts\n"),
-	ISS(ICR_READ_RETRY,		"RTR",	"  APIC ICR read retries\n"),
+	IDS(ICR_READ_RETRY,		"RTR",	"  APIC ICR read retries\n"),
  	ISS(X86_PLATFORM_IPI,		"PLT",	"  Platform interrupts\n"),
  #endif
  #ifdef CONFIG_SMP
@@ -121,34 +126,47 @@ static struct irq_stat_info irq_stat_inf
  #endif
  };
 
+static DECLARE_BITMAP(irq_stat_count_show, IRQ_COUNT_MAX) __read_mostly;
+
  static int __init irq_init_stats(void)
  {
-	struct irq_stat_info *info = irq_stat_info;
+	const struct irq_stat_info *info = irq_stat_info;
 
  	for (unsigned int i = 0; i < ARRAY_SIZE(irq_stat_info); i++, info++) {
-		if (info->skip_vector && test_bit(info->skip_vector, system_vectors))
-			info->skip_vector = 0;
+		if (!info->skip_vector || (info->skip_vector != DEFAULT_SUPPRESSED_VECTOR &&
+					   test_bit(info->skip_vector, system_vectors)))
+			set_bit(i, irq_stat_count_show);
  	}
 
  #ifdef CONFIG_X86_LOCAL_APIC
  	if (!x86_platform_ipi_callback)
-		irq_stat_info[IRQ_COUNT_X86_PLATFORM_IPI].skip_vector = 1;
+		clear_bit(IRQ_COUNT_X86_PLATFORM_IPI, irq_stat_count_show);
  #endif
 
  #ifdef CONFIG_X86_POSTED_MSI
  	if (!posted_msi_enabled())
-		irq_stat_info[IRQ_COUNT_POSTED_MSI_NOTIFICATION].skip_vector = 1;
+		clear_bit(IRQ_COUNT_POSTED_MSI_NOTIFICATION, irq_stat_count_show);
  #endif
 
  #ifdef CONFIG_X86_MCE_AMD
  	if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD &&
  	    boot_cpu_data.x86_vendor != X86_VENDOR_HYGON)
-		irq_stat_info[IRQ_COUNT_DEFERRED_ERROR].skip_vector = 1;

arch/x86/kernel/irq.c static struct irq_stat_info irq_stat_inf · line 163

Quoted 10 lines
arch/x86/kernel/irq.c +5 -0
+		clear_bit(IRQ_COUNT_DEFERRED_ERROR, irq_stat_count_show);
  #endif
  	return 0;
  }
  late_initcall(irq_init_stats);
 
+/*
+ * Used for default enabled counters to increment the stats and to enable the
+ * entry for /proc/interrupts output.
+ */

nit:

Noticed this while copying the code.
I think you meant "Used for default disabled counters"

Quoted 19 lines
arch/x86/kernel/irq.c +7 -1
+void irq_stat_inc_and_enable(enum irq_stat_counts which)
+{
+	this_cpu_inc(irq_stat.counts[which]);
+	set_bit(which, irq_stat_count_show);
+}
+
  #ifdef CONFIG_PROC_FS
  /*
   * /proc/interrupts printing for arch specific interrupts
@@ -158,7 +176,7 @@ int arch_show_interrupts(struct seq_file
  	const struct irq_stat_info *info = irq_stat_info;
 
  	for (unsigned int i = 0; i < ARRAY_SIZE(irq_stat_info); i++, info++) {
-		if (info->skip_vector)
+		if (!test_bit(i, irq_stat_count_show))
  			continue;
 
  		seq_printf(p, "%*s:", prec, info->symbol);
author maintainer
#15 lore raw

Re: [patch V6 05/16] x86/irq: Suppress unlikely interrupt stats by default

Quoted 10 lines · Shrikanth Hegde
Quoted 4 lines · Thomas Gleixner

+/*
+ * Used for default enabled counters to increment the stats and to enable the
+ * entry for /proc/interrupts output.
+ */

nit:

Noticed this while copying the code.
I think you meant "Used for default disabled counters"

Indeed.

#16 lore raw

[tip: irq/core] x86/irq: Suppress unlikely interrupt stats by default

In reply to Thomas Gleixner
The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     8713f2e596a134ed5c41e96bb9714251a5e6d56a
Gitweb:        https://git.kernel.org/tip/8713f2e596a134ed5c41e96bb9714251a5e6d56a
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Sun, 17 May 2026 22:01:54 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Tue, 26 May 2026 16:21:12 +02:00

x86/irq: Suppress unlikely interrupt stats by default

Unlikely interrupt counters like the spurious vector and the synthetic APIC ICR read retry show up in /proc/interrupts with all counts 0 most of the time.

As these are events which should never happen, suppress them by default and enable them for output when they actually happen.

This requires a seperate bitmap as the description array is marked __ro_after_init. With that bitmap in place it becomes RO data.

---
 arch/x86/include/asm/hardirq.h |  1 +-
 arch/x86/kernel/apic/apic.c    |  2 +-
 arch/x86/kernel/apic/ipi.c     |  2 +-
 arch/x86/kernel/irq.c          | 38 ++++++++++++++++++++++++---------
 4 files changed, 31 insertions(+), 12 deletions(-)
arch/x86/include/asm/hardirq.h +1 -0
index 423fa4d..6723b51 100644
@@ -68,6 +68,7 @@ DECLARE_PER_CPU_ALIGNED(struct pi_desc, posted_msi_pi_desc);
 #define __ARCH_IRQ_STAT
 
 #define inc_irq_stat(index)	this_cpu_inc(irq_stat.counts[IRQ_COUNT_##index])
+void irq_stat_inc_and_enable(enum irq_stat_counts which);
 
 #ifdef CONFIG_X86_LOCAL_APIC
 #define inc_perf_irq_stat()	inc_irq_stat(APIC_PERF)
arch/x86/kernel/apic/apic.c +1 -1
index 6599e80..e27c263 100644
@@ -2114,7 +2114,7 @@ static noinline void handle_spurious_interrupt(u8 vector)
 
 	trace_spurious_apic_entry(vector);
 
-	inc_irq_stat(SPURIOUS);
+	irq_stat_inc_and_enable(IRQ_COUNT_SPURIOUS);
 
 	/*
 	 * If this is a spurious interrupt then do not acknowledge
arch/x86/kernel/apic/ipi.c +1 -1
index 3635c4d..c627bee 100644
@@ -120,7 +120,7 @@ u32 apic_mem_wait_icr_idle_timeout(void)
 	for (cnt = 0; cnt < 1000; cnt++) {
 		if (!(apic_read(APIC_ICR) & APIC_ICR_BUSY))
 			return 0;
-		inc_irq_stat(ICR_READ_RETRY);
+		irq_stat_inc_and_enable(IRQ_COUNT_ICR_READ_RETRY);
 		udelay(100);
 	}
 	return APIC_ICR_BUSY;
arch/x86/kernel/irq.c +28 -10
index decd08d..2d8ca41 100644
@@ -68,19 +68,24 @@ struct irq_stat_info {
 	const char	*text;
 };
 
+#define DEFAULT_SUPPRESSED_VECTOR	UINT_MAX
+
 #define ISS(idx, sym, txt) [IRQ_COUNT_##idx] = { .symbol = sym, .text = txt }
 
 #define ITS(idx, sym, txt) [IRQ_COUNT_##idx] =				\
 	{ .skip_vector = idx## _VECTOR, .symbol = sym, .text = txt }
 
-static struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] __ro_after_init = {
+#define IDS(idx, sym, txt) [IRQ_COUNT_##idx] =				\
+	{ .skip_vector = DEFAULT_SUPPRESSED_VECTOR, .symbol = sym, .text = txt }
+
+static const struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] = {
 	ISS(NMI,			"NMI",	"  Non-maskable interrupts\n"),
 #ifdef CONFIG_X86_LOCAL_APIC
 	ISS(APIC_TIMER,			"LOC",	"  Local timer interrupts\n"),
-	ISS(SPURIOUS,			"SPU",	"  Spurious interrupts\n"),
+	IDS(SPURIOUS,			"SPU",	"  Spurious interrupts\n"),
 	ISS(APIC_PERF,			"PMI",	"  Performance monitoring interrupts\n"),
 	ISS(IRQ_WORK,			"IWI",	"  IRQ work interrupts\n"),
-	ISS(ICR_READ_RETRY,		"RTR",	"  APIC ICR read retries\n"),
+	IDS(ICR_READ_RETRY,		"RTR",	"  APIC ICR read retries\n"),
 	ISS(X86_PLATFORM_IPI,		"PLT",	"  Platform interrupts\n"),
 #endif
 #ifdef CONFIG_SMP
@@ -121,34 +126,47 @@ static struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] __ro_after_init = {
 #endif
 };
 
+static DECLARE_BITMAP(irq_stat_count_show, IRQ_COUNT_MAX) __read_mostly;
+
 static int __init irq_init_stats(void)
 {
-	struct irq_stat_info *info = irq_stat_info;
+	const struct irq_stat_info *info = irq_stat_info;
 
 	for (unsigned int i = 0; i < ARRAY_SIZE(irq_stat_info); i++, info++) {
-		if (info->skip_vector && test_bit(info->skip_vector, system_vectors))
-			info->skip_vector = 0;
+		if (!info->skip_vector || (info->skip_vector != DEFAULT_SUPPRESSED_VECTOR &&
+					   test_bit(info->skip_vector, system_vectors)))
+			set_bit(i, irq_stat_count_show);
 	}
 
 #ifdef CONFIG_X86_LOCAL_APIC
 	if (!x86_platform_ipi_callback)
-		irq_stat_info[IRQ_COUNT_X86_PLATFORM_IPI].skip_vector = 1;
+		clear_bit(IRQ_COUNT_X86_PLATFORM_IPI, irq_stat_count_show);
 #endif
 
 #ifdef CONFIG_X86_POSTED_MSI
 	if (!posted_msi_enabled())
-		irq_stat_info[IRQ_COUNT_POSTED_MSI_NOTIFICATION].skip_vector = 1;
+		clear_bit(IRQ_COUNT_POSTED_MSI_NOTIFICATION, irq_stat_count_show);
 #endif
 
 #ifdef CONFIG_X86_MCE_AMD
 	if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD &&
 	    boot_cpu_data.x86_vendor != X86_VENDOR_HYGON)
-		irq_stat_info[IRQ_COUNT_DEFERRED_ERROR].skip_vector = 1;
+		clear_bit(IRQ_COUNT_DEFERRED_ERROR, irq_stat_count_show);
 #endif
 	return 0;
 }
 late_initcall(irq_init_stats);
 
+/*
+ * Used for default disabled counters to increment the stats and to enable the
+ * entry for /proc/interrupts output.
+ */
+void irq_stat_inc_and_enable(enum irq_stat_counts which)
+{
+	this_cpu_inc(irq_stat.counts[which]);
+	set_bit(which, irq_stat_count_show);
+}
+
 #ifdef CONFIG_PROC_FS
 /*
  * /proc/interrupts printing for arch specific interrupts
@@ -158,7 +176,7 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 	const struct irq_stat_info *info = irq_stat_info;
 
 	for (unsigned int i = 0; i < ARRAY_SIZE(irq_stat_info); i++, info++) {
-		if (info->skip_vector)
+		if (!test_bit(i, irq_stat_count_show))
 			continue;
 
 		seq_printf(p, "%*s:", prec, info->symbol);
author maintainer
#17 lore raw

PATCH v6 6/16 x86/irq: Move IOAPIC misrouted and PIC/APIC error counts into irq_stats

In reply to Thomas Gleixner

From: Thomas Gleixner <[email protected]>

The special treatment of these counts is just adding extra code for no real value. The irq_stats mechanism allows to suppress output of counters, which should never happen by default and provides a mechanism to enable them for the rare case that they occur.

Move the IOAPIC misrouted and the PIC/APIC error counts into irq_stats, mark them suppressed by default and update the sites which increment them.

This changes the output format of 'ERR' and 'MIS' in case there are events to the regular per CPU display format and otherwise suppresses them completely.

As a side effect this removes the arch_cpu_stat() mechanism from proc/stat which was only there to account for the error interrupts on x86 and missed to take the misrouted ones into account.

---
V3: New patch

---
 arch/x86/include/asm/hardirq.h |    7 ++++---
 arch/x86/include/asm/hw_irq.h  |    4 ----
 arch/x86/kernel/apic/apic.c    |    2 +-
 arch/x86/kernel/apic/io_apic.c |    4 +---
 arch/x86/kernel/i8259.c        |    2 +-
 arch/x86/kernel/irq.c          |   16 ++++------------
 fs/proc/stat.c                 |    4 ----
 7 files changed, 11 insertions(+), 28 deletions(-)
arch/x86/include/asm/hardirq.h +4 -3
@@ -50,6 +50,10 @@ enum irq_stat_counts {
 #ifdef CONFIG_X86_POSTED_MSI
 	IRQ_COUNT_POSTED_MSI_NOTIFICATION,
 #endif
+	IRQ_COUNT_PIC_APIC_ERROR,
+#ifdef CONFIG_X86_IO_APIC
+	IRQ_COUNT_IOAPIC_MISROUTED,
+#endif
 	IRQ_COUNT_MAX,
 };
 
@@ -81,9 +85,6 @@ extern void ack_bad_irq(unsigned int irq
 #ifdef CONFIG_PROC_FS
 extern u64 arch_irq_stat_cpu(unsigned int cpu);
 #define arch_irq_stat_cpu	arch_irq_stat_cpu
-
-extern u64 arch_irq_stat(void);
-#define arch_irq_stat		arch_irq_stat
 #endif
 
 DECLARE_PER_CPU_CACHE_HOT(u16, __softirq_pending);
arch/x86/include/asm/hw_irq.h +0 -4
@@ -110,10 +110,6 @@ static inline void lock_vector_lock(void
 static inline void unlock_vector_lock(void) {}
 #endif
 
-/* Statistics */
-extern atomic_t irq_err_count;
-extern atomic_t irq_mis_count;
-
 extern void elcr_set_level_irq(unsigned int irq);
 
 extern char irq_entries_start[];
arch/x86/kernel/apic/apic.c +1 -1
@@ -2186,7 +2186,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_error_inte
 		apic_write(APIC_ESR, 0);
 	v = apic_read(APIC_ESR);
 	apic_eoi();
-	atomic_inc(&irq_err_count);
+	irq_stat_inc_and_enable(IRQ_COUNT_PIC_APIC_ERROR);
 
 	apic_pr_debug("APIC error on CPU%d: %02x", smp_processor_id(), v);
 
arch/x86/kernel/apic/io_apic.c +1 -3
@@ -1575,8 +1575,6 @@ static unsigned int startup_ioapic_irq(s
 	return was_pending;
 }
 
-atomic_t irq_mis_count;
-
 #ifdef CONFIG_GENERIC_PENDING_IRQ
 static bool io_apic_level_ack_pending(struct mp_chip_data *data)
 {
@@ -1713,7 +1711,7 @@ static void ioapic_ack_level(struct irq_
 	 * at the cpu.
 	 */
 	if (!(v & (1 << (i & 0x1f)))) {
-		atomic_inc(&irq_mis_count);
+		irq_stat_inc_and_enable(IRQ_COUNT_IOAPIC_MISROUTED);
 		eoi_ioapic_pin(cfg->vector, irq_data->chip_data);
 	}
 
arch/x86/kernel/i8259.c +1 -1
@@ -214,7 +214,7 @@ static void mask_and_ack_8259A(struct ir
 			       "spurious 8259A interrupt: IRQ%d.\n", irq);
 			spurious_irq_mask |= irqmask;
 		}
-		atomic_inc(&irq_err_count);
+		irq_stat_inc_and_enable(IRQ_COUNT_PIC_APIC_ERROR);
 		/*
 		 * Theoretically we do not have to handle this IRQ,
 		 * but in Linux this does not cause problems and is
arch/x86/kernel/irq.c +4 -12
@@ -39,8 +39,6 @@ EXPORT_PER_CPU_SYMBOL(__softirq_pending)
 
 DEFINE_PER_CPU_CACHE_HOT(struct irq_stack *, hardirq_stack_ptr);
 
-atomic_t irq_err_count;
-
 /*
  * 'what should we do if we get a hw irq event on an illegal vector'.
  * each architecture has to answer this themselves.
@@ -124,6 +122,10 @@ static const struct irq_stat_info irq_st
 #ifdef CONFIG_X86_POSTED_MSI
 	ISS(POSTED_MSI_NOTIFICATION,	"PMN",	"  Posted MSI notification event\n"),
 #endif
+	IDS(PIC_APIC_ERROR,		"ERR",	"  PIC/APIC error interrupts\n"),
+#ifdef CONFIG_X86_IO_APIC
+	IDS(IOAPIC_MISROUTED,		"MIS",	"  Misrouted IO/APIC interrupts\n"),
+#endif
 };
 
 static DECLARE_BITMAP(irq_stat_count_show, IRQ_COUNT_MAX) __read_mostly;
@@ -183,10 +185,6 @@ int arch_show_interrupts(struct seq_file
 		irq_proc_emit_counts(p, &irq_stat.counts[i]);
 		seq_puts(p, info->text);
 	}
-
-	seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count));
-	if (IS_ENABLED(CONFIG_X86_IO_APIC))
-		seq_printf(p, "%*s: %10u\n", prec, "MIS", atomic_read(&irq_mis_count));
 	return 0;
 }
 
@@ -202,12 +200,6 @@ u64 arch_irq_stat_cpu(unsigned int cpu)
 		sum += p->counts[i];
 	return sum;
 }
-
-u64 arch_irq_stat(void)
-{
-	u64 sum = atomic_read(&irq_err_count);
-	return sum;
-}
 #endif /* CONFIG_PROC_FS */
 
 static __always_inline void handle_irq(struct irq_desc *desc,
fs/proc/stat.c +0 -4
@@ -18,9 +18,6 @@
 #ifndef arch_irq_stat_cpu
 #define arch_irq_stat_cpu(cpu) 0
 #endif
-#ifndef arch_irq_stat
-#define arch_irq_stat() 0
-#endif
 
 u64 get_idle_time(struct kernel_cpustat *kcs, int cpu)
 {
@@ -122,7 +119,6 @@ static int show_stat(struct seq_file *p,
 			sum_softirq += softirq_stat;
 		}
 	}
-	sum += arch_irq_stat();
 
 	seq_put_decimal_ull(p, "cpu  ", nsec_to_clock_t(user));
 	seq_put_decimal_ull(p, " ", nsec_to_clock_t(nice));
#18 lore raw

[tip: irq/core] x86/irq: Move IOAPIC misrouted and PIC/APIC error counts into irq_stats

The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     d6b70b16b4e7035d230ef97ac6927f40e6aefcce
Gitweb:        https://git.kernel.org/tip/d6b70b16b4e7035d230ef97ac6927f40e6aefcce
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Sun, 17 May 2026 22:01:59 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Tue, 26 May 2026 16:21:12 +02:00

x86/irq: Move IOAPIC misrouted and PIC/APIC error counts into irq_stats

The special treatment of these counts is just adding extra code for no real value. The irq_stats mechanism allows to suppress output of counters, which should never happen by default and provides a mechanism to enable them for the rare case that they occur.

Move the IOAPIC misrouted and the PIC/APIC error counts into irq_stats, mark them suppressed by default and update the sites which increment them.

This changes the output format of 'ERR' and 'MIS' in case there are events to the regular per CPU display format and otherwise suppresses them completely.

As a side effect this removes the arch_cpu_stat() mechanism from proc/stat which was only there to account for the error interrupts on x86 and missed to take the misrouted ones into account.

---
 arch/x86/include/asm/hardirq.h |  7 ++++---
 arch/x86/include/asm/hw_irq.h  |  4 ----
 arch/x86/kernel/apic/apic.c    |  2 +-
 arch/x86/kernel/apic/io_apic.c |  4 +---
 arch/x86/kernel/i8259.c        |  2 +-
 arch/x86/kernel/irq.c          | 16 ++++------------
 fs/proc/stat.c                 |  4 ----
 7 files changed, 11 insertions(+), 28 deletions(-)
arch/x86/include/asm/hardirq.h +4 -3
index 6723b51..dea60d6 100644
@@ -50,6 +50,10 @@ enum irq_stat_counts {
 #ifdef CONFIG_X86_POSTED_MSI
 	IRQ_COUNT_POSTED_MSI_NOTIFICATION,
 #endif
+	IRQ_COUNT_PIC_APIC_ERROR,
+#ifdef CONFIG_X86_IO_APIC
+	IRQ_COUNT_IOAPIC_MISROUTED,
+#endif
 	IRQ_COUNT_MAX,
 };
 
@@ -81,9 +85,6 @@ extern void ack_bad_irq(unsigned int irq);
 #ifdef CONFIG_PROC_FS
 extern u64 arch_irq_stat_cpu(unsigned int cpu);
 #define arch_irq_stat_cpu	arch_irq_stat_cpu
-
-extern u64 arch_irq_stat(void);
-#define arch_irq_stat		arch_irq_stat
 #endif
 
 DECLARE_PER_CPU_CACHE_HOT(u16, __softirq_pending);
arch/x86/include/asm/hw_irq.h +0 -4
index cbe19e6..47727d0 100644
@@ -110,10 +110,6 @@ static inline void lock_vector_lock(void) {}
 static inline void unlock_vector_lock(void) {}
 #endif
 
-/* Statistics */
-extern atomic_t irq_err_count;
-extern atomic_t irq_mis_count;
-
 extern void elcr_set_level_irq(unsigned int irq);
 
 extern char irq_entries_start[];
arch/x86/kernel/apic/apic.c +1 -1
index e27c263..3eeebc2 100644
@@ -2186,7 +2186,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_error_interrupt)
 		apic_write(APIC_ESR, 0);
 	v = apic_read(APIC_ESR);
 	apic_eoi();
-	atomic_inc(&irq_err_count);
+	irq_stat_inc_and_enable(IRQ_COUNT_PIC_APIC_ERROR);
 
 	apic_pr_debug("APIC error on CPU%d: %02x", smp_processor_id(), v);
 
arch/x86/kernel/apic/io_apic.c +1 -3
index 352ed55..7d7175d 100644
@@ -1575,8 +1575,6 @@ static unsigned int startup_ioapic_irq(struct irq_data *data)
 	return was_pending;
 }
 
-atomic_t irq_mis_count;
-
 #ifdef CONFIG_GENERIC_PENDING_IRQ
 static bool io_apic_level_ack_pending(struct mp_chip_data *data)
 {
@@ -1713,7 +1711,7 @@ static void ioapic_ack_level(struct irq_data *irq_data)
 	 * at the cpu.
 	 */
 	if (!(v & (1 << (i & 0x1f)))) {
-		atomic_inc(&irq_mis_count);
+		irq_stat_inc_and_enable(IRQ_COUNT_IOAPIC_MISROUTED);
 		eoi_ioapic_pin(cfg->vector, irq_data->chip_data);
 	}
 
arch/x86/kernel/i8259.c +1 -1
index f67063d..f7a86b9 100644
@@ -214,7 +214,7 @@ spurious_8259A_irq:
 			       "spurious 8259A interrupt: IRQ%d.\n", irq);
 			spurious_irq_mask |= irqmask;
 		}
-		atomic_inc(&irq_err_count);
+		irq_stat_inc_and_enable(IRQ_COUNT_PIC_APIC_ERROR);
 		/*
 		 * Theoretically we do not have to handle this IRQ,
 		 * but in Linux this does not cause problems and is
arch/x86/kernel/irq.c +4 -12
index 2d8ca41..9d98c81 100644
@@ -39,8 +39,6 @@ EXPORT_PER_CPU_SYMBOL(__softirq_pending);
 
 DEFINE_PER_CPU_CACHE_HOT(struct irq_stack *, hardirq_stack_ptr);
 
-atomic_t irq_err_count;
-
 /*
  * 'what should we do if we get a hw irq event on an illegal vector'.
  * each architecture has to answer this themselves.
@@ -124,6 +122,10 @@ static const struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] = {
 #ifdef CONFIG_X86_POSTED_MSI
 	ISS(POSTED_MSI_NOTIFICATION,	"PMN",	"  Posted MSI notification event\n"),
 #endif
+	IDS(PIC_APIC_ERROR,		"ERR",	"  PIC/APIC error interrupts\n"),
+#ifdef CONFIG_X86_IO_APIC
+	IDS(IOAPIC_MISROUTED,		"MIS",	"  Misrouted IO/APIC interrupts\n"),
+#endif
 };
 
 static DECLARE_BITMAP(irq_stat_count_show, IRQ_COUNT_MAX) __read_mostly;
@@ -183,10 +185,6 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 		irq_proc_emit_counts(p, &irq_stat.counts[i]);
 		seq_puts(p, info->text);
 	}
-
-	seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count));
-	if (IS_ENABLED(CONFIG_X86_IO_APIC))
-		seq_printf(p, "%*s: %10u\n", prec, "MIS", atomic_read(&irq_mis_count));
 	return 0;
 }
 
@@ -202,12 +200,6 @@ u64 arch_irq_stat_cpu(unsigned int cpu)
 		sum += p->counts[i];
 	return sum;
 }
-
-u64 arch_irq_stat(void)
-{
-	u64 sum = atomic_read(&irq_err_count);
-	return sum;
-}
 #endif /* CONFIG_PROC_FS */
 
 static __always_inline void handle_irq(struct irq_desc *desc,
fs/proc/stat.c +0 -4
index 8b444e8..20c3df9 100644
@@ -18,9 +18,6 @@
 #ifndef arch_irq_stat_cpu
 #define arch_irq_stat_cpu(cpu) 0
 #endif
-#ifndef arch_irq_stat
-#define arch_irq_stat() 0
-#endif
 
 u64 get_idle_time(struct kernel_cpustat *kcs, int cpu)
 {
@@ -122,7 +119,6 @@ static int show_stat(struct seq_file *p, void *v)
 			sum_softirq += softirq_stat;
 		}
 	}
-	sum += arch_irq_stat();
 
 	seq_put_decimal_ull(p, "cpu  ", nsec_to_clock_t(user));
 	seq_put_decimal_ull(p, " ", nsec_to_clock_t(nice));
author maintainer
#19 lore raw

PATCH v6 7/16 scripts/gdb: Update x86 interrupts to the array based storage

In reply to Thomas Gleixner

From: Thomas Gleixner <[email protected]>

x86 changed the interrupt statistics from a struct with individual members to an counter array. It also provides a corresponding info array with the strings for prefix and description and an indicator to skip the entry.

Update the already out of sync GDB script to use the counter and the info array, which keeps the GDB script in sync automatically.

---
V6: Fixup alignment for interrupts - Radu
V5: Bring the show_err_irq() back as it is used on mips
Fix the index calculation for the show bitmap so it works with more than one unsigned long.
V4: Adopted to irq_stat_count_show bitmap and removed the duplicate ERR/MIS output
V3: New patch - Radu

---
 scripts/gdb/linux/interrupts.py |   71 ++++++++++------------------------------
 1 file changed, 18 insertions(+), 53 deletions(-)
scripts/gdb/linux/interrupts.py +18 -53
@@ -48,7 +48,7 @@ irq_desc_type = utils.CachedType("struct
             count = cpus.per_cpu(desc['kstat_irqs'], cpu)['cnt']
         else:
             count = 0
-        text += "%10u" % (count)
+        text += "%10u " % (count)
 
     name = "None"
     if desc['irq_data']['chip']:
@@ -58,7 +58,7 @@ irq_desc_type = utils.CachedType("struct
         else:
             name = "-"
 
-    text += "  %8s" % (name)
+    text += "  %-8s" % (name)
 
     if desc['irq_data']['domain']:
         text += "  %*lu" % (prec, desc['irq_data']['hwirq'])
@@ -97,64 +97,29 @@ irq_desc_type = utils.CachedType("struct
         text += "%*s: %10u\n" % (prec, "ERR", cnt['counter'])
     return text
 
-def x86_show_irqstat(prec, pfx, field, desc):
-    irq_stat = gdb.parse_and_eval("&irq_stat")
+def x86_show_irqstat(prec, pfx, idx, desc):
+    irq_stat = gdb.parse_and_eval("&irq_stat.counts[%d]" %idx)
     text = "%*s: " % (prec, pfx)
     for cpu in cpus.each_online_cpu():
         stat = cpus.per_cpu(irq_stat, cpu)
-        text += "%10u " % (stat[field])
-    text += "  %s\n" % (desc)
-    return text
-
-def x86_show_mce(prec, var, pfx, desc):
-    pvar = gdb.parse_and_eval(var)
-    text = "%*s: " % (prec, pfx)
-    for cpu in cpus.each_online_cpu():
-        text += "%10u " % (cpus.per_cpu(pvar, cpu).dereference())
-    text += "  %s\n" % (desc)
+        text += "%10u " % (stat.dereference())
+    text += desc
     return text
 
 def x86_show_interupts(prec):
-    text = x86_show_irqstat(prec, "NMI", '__nmi_count', 'Non-maskable interrupts')
+    info_type = gdb.lookup_type('struct irq_stat_info')
+    info = gdb.parse_and_eval('irq_stat_info')
+    bitmap = gdb.parse_and_eval('irq_stat_count_show')
+    bitsperlong = 8 * int(bitmap.type.target().sizeof)
 
-    if constants.LX_CONFIG_X86_LOCAL_APIC:
-        text += x86_show_irqstat(prec, "LOC", 'apic_timer_irqs', "Local timer interrupts")
-        text += x86_show_irqstat(prec, "SPU", 'irq_spurious_count', "Spurious interrupts")
-        text += x86_show_irqstat(prec, "PMI", 'apic_perf_irqs', "Performance monitoring interrupts")
-        text += x86_show_irqstat(prec, "IWI", 'apic_irq_work_irqs', "IRQ work interrupts")
-        text += x86_show_irqstat(prec, "RTR", 'icr_read_retry_count', "APIC ICR read retries")
-        if utils.gdb_eval_or_none("x86_platform_ipi_callback") is not None:
-            text += x86_show_irqstat(prec, "PLT", 'x86_platform_ipis', "Platform interrupts")
-
-    if constants.LX_CONFIG_SMP:
-        text += x86_show_irqstat(prec, "RES", 'irq_resched_count', "Rescheduling interrupts")
-        text += x86_show_irqstat(prec, "CAL", 'irq_call_count', "Function call interrupts")
-        text += x86_show_irqstat(prec, "TLB", 'irq_tlb_count', "TLB shootdowns")
-
-    if constants.LX_CONFIG_X86_THERMAL_VECTOR:
-        text += x86_show_irqstat(prec, "TRM", 'irq_thermal_count', "Thermal events interrupts")
-
-    if constants.LX_CONFIG_X86_MCE_THRESHOLD:
-        text += x86_show_irqstat(prec, "THR", 'irq_threshold_count', "Threshold APIC interrupts")
-
-    if constants.LX_CONFIG_X86_MCE_AMD:
-        text += x86_show_irqstat(prec, "DFR", 'irq_deferred_error_count', "Deferred Error APIC interrupts")
-
-    if constants.LX_CONFIG_X86_MCE:
-        text += x86_show_mce(prec, "&mce_exception_count", "MCE", "Machine check exceptions")
-        text += x86_show_mce(prec, "&mce_poll_count", "MCP", "Machine check polls")
-
-    text += show_irq_err_count(prec)
-
-    if constants.LX_CONFIG_X86_IO_APIC:
-        cnt = utils.gdb_eval_or_none("irq_mis_count")
-        if cnt is not None:
-            text += "%*s: %10u\n" % (prec, "MIS", cnt['counter'])
-
-    if constants.LX_CONFIG_KVM:
-        text += x86_show_irqstat(prec, "PIN", 'kvm_posted_intr_ipis', 'Posted-interrupt notification event')
-        text += x86_show_irqstat(prec, "NPI", 'kvm_posted_intr_nested_ipis', 'Nested posted-interrupt event')
-        text += x86_show_irqstat(prec, "PIW", 'kvm_posted_intr_wakeup_ipis', 'Posted-interrupt wakeup event')
+    text = ""
+    for idx in range(int(info.type.sizeof / info_type.sizeof)):
+        show = bitmap[int(idx / bitsperlong)]
+        if not show & 1 << int(idx % bitsperlong):
+            continue
+        pfx = info[idx]['symbol'].string()
+        desc = info[idx]['text'].string()
+        text += x86_show_irqstat(prec, pfx, idx, desc)
 
     return text
 
#20 lore raw

[tip: irq/core] scripts/gdb: Update x86 interrupts to the array based storage

The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     cca5e6fa791bdf84f716ae92604d8330a6b6411a
Gitweb:        https://git.kernel.org/tip/cca5e6fa791bdf84f716ae92604d8330a6b6411a
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Sun, 17 May 2026 22:02:04 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Tue, 26 May 2026 16:21:13 +02:00

scripts/gdb: Update x86 interrupts to the array based storage

x86 changed the interrupt statistics from a struct with individual members to an counter array. It also provides a corresponding info array with the strings for prefix and description and an indicator to skip the entry.

Update the already out of sync GDB script to use the counter and the info array, which keeps the GDB script in sync automatically.

---
 scripts/gdb/linux/interrupts.py | 73 ++++++++------------------------
 1 file changed, 19 insertions(+), 54 deletions(-)
scripts/gdb/linux/interrupts.py +19 -54
index f4f715a..b794e01 100644
@@ -48,7 +48,7 @@ def show_irq_desc(prec, irq):
             count = cpus.per_cpu(desc['kstat_irqs'], cpu)['cnt']
         else:
             count = 0
-        text += "%10u" % (count)
+        text += "%10u " % (count)
 
     name = "None"
     if desc['irq_data']['chip']:
@@ -58,7 +58,7 @@ def show_irq_desc(prec, irq):
         else:
             name = "-"
 
-    text += "  %8s" % (name)
+    text += "  %-8s" % (name)
 
     if desc['irq_data']['domain']:
         text += "  %*lu" % (prec, desc['irq_data']['hwirq'])
@@ -97,64 +97,29 @@ def show_irq_err_count(prec):
         text += "%*s: %10u\n" % (prec, "ERR", cnt['counter'])
     return text
 
-def x86_show_irqstat(prec, pfx, field, desc):
-    irq_stat = gdb.parse_and_eval("&irq_stat")
+def x86_show_irqstat(prec, pfx, idx, desc):
+    irq_stat = gdb.parse_and_eval("&irq_stat.counts[%d]" %idx)
     text = "%*s: " % (prec, pfx)
     for cpu in cpus.each_online_cpu():
         stat = cpus.per_cpu(irq_stat, cpu)
-        text += "%10u " % (stat[field])
-    text += "  %s\n" % (desc)
-    return text
-
-def x86_show_mce(prec, var, pfx, desc):
-    pvar = gdb.parse_and_eval(var)
-    text = "%*s: " % (prec, pfx)
-    for cpu in cpus.each_online_cpu():
-        text += "%10u " % (cpus.per_cpu(pvar, cpu).dereference())
-    text += "  %s\n" % (desc)
+        text += "%10u " % (stat.dereference())
+    text += desc
     return text
 
 def x86_show_interupts(prec):
-    text = x86_show_irqstat(prec, "NMI", '__nmi_count', 'Non-maskable interrupts')
-
-    if constants.LX_CONFIG_X86_LOCAL_APIC:
-        text += x86_show_irqstat(prec, "LOC", 'apic_timer_irqs', "Local timer interrupts")
-        text += x86_show_irqstat(prec, "SPU", 'irq_spurious_count', "Spurious interrupts")
-        text += x86_show_irqstat(prec, "PMI", 'apic_perf_irqs', "Performance monitoring interrupts")
-        text += x86_show_irqstat(prec, "IWI", 'apic_irq_work_irqs', "IRQ work interrupts")
-        text += x86_show_irqstat(prec, "RTR", 'icr_read_retry_count', "APIC ICR read retries")
-        if utils.gdb_eval_or_none("x86_platform_ipi_callback") is not None:
-            text += x86_show_irqstat(prec, "PLT", 'x86_platform_ipis', "Platform interrupts")
-
-    if constants.LX_CONFIG_SMP:
-        text += x86_show_irqstat(prec, "RES", 'irq_resched_count', "Rescheduling interrupts")
-        text += x86_show_irqstat(prec, "CAL", 'irq_call_count', "Function call interrupts")
-        text += x86_show_irqstat(prec, "TLB", 'irq_tlb_count', "TLB shootdowns")
-
-    if constants.LX_CONFIG_X86_THERMAL_VECTOR:
-        text += x86_show_irqstat(prec, "TRM", 'irq_thermal_count', "Thermal events interrupts")
-
-    if constants.LX_CONFIG_X86_MCE_THRESHOLD:
-        text += x86_show_irqstat(prec, "THR", 'irq_threshold_count', "Threshold APIC interrupts")
-
-    if constants.LX_CONFIG_X86_MCE_AMD:
-        text += x86_show_irqstat(prec, "DFR", 'irq_deferred_error_count', "Deferred Error APIC interrupts")
-
-    if constants.LX_CONFIG_X86_MCE:
-        text += x86_show_mce(prec, "&mce_exception_count", "MCE", "Machine check exceptions")
-        text += x86_show_mce(prec, "&mce_poll_count", "MCP", "Machine check polls")
-
-    text += show_irq_err_count(prec)
-
-    if constants.LX_CONFIG_X86_IO_APIC:
-        cnt = utils.gdb_eval_or_none("irq_mis_count")
-        if cnt is not None:
-            text += "%*s: %10u\n" % (prec, "MIS", cnt['counter'])
-
-    if constants.LX_CONFIG_KVM:
-        text += x86_show_irqstat(prec, "PIN", 'kvm_posted_intr_ipis', 'Posted-interrupt notification event')
-        text += x86_show_irqstat(prec, "NPI", 'kvm_posted_intr_nested_ipis', 'Nested posted-interrupt event')
-        text += x86_show_irqstat(prec, "PIW", 'kvm_posted_intr_wakeup_ipis', 'Posted-interrupt wakeup event')
+    info_type = gdb.lookup_type('struct irq_stat_info')
+    info = gdb.parse_and_eval('irq_stat_info')
+    bitmap = gdb.parse_and_eval('irq_stat_count_show')
+    bitsperlong = 8 * int(bitmap.type.target().sizeof)
+
+    text = ""
+    for idx in range(int(info.type.sizeof / info_type.sizeof)):
+        show = bitmap[int(idx / bitsperlong)]
+        if not show & 1 << int(idx % bitsperlong):
+            continue
+        pfx = info[idx]['symbol'].string()
+        desc = info[idx]['text'].string()
+        text += x86_show_irqstat(prec, pfx, idx, desc)
 
     return text
 
author maintainer
#21 lore raw

PATCH v6 8/16 genirq: Expose nr_irqs in core code

In reply to Thomas Gleixner

From: Thomas Gleixner <[email protected]>

... to avoid function calls in the core code to retrieve the maximum number of interrupts.

Rename it to 'total_nr_irqs' as 'nr_irqs' is too generic and fix up the 'nr_irqs' reference in the related GDB script as well.

---
V3: Fix up the missing usage sites and gdb script - Radu

---
 kernel/irq/internals.h          |    1 +
 kernel/irq/irqdesc.c            |   28 ++++++++++++++--------------
 kernel/irq/proc.c               |    4 ++--
 scripts/gdb/linux/interrupts.py |    2 +-
 4 files changed, 18 insertions(+), 17 deletions(-)
kernel/irq/internals.h +1 -0
@@ -21,6 +21,7 @@
 
 extern bool noirqdebug;
 extern int irq_poll_cpu;
+extern unsigned int total_nr_irqs;
 
 extern struct irqaction chained_action;
 
kernel/irq/irqdesc.c +14 -14
@@ -140,14 +140,14 @@ static void desc_set_defaults(unsigned i
 	desc_smp_init(desc, node, affinity);
 }
 
-static unsigned int nr_irqs = NR_IRQS;
+unsigned int total_nr_irqs __read_mostly = NR_IRQS;
 
 /**
  * irq_get_nr_irqs() - Number of interrupts supported by the system.
  */
 unsigned int irq_get_nr_irqs(void)
 {
-	return nr_irqs;
+	return total_nr_irqs;
 }
 EXPORT_SYMBOL_GPL(irq_get_nr_irqs);
 
@@ -159,7 +159,7 @@ EXPORT_SYMBOL_GPL(irq_get_nr_irqs);
  */
 unsigned int irq_set_nr_irqs(unsigned int nr)
 {
-	nr_irqs = nr;
+	total_nr_irqs = nr;
 
 	return nr;
 }
@@ -187,9 +187,9 @@ static unsigned int irq_find_at_or_after
 	struct irq_desc *desc;
 
 	guard(rcu)();
-	desc = mt_find(&sparse_irqs, &index, nr_irqs);
+	desc = mt_find(&sparse_irqs, &index, total_nr_irqs);
 
-	return desc ? irq_desc_get_irq(desc) : nr_irqs;
+	return desc ? irq_desc_get_irq(desc) : total_nr_irqs;
 }
 
 static void irq_insert_desc(unsigned int irq, struct irq_desc *desc)
@@ -543,7 +543,7 @@ static bool irq_expand_nr_irqs(unsigned
 {
 	if (nr > MAX_SPARSE_IRQS)
 		return false;
-	nr_irqs = nr;
+	total_nr_irqs = nr;
 	return true;
 }
 
@@ -557,16 +557,16 @@ int __init early_irq_init(void)
 	/* Let arch update nr_irqs and return the nr of preallocated irqs */
 	initcnt = arch_probe_nr_irqs();
 	printk(KERN_INFO "NR_IRQS: %d, nr_irqs: %d, preallocated irqs: %d\n",
-	       NR_IRQS, nr_irqs, initcnt);
+	       NR_IRQS, total_nr_irqs, initcnt);
 
-	if (WARN_ON(nr_irqs > MAX_SPARSE_IRQS))
-		nr_irqs = MAX_SPARSE_IRQS;
+	if (WARN_ON(total_nr_irqs > MAX_SPARSE_IRQS))
+		total_nr_irqs = MAX_SPARSE_IRQS;
 
 	if (WARN_ON(initcnt > MAX_SPARSE_IRQS))
 		initcnt = MAX_SPARSE_IRQS;
 
-	if (initcnt > nr_irqs)
-		nr_irqs = initcnt;
+	if (initcnt > total_nr_irqs)
+		total_nr_irqs = initcnt;
 
 	for (i = 0; i < initcnt; i++) {
 		desc = alloc_desc(i, node, 0, NULL, NULL);
@@ -862,7 +862,7 @@ void irq_free_descs(unsigned int from, u
 {
 	int i;
 
-	if (from >= nr_irqs || (from + cnt) > nr_irqs)
+	if (from >= total_nr_irqs || (from + cnt) > total_nr_irqs)
 		return;
 
 	guard(mutex)(&sparse_irq_lock);
@@ -911,7 +911,7 @@ int __ref __irq_alloc_descs(int irq, uns
 	if (irq >=0 && start != irq)
 		return -EEXIST;
 
-	if (start + cnt > nr_irqs) {
+	if (start + cnt > total_nr_irqs) {
 		if (!irq_expand_nr_irqs(start + cnt))
 			return -ENOMEM;
 	}
@@ -923,7 +923,7 @@ EXPORT_SYMBOL_GPL(__irq_alloc_descs);
  * irq_get_next_irq - get next allocated irq number
  * @offset:	where to start the search
  *
- * Returns next irq number after offset or nr_irqs if none is found.
+ * Returns next irq number after offset or total_nr_irqs if none is found.
  */
 unsigned int irq_get_next_irq(unsigned int offset)
 {
kernel/irq/proc.c +2 -2
@@ -447,7 +447,7 @@ int __weak arch_show_interrupts(struct s
 }
 
 #ifndef ACTUAL_NR_IRQS
-# define ACTUAL_NR_IRQS irq_get_nr_irqs()
+# define ACTUAL_NR_IRQS total_nr_irqs
 #endif
 
 /* Same as seq_put_decimal_ull_width(p, " ", cnt, 10) */
@@ -489,7 +489,7 @@ void irq_proc_emit_counts(struct seq_fil
 
 int show_interrupts(struct seq_file *p, void *v)
 {
-	const unsigned int nr_irqs = irq_get_nr_irqs();
+	const unsigned int nr_irqs = total_nr_irqs;
 	static int prec;
 
 	int i = *(loff_t *) v, j;
scripts/gdb/linux/interrupts.py +1 -1
@@ -174,7 +174,7 @@ irq_desc_type = utils.CachedType("struct
         super(LxInterruptList, self).__init__("lx-interruptlist", gdb.COMMAND_DATA)
 
     def invoke(self, arg, from_tty):
-        nr_irqs = gdb.parse_and_eval("nr_irqs")
+        nr_irqs = gdb.parse_and_eval("total_nr_irqs")
         prec = 3
         j = 1000
         while prec < 10 and j <= nr_irqs:
reviewer Reviewed-by on v6 8/16
#22 lore raw

Re: [patch V6 08/16] genirq: Expose nr_irqs in core code

Quoted 152 lines · Thomas Gleixner

From: Thomas Gleixner <[email protected]>

... to avoid function calls in the core code to retrieve the maximum number of interrupts.

Rename it to 'total_nr_irqs' as 'nr_irqs' is too generic and fix up the 'nr_irqs' reference in the related GDB script as well.

---
V3: Fix up the missing usage sites and gdb script - Radu

---
  kernel/irq/internals.h          |    1 +
  kernel/irq/irqdesc.c            |   28 ++++++++++++++--------------
  kernel/irq/proc.c               |    4 ++--
  scripts/gdb/linux/interrupts.py |    2 +-
  4 files changed, 18 insertions(+), 17 deletions(-)
kernel/irq/internals.h +1 -0
@@ -21,6 +21,7 @@
 
  extern bool noirqdebug;
  extern int irq_poll_cpu;
+extern unsigned int total_nr_irqs;
 
  extern struct irqaction chained_action;
 
kernel/irq/irqdesc.c +14 -14
@@ -140,14 +140,14 @@ static void desc_set_defaults(unsigned i
  	desc_smp_init(desc, node, affinity);
  }
 
-static unsigned int nr_irqs = NR_IRQS;
+unsigned int total_nr_irqs __read_mostly = NR_IRQS;
 
  /**
   * irq_get_nr_irqs() - Number of interrupts supported by the system.
   */
  unsigned int irq_get_nr_irqs(void)
  {
-	return nr_irqs;
+	return total_nr_irqs;
  }
  EXPORT_SYMBOL_GPL(irq_get_nr_irqs);
 
@@ -159,7 +159,7 @@ EXPORT_SYMBOL_GPL(irq_get_nr_irqs);
   */
  unsigned int irq_set_nr_irqs(unsigned int nr)
  {
-	nr_irqs = nr;
+	total_nr_irqs = nr;
 
  	return nr;
  }
@@ -187,9 +187,9 @@ static unsigned int irq_find_at_or_after
  	struct irq_desc *desc;
 
  	guard(rcu)();
-	desc = mt_find(&sparse_irqs, &index, nr_irqs);
+	desc = mt_find(&sparse_irqs, &index, total_nr_irqs);
 
-	return desc ? irq_desc_get_irq(desc) : nr_irqs;
+	return desc ? irq_desc_get_irq(desc) : total_nr_irqs;
  }
 
  static void irq_insert_desc(unsigned int irq, struct irq_desc *desc)
@@ -543,7 +543,7 @@ static bool irq_expand_nr_irqs(unsigned
  {
  	if (nr > MAX_SPARSE_IRQS)
  		return false;
-	nr_irqs = nr;
+	total_nr_irqs = nr;
  	return true;
  }
 
@@ -557,16 +557,16 @@ int __init early_irq_init(void)
  	/* Let arch update nr_irqs and return the nr of preallocated irqs */
  	initcnt = arch_probe_nr_irqs();
  	printk(KERN_INFO "NR_IRQS: %d, nr_irqs: %d, preallocated irqs: %d\n",
-	       NR_IRQS, nr_irqs, initcnt);
+	       NR_IRQS, total_nr_irqs, initcnt);
 
-	if (WARN_ON(nr_irqs > MAX_SPARSE_IRQS))
-		nr_irqs = MAX_SPARSE_IRQS;
+	if (WARN_ON(total_nr_irqs > MAX_SPARSE_IRQS))
+		total_nr_irqs = MAX_SPARSE_IRQS;
 
  	if (WARN_ON(initcnt > MAX_SPARSE_IRQS))
  		initcnt = MAX_SPARSE_IRQS;
 
-	if (initcnt > nr_irqs)
-		nr_irqs = initcnt;
+	if (initcnt > total_nr_irqs)
+		total_nr_irqs = initcnt;
 
  	for (i = 0; i < initcnt; i++) {
  		desc = alloc_desc(i, node, 0, NULL, NULL);
@@ -862,7 +862,7 @@ void irq_free_descs(unsigned int from, u
  {
  	int i;
 
-	if (from >= nr_irqs || (from + cnt) > nr_irqs)
+	if (from >= total_nr_irqs || (from + cnt) > total_nr_irqs)
  		return;
 
  	guard(mutex)(&sparse_irq_lock);
@@ -911,7 +911,7 @@ int __ref __irq_alloc_descs(int irq, uns
  	if (irq >=0 && start != irq)
  		return -EEXIST;
 
-	if (start + cnt > nr_irqs) {
+	if (start + cnt > total_nr_irqs) {
  		if (!irq_expand_nr_irqs(start + cnt))
  			return -ENOMEM;
  	}
@@ -923,7 +923,7 @@ EXPORT_SYMBOL_GPL(__irq_alloc_descs);
   * irq_get_next_irq - get next allocated irq number
   * @offset:	where to start the search
   *
- * Returns next irq number after offset or nr_irqs if none is found.
+ * Returns next irq number after offset or total_nr_irqs if none is found.
   */
  unsigned int irq_get_next_irq(unsigned int offset)
  {
kernel/irq/proc.c +2 -2
@@ -447,7 +447,7 @@ int __weak arch_show_interrupts(struct s
  }
 
  #ifndef ACTUAL_NR_IRQS
-# define ACTUAL_NR_IRQS irq_get_nr_irqs()
+# define ACTUAL_NR_IRQS total_nr_irqs
  #endif
 
  /* Same as seq_put_decimal_ull_width(p, " ", cnt, 10) */
@@ -489,7 +489,7 @@ void irq_proc_emit_counts(struct seq_fil
 
  int show_interrupts(struct seq_file *p, void *v)
  {
-	const unsigned int nr_irqs = irq_get_nr_irqs();
+	const unsigned int nr_irqs = total_nr_irqs;
  	static int prec;
 
  	int i = *(loff_t *) v, j;

scripts/gdb/linux/interrupts.py irq_desc_type = utils.CachedType("struct · line 180

Quoted 8 lines
scripts/gdb/linux/interrupts.py +1 -1
@@ -174,7 +174,7 @@ irq_desc_type = utils.CachedType("struct
          super(LxInterruptList, self).__init__("lx-interruptlist", gdb.COMMAND_DATA)
 
      def invoke(self, arg, from_tty):
-        nr_irqs = gdb.parse_and_eval("nr_irqs")
+        nr_irqs = gdb.parse_and_eval("total_nr_irqs")
          prec = 3
          j = 1000
          while prec < 10 and j <= nr_irqs:

nr_irqs was used as arg in some of those irq_chip methods. This can help avoid any such confusion.

#23 lore raw

[tip: irq/core] genirq: Expose nr_irqs in core code

In reply to Thomas Gleixner
The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     b99dc723b12ea587fc8b2e07bd8401433eec58d8
Gitweb:        https://git.kernel.org/tip/b99dc723b12ea587fc8b2e07bd8401433eec58d8
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Sun, 17 May 2026 22:02:09 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Tue, 26 May 2026 16:21:13 +02:00

genirq: Expose nr_irqs in core code

... to avoid function calls in the core code to retrieve the maximum number of interrupts.

Rename it to 'total_nr_irqs' as 'nr_irqs' is too generic and fix up the 'nr_irqs' reference in the related GDB script as well.

---
 kernel/irq/internals.h          |  1 +
 kernel/irq/irqdesc.c            | 28 ++++++++++++++--------------
 kernel/irq/proc.c               |  4 ++--
 scripts/gdb/linux/interrupts.py |  2 +-
 4 files changed, 18 insertions(+), 17 deletions(-)
kernel/irq/internals.h +1 -0
index 9412e57..db359ac 100644
@@ -21,6 +21,7 @@
 
 extern bool noirqdebug;
 extern int irq_poll_cpu;
+extern unsigned int total_nr_irqs;
 
 extern struct irqaction chained_action;
 
kernel/irq/irqdesc.c +14 -14
index 7173b8b..a7ce1e8 100644
@@ -140,14 +140,14 @@ static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node,
 	desc_smp_init(desc, node, affinity);
 }
 
-static unsigned int nr_irqs = NR_IRQS;
+unsigned int total_nr_irqs __read_mostly = NR_IRQS;
 
 /**
  * irq_get_nr_irqs() - Number of interrupts supported by the system.
  */
 unsigned int irq_get_nr_irqs(void)
 {
-	return nr_irqs;
+	return total_nr_irqs;
 }
 EXPORT_SYMBOL_GPL(irq_get_nr_irqs);
 
@@ -159,7 +159,7 @@ EXPORT_SYMBOL_GPL(irq_get_nr_irqs);
  */
 unsigned int irq_set_nr_irqs(unsigned int nr)
 {
-	nr_irqs = nr;
+	total_nr_irqs = nr;
 
 	return nr;
 }
@@ -187,9 +187,9 @@ static unsigned int irq_find_at_or_after(unsigned int offset)
 	struct irq_desc *desc;
 
 	guard(rcu)();
-	desc = mt_find(&sparse_irqs, &index, nr_irqs);
+	desc = mt_find(&sparse_irqs, &index, total_nr_irqs);
 
-	return desc ? irq_desc_get_irq(desc) : nr_irqs;
+	return desc ? irq_desc_get_irq(desc) : total_nr_irqs;
 }
 
 static void irq_insert_desc(unsigned int irq, struct irq_desc *desc)
@@ -543,7 +543,7 @@ static bool irq_expand_nr_irqs(unsigned int nr)
 {
 	if (nr > MAX_SPARSE_IRQS)
 		return false;
-	nr_irqs = nr;
+	total_nr_irqs = nr;
 	return true;
 }
 
@@ -557,16 +557,16 @@ int __init early_irq_init(void)
 	/* Let arch update nr_irqs and return the nr of preallocated irqs */
 	initcnt = arch_probe_nr_irqs();
 	printk(KERN_INFO "NR_IRQS: %d, nr_irqs: %d, preallocated irqs: %d\n",
-	       NR_IRQS, nr_irqs, initcnt);
+	       NR_IRQS, total_nr_irqs, initcnt);
 
-	if (WARN_ON(nr_irqs > MAX_SPARSE_IRQS))
-		nr_irqs = MAX_SPARSE_IRQS;
+	if (WARN_ON(total_nr_irqs > MAX_SPARSE_IRQS))
+		total_nr_irqs = MAX_SPARSE_IRQS;
 
 	if (WARN_ON(initcnt > MAX_SPARSE_IRQS))
 		initcnt = MAX_SPARSE_IRQS;
 
-	if (initcnt > nr_irqs)
-		nr_irqs = initcnt;
+	if (initcnt > total_nr_irqs)
+		total_nr_irqs = initcnt;
 
 	for (i = 0; i < initcnt; i++) {
 		desc = alloc_desc(i, node, 0, NULL, NULL);
@@ -862,7 +862,7 @@ void irq_free_descs(unsigned int from, unsigned int cnt)
 {
 	int i;
 
-	if (from >= nr_irqs || (from + cnt) > nr_irqs)
+	if (from >= total_nr_irqs || (from + cnt) > total_nr_irqs)
 		return;
 
 	guard(mutex)(&sparse_irq_lock);
@@ -911,7 +911,7 @@ int __ref __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int no
 	if (irq >=0 && start != irq)
 		return -EEXIST;
 
-	if (start + cnt > nr_irqs) {
+	if (start + cnt > total_nr_irqs) {
 		if (!irq_expand_nr_irqs(start + cnt))
 			return -ENOMEM;
 	}
@@ -923,7 +923,7 @@ EXPORT_SYMBOL_GPL(__irq_alloc_descs);
  * irq_get_next_irq - get next allocated irq number
  * @offset:	where to start the search
  *
- * Returns next irq number after offset or nr_irqs if none is found.
+ * Returns next irq number after offset or total_nr_irqs if none is found.
  */
 unsigned int irq_get_next_irq(unsigned int offset)
 {
kernel/irq/proc.c +2 -2
index 5a1805b..d5b8126 100644
@@ -448,7 +448,7 @@ int __weak arch_show_interrupts(struct seq_file *p, int prec)
 }
 
 #ifndef ACTUAL_NR_IRQS
-# define ACTUAL_NR_IRQS irq_get_nr_irqs()
+# define ACTUAL_NR_IRQS total_nr_irqs
 #endif
 
 /* Same as seq_put_decimal_ull_width(p, " ", cnt, 10) */
@@ -490,7 +490,7 @@ void irq_proc_emit_counts(struct seq_file *p, unsigned int __percpu *cnts)
 
 int show_interrupts(struct seq_file *p, void *v)
 {
-	const unsigned int nr_irqs = irq_get_nr_irqs();
+	const unsigned int nr_irqs = total_nr_irqs;
 	static int prec;
 
 	int i = *(loff_t *) v, j;
scripts/gdb/linux/interrupts.py +1 -1
index b794e01..a4e25d2 100644
@@ -174,7 +174,7 @@ class LxInterruptList(gdb.Command):
         super(LxInterruptList, self).__init__("lx-interruptlist", gdb.COMMAND_DATA)
 
     def invoke(self, arg, from_tty):
-        nr_irqs = gdb.parse_and_eval("nr_irqs")
+        nr_irqs = gdb.parse_and_eval("total_nr_irqs")
         prec = 3
         j = 1000
         while prec < 10 and j <= nr_irqs:
author maintainer
#24 lore raw

PATCH v6 9/16 genirq/manage: Make NMI cleanup RT safe

In reply to Thomas Gleixner

From: Thomas Gleixner <[email protected]>

Eventually blocking functions cannot be invoked with interrupts disabled and a raw spin lock held. Restructure the code so this happens outside of the descriptor lock held region.

---
V4: New patch. Found when adding the validation update to it

---
 kernel/irq/manage.c |   37 +++++++++++++++++++++----------------
 1 file changed, 21 insertions(+), 16 deletions(-)
kernel/irq/manage.c +21 -16
@@ -2026,24 +2026,30 @@ const void *free_irq(unsigned int irq, v
 }
 EXPORT_SYMBOL(free_irq);
 
-/* This function must be called with desc->lock held */
 static const void *__cleanup_nmi(unsigned int irq, struct irq_desc *desc)
 {
+	struct irqaction *action = NULL;
 	const char *devname = NULL;
 
-	desc->istate &= ~IRQS_NMI;
+	scoped_guard(raw_spinlock_irqsave, &desc->lock) {
+		irq_nmi_teardown(desc);
 
-	if (!WARN_ON(desc->action == NULL)) {
-		irq_pm_remove_action(desc, desc->action);
-		devname = desc->action->name;
-		unregister_handler_proc(irq, desc->action);
+		desc->istate &= ~IRQS_NMI;
 
-		kfree(desc->action);
+		if (!WARN_ON(desc->action == NULL)) {
+			action = desc->action;
+			irq_pm_remove_action(desc, action);
+			devname = action->name;
+		}
 		desc->action = NULL;
+
+		irq_settings_clr_disable_unlazy(desc);
+		irq_shutdown_and_deactivate(desc);
 	}
 
-	irq_settings_clr_disable_unlazy(desc);
-	irq_shutdown_and_deactivate(desc);
+	if (action)
+		unregister_handler_proc(irq, action);
+	kfree(action);
 
 	irq_release_resources(desc);
 
@@ -2067,8 +2073,6 @@ const void *free_nmi(unsigned int irq, v
 	if (WARN_ON(desc->depth == 0))
 		disable_nmi_nosync(irq);
 
-	guard(raw_spinlock_irqsave)(&desc->lock);
-	irq_nmi_teardown(desc);
 	return __cleanup_nmi(irq, desc);
 }
 
@@ -2318,13 +2322,14 @@ int request_nmi(unsigned int irq, irq_ha
 		/* Setup NMI state */
 		desc->istate |= IRQS_NMI;
 		retval = irq_nmi_setup(desc);
-		if (retval) {
-			__cleanup_nmi(irq, desc);
-			return -EINVAL;
-		}
-		return 0;
 	}
 
+	if (retval) {
+		__cleanup_nmi(irq, desc);
+		return -EINVAL;
+	}
+	return 0;
+
 err_irq_setup:
 	irq_chip_pm_put(&desc->irq_data);
 err_out:
#25 lore raw

[tip: irq/core] genirq/manage: Make NMI cleanup RT safe

The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     3ba92f6a28203e30d0b2c7d75b59f48d5ff9fbcc
Gitweb:        https://git.kernel.org/tip/3ba92f6a28203e30d0b2c7d75b59f48d5ff9fbcc
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Sun, 17 May 2026 22:02:14 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Tue, 26 May 2026 16:21:13 +02:00

genirq/manage: Make NMI cleanup RT safe

Eventually blocking functions cannot be invoked with interrupts disabled and a raw spin lock held. Restructure the code so this happens outside of the descriptor lock held region.

---
 kernel/irq/manage.c | 37 +++++++++++++++++++++----------------
 1 file changed, 21 insertions(+), 16 deletions(-)
kernel/irq/manage.c +21 -16
index 2e80724..8863b2d 100644
@@ -2026,24 +2026,30 @@ const void *free_irq(unsigned int irq, void *dev_id)
 }
 EXPORT_SYMBOL(free_irq);
 
-/* This function must be called with desc->lock held */
 static const void *__cleanup_nmi(unsigned int irq, struct irq_desc *desc)
 {
+	struct irqaction *action = NULL;
 	const char *devname = NULL;
 
-	desc->istate &= ~IRQS_NMI;
+	scoped_guard(raw_spinlock_irqsave, &desc->lock) {
+		irq_nmi_teardown(desc);
 
-	if (!WARN_ON(desc->action == NULL)) {
-		irq_pm_remove_action(desc, desc->action);
-		devname = desc->action->name;
-		unregister_handler_proc(irq, desc->action);
+		desc->istate &= ~IRQS_NMI;
 
-		kfree(desc->action);
+		if (!WARN_ON(desc->action == NULL)) {
+			action = desc->action;
+			irq_pm_remove_action(desc, action);
+			devname = action->name;
+		}
 		desc->action = NULL;
+
+		irq_settings_clr_disable_unlazy(desc);
+		irq_shutdown_and_deactivate(desc);
 	}
 
-	irq_settings_clr_disable_unlazy(desc);
-	irq_shutdown_and_deactivate(desc);
+	if (action)
+		unregister_handler_proc(irq, action);
+	kfree(action);
 
 	irq_release_resources(desc);
 
@@ -2067,8 +2073,6 @@ const void *free_nmi(unsigned int irq, void *dev_id)
 	if (WARN_ON(desc->depth == 0))
 		disable_nmi_nosync(irq);
 
-	guard(raw_spinlock_irqsave)(&desc->lock);
-	irq_nmi_teardown(desc);
 	return __cleanup_nmi(irq, desc);
 }
 
@@ -2318,13 +2322,14 @@ int request_nmi(unsigned int irq, irq_handler_t handler,
 		/* Setup NMI state */
 		desc->istate |= IRQS_NMI;
 		retval = irq_nmi_setup(desc);
-		if (retval) {
-			__cleanup_nmi(irq, desc);
-			return -EINVAL;
-		}
-		return 0;
 	}
 
+	if (retval) {
+		__cleanup_nmi(irq, desc);
+		return -EINVAL;
+	}
+	return 0;
+
 err_irq_setup:
 	irq_chip_pm_put(&desc->irq_data);
 err_out:
author maintainer
#26 lore raw

PATCH v6 10/16 genirq: Cache the condition for /proc/interrupts exposure

In reply to Thomas Gleixner

From: Thomas Gleixner <[email protected]>

show_interrupts() evaluates a boatload of conditions to establish whether it should expose an interrupt in /proc/interrupts or not.

That can be simplified by caching the condition in an internal status flag, which is updated when one of the relevant inputs changes.

The irq_desc::kstat_irq check is dropped because visible interrupt descriptors always have a valid pointer.

As a result the number of instructions and branches for reading /proc/interrupts is reduced significantly.

---
V4: Cover freeing of per CPU and NMI type interrupts
V3: Remove the historical kstat_irq check - Radu
V2: s/IRQF_/IRQ/ and fixup the enum treatment - Dmitry

---
 include/linux/irq.h    |    1 +
 kernel/irq/chip.c      |    2 ++
 kernel/irq/internals.h |    2 ++
 kernel/irq/manage.c    |    8 +++++++-
 kernel/irq/proc.c      |   15 +++++++++++----
 kernel/irq/settings.h  |   13 +++++++++++++
 6 files changed, 36 insertions(+), 5 deletions(-)
include/linux/irq.h +1 -0
@@ -103,6 +103,7 @@ enum {
 	IRQ_DISABLE_UNLAZY	= (1 << 19),
 	IRQ_HIDDEN		= (1 << 20),
 	IRQ_NO_DEBUG		= (1 << 21),
+	IRQ_RESERVED		= (1 << 22),
 };
 
 #define IRQF_MODIFY_MASK	\
kernel/irq/chip.c +2 -0
@@ -1007,6 +1007,7 @@ static void
 		WARN_ON(irq_chip_pm_get(irq_desc_get_irq_data(desc)));
 		irq_activate_and_startup(desc, IRQ_RESEND);
 	}
+	irq_proc_update_valid(desc);
 }
 
 void __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
@@ -1067,6 +1068,7 @@ void irq_modify_status(unsigned int irq,
 			trigger = tmp;
 
 		irqd_set(&desc->irq_data, trigger);
+		irq_proc_update_valid(desc);
 	}
 }
 EXPORT_SYMBOL_GPL(irq_modify_status);
kernel/irq/internals.h +2 -0
@@ -123,6 +123,7 @@ extern void register_irq_proc(unsigned i
 extern void unregister_irq_proc(unsigned int irq, struct irq_desc *desc);
 extern void register_handler_proc(unsigned int irq, struct irqaction *action);
 extern void unregister_handler_proc(unsigned int irq, struct irqaction *action);
+void irq_proc_update_valid(struct irq_desc *desc);
 #else
 static inline void register_irq_proc(unsigned int irq, struct irq_desc *desc) { }
 static inline void unregister_irq_proc(unsigned int irq, struct irq_desc *desc) { }
@@ -130,6 +131,7 @@ static inline void register_handler_proc
 					 struct irqaction *action) { }
 static inline void unregister_handler_proc(unsigned int irq,
 					   struct irqaction *action) { }
+static inline void irq_proc_update_valid(struct irq_desc *desc) { }
 #endif
 
 extern bool irq_can_set_affinity_usr(unsigned int irq);
kernel/irq/manage.c +7 -1
@@ -1802,6 +1802,7 @@ static int
 		__enable_irq(desc);
 	}
 
+	irq_proc_update_valid(desc);
 	raw_spin_unlock_irqrestore(&desc->lock, flags);
 	chip_bus_sync_unlock(desc);
 	mutex_unlock(&desc->request_mutex);
@@ -1906,6 +1907,7 @@ static struct irqaction *__free_irq(stru
 		desc->affinity_hint = NULL;
 #endif
 
+	irq_proc_update_valid(desc);
 	raw_spin_unlock_irqrestore(&desc->lock, flags);
 	/*
 	 * Drop bus_lock here so the changes which were done in the chip
@@ -2047,6 +2049,8 @@ static const void *__cleanup_nmi(unsigne
 		irq_shutdown_and_deactivate(desc);
 	}
 
+	irq_proc_update_valid(desc);
+
 	if (action)
 		unregister_handler_proc(irq, action);
 	kfree(action);
@@ -2433,8 +2437,10 @@ static struct irqaction *__free_percpu_i
 		*action_ptr = action->next;
 
 		/* Demote from NMI if we killed the last action */
-		if (!desc->action)
+		if (!desc->action) {
 			desc->istate &= ~IRQS_NMI;
+			irq_proc_update_valid(desc);
+		}
 	}
 
 	unregister_handler_proc(irq, action);
kernel/irq/proc.c +11 -4
@@ -439,6 +439,16 @@ void init_irq_proc(void)
 		register_irq_proc(irq, desc);
 }
 
+void irq_proc_update_valid(struct irq_desc *desc)
+{
+	u32 set = _IRQ_PROC_VALID;
+
+	if (irq_settings_is_hidden(desc) || irq_desc_is_chained(desc) || !desc->action)
+		set = 0;
+
+	irq_settings_update_proc_valid(desc, set);
+}
+
 #ifdef CONFIG_GENERIC_IRQ_SHOW
 
 int __weak arch_show_interrupts(struct seq_file *p, int prec)
@@ -515,10 +525,7 @@ int show_interrupts(struct seq_file *p,
 
 	guard(rcu)();
 	desc = irq_to_desc(i);
-	if (!desc || irq_settings_is_hidden(desc))
-		return 0;
-
-	if (!desc->action || irq_desc_is_chained(desc) || !desc->kstat_irqs)
+	if (!desc || !irq_settings_proc_valid(desc))
 		return 0;
 
 	seq_printf(p, "%*d:", prec, i);
kernel/irq/settings.h +13 -0
@@ -18,6 +18,7 @@ enum {
 	_IRQ_DISABLE_UNLAZY	= IRQ_DISABLE_UNLAZY,
 	_IRQ_HIDDEN		= IRQ_HIDDEN,
 	_IRQ_NO_DEBUG		= IRQ_NO_DEBUG,
+	_IRQ_PROC_VALID		= IRQ_RESERVED,
 	_IRQF_MODIFY_MASK	= IRQF_MODIFY_MASK,
 };
 
@@ -34,6 +35,7 @@ enum {
 #define IRQ_DISABLE_UNLAZY	GOT_YOU_MORON
 #define IRQ_HIDDEN		GOT_YOU_MORON
 #define IRQ_NO_DEBUG		GOT_YOU_MORON
+#define IRQ_RESERVED		GOT_YOU_MORON
 #undef IRQF_MODIFY_MASK
 #define IRQF_MODIFY_MASK	GOT_YOU_MORON
 
@@ -180,3 +182,14 @@ static inline bool irq_settings_no_debug
 {
 	return desc->status_use_accessors & _IRQ_NO_DEBUG;
 }
+
+static inline bool irq_settings_proc_valid(struct irq_desc *desc)
+{
+	return desc->status_use_accessors & _IRQ_PROC_VALID;
+}
+
+static inline void irq_settings_update_proc_valid(struct irq_desc *desc, u32 set)
+{
+	desc->status_use_accessors &= ~_IRQ_PROC_VALID;
+	desc->status_use_accessors |= (set & _IRQ_PROC_VALID);
+}
#27 lore raw

[tip: irq/core] genirq: Cache the condition for /proc/interrupts exposure

The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     4892e5e71ec9c942293cea7fd26e0c76179211ed
Gitweb:        https://git.kernel.org/tip/4892e5e71ec9c942293cea7fd26e0c76179211ed
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Sun, 17 May 2026 22:02:19 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Tue, 26 May 2026 16:21:13 +02:00

genirq: Cache the condition for /proc/interrupts exposure

show_interrupts() evaluates a boatload of conditions to establish whether it should expose an interrupt in /proc/interrupts or not.

That can be simplified by caching the condition in an internal status flag, which is updated when one of the relevant inputs changes.

The irq_desc::kstat_irq check is dropped because visible interrupt descriptors always have a valid pointer.

As a result the number of instructions and branches for reading /proc/interrupts is reduced significantly.

---
 include/linux/irq.h    |  1 +
 kernel/irq/chip.c      |  2 ++
 kernel/irq/internals.h |  2 ++
 kernel/irq/manage.c    |  8 +++++++-
 kernel/irq/proc.c      | 15 +++++++++++----
 kernel/irq/settings.h  | 13 +++++++++++++
 6 files changed, 36 insertions(+), 5 deletions(-)
include/linux/irq.h +1 -0
index efa514e..f485369 100644
@@ -103,6 +103,7 @@ enum {
 	IRQ_DISABLE_UNLAZY	= (1 << 19),
 	IRQ_HIDDEN		= (1 << 20),
 	IRQ_NO_DEBUG		= (1 << 21),
+	IRQ_RESERVED		= (1 << 22),
 };
 
 #define IRQF_MODIFY_MASK	\
kernel/irq/chip.c +2 -0
index 6c9b1dc..2d283d1 100644
@@ -1007,6 +1007,7 @@ __irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
 		WARN_ON(irq_chip_pm_get(irq_desc_get_irq_data(desc)));
 		irq_activate_and_startup(desc, IRQ_RESEND);
 	}
+	irq_proc_update_valid(desc);
 }
 
 void __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
@@ -1067,6 +1068,7 @@ void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set)
 			trigger = tmp;
 
 		irqd_set(&desc->irq_data, trigger);
+		irq_proc_update_valid(desc);
 	}
 }
 EXPORT_SYMBOL_GPL(irq_modify_status);
kernel/irq/internals.h +2 -0
index db359ac..e5f3d31 100644
@@ -123,6 +123,7 @@ extern void register_irq_proc(unsigned int irq, struct irq_desc *desc);
 extern void unregister_irq_proc(unsigned int irq, struct irq_desc *desc);
 extern void register_handler_proc(unsigned int irq, struct irqaction *action);
 extern void unregister_handler_proc(unsigned int irq, struct irqaction *action);
+void irq_proc_update_valid(struct irq_desc *desc);
 #else
 static inline void register_irq_proc(unsigned int irq, struct irq_desc *desc) { }
 static inline void unregister_irq_proc(unsigned int irq, struct irq_desc *desc) { }
@@ -130,6 +131,7 @@ static inline void register_handler_proc(unsigned int irq,
 					 struct irqaction *action) { }
 static inline void unregister_handler_proc(unsigned int irq,
 					   struct irqaction *action) { }
+static inline void irq_proc_update_valid(struct irq_desc *desc) { }
 #endif
 
 extern bool irq_can_set_affinity_usr(unsigned int irq);
kernel/irq/manage.c +7 -1
index 8863b2d..7eb07e3 100644
@@ -1802,6 +1802,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
 		__enable_irq(desc);
 	}
 
+	irq_proc_update_valid(desc);
 	raw_spin_unlock_irqrestore(&desc->lock, flags);
 	chip_bus_sync_unlock(desc);
 	mutex_unlock(&desc->request_mutex);
@@ -1906,6 +1907,7 @@ static struct irqaction *__free_irq(struct irq_desc *desc, void *dev_id)
 		desc->affinity_hint = NULL;
 #endif
 
+	irq_proc_update_valid(desc);
 	raw_spin_unlock_irqrestore(&desc->lock, flags);
 	/*
 	 * Drop bus_lock here so the changes which were done in the chip
@@ -2047,6 +2049,8 @@ static const void *__cleanup_nmi(unsigned int irq, struct irq_desc *desc)
 		irq_shutdown_and_deactivate(desc);
 	}
 
+	irq_proc_update_valid(desc);
+
 	if (action)
 		unregister_handler_proc(irq, action);
 	kfree(action);
@@ -2433,8 +2437,10 @@ static struct irqaction *__free_percpu_irq(unsigned int irq, void __percpu *dev_
 		*action_ptr = action->next;
 
 		/* Demote from NMI if we killed the last action */
-		if (!desc->action)
+		if (!desc->action) {
 			desc->istate &= ~IRQS_NMI;
+			irq_proc_update_valid(desc);
+		}
 	}
 
 	unregister_handler_proc(irq, action);
kernel/irq/proc.c +11 -4
index d5b8126..cc9c7c9 100644
@@ -440,6 +440,16 @@ void init_irq_proc(void)
 		register_irq_proc(irq, desc);
 }
 
+void irq_proc_update_valid(struct irq_desc *desc)
+{
+	u32 set = _IRQ_PROC_VALID;
+
+	if (irq_settings_is_hidden(desc) || irq_desc_is_chained(desc) || !desc->action)
+		set = 0;
+
+	irq_settings_update_proc_valid(desc, set);
+}
+
 #ifdef CONFIG_GENERIC_IRQ_SHOW
 
 int __weak arch_show_interrupts(struct seq_file *p, int prec)
@@ -516,10 +526,7 @@ int show_interrupts(struct seq_file *p, void *v)
 
 	guard(rcu)();
 	desc = irq_to_desc(i);
-	if (!desc || irq_settings_is_hidden(desc))
-		return 0;
-
-	if (!desc->action || irq_desc_is_chained(desc) || !desc->kstat_irqs)
+	if (!desc || !irq_settings_proc_valid(desc))
 		return 0;
 
 	seq_printf(p, "%*d:", prec, i);
kernel/irq/settings.h +13 -0
index 00b3bd1..0a0c027 100644
@@ -18,6 +18,7 @@ enum {
 	_IRQ_DISABLE_UNLAZY	= IRQ_DISABLE_UNLAZY,
 	_IRQ_HIDDEN		= IRQ_HIDDEN,
 	_IRQ_NO_DEBUG		= IRQ_NO_DEBUG,
+	_IRQ_PROC_VALID		= IRQ_RESERVED,
 	_IRQF_MODIFY_MASK	= IRQF_MODIFY_MASK,
 };
 
@@ -34,6 +35,7 @@ enum {
 #define IRQ_DISABLE_UNLAZY	GOT_YOU_MORON
 #define IRQ_HIDDEN		GOT_YOU_MORON
 #define IRQ_NO_DEBUG		GOT_YOU_MORON
+#define IRQ_RESERVED		GOT_YOU_MORON
 #undef IRQF_MODIFY_MASK
 #define IRQF_MODIFY_MASK	GOT_YOU_MORON
 
@@ -180,3 +182,14 @@ static inline bool irq_settings_no_debug(struct irq_desc *desc)
 {
 	return desc->status_use_accessors & _IRQ_NO_DEBUG;
 }
+
+static inline bool irq_settings_proc_valid(struct irq_desc *desc)
+{
+	return desc->status_use_accessors & _IRQ_PROC_VALID;
+}
+
+static inline void irq_settings_update_proc_valid(struct irq_desc *desc, u32 set)
+{
+	desc->status_use_accessors &= ~_IRQ_PROC_VALID;
+	desc->status_use_accessors |= (set & _IRQ_PROC_VALID);
+}
author maintainer
#28 lore raw

PATCH v6 11/16 genirq: Calculate precision only when required

In reply to Thomas Gleixner

From: Thomas Gleixner <[email protected]>

Calculating the precision of the interrupt number column on every initial show_interrupt() invocation is a pointless exercise as the underlying maximum number of interrupts rarely changes.

Calculate it only when that number is modified and let show_interrupts() use the cached value.

---
 kernel/irq/internals.h |    6 ++++++
 kernel/irq/irqdesc.c   |   10 ++++++----
 kernel/irq/proc.c      |   33 +++++++++++++++++++++++----------
 3 files changed, 35 insertions(+), 14 deletions(-)
kernel/irq/internals.h +6 -0
@@ -134,6 +134,12 @@ static inline void unregister_handler_pr
 static inline void irq_proc_update_valid(struct irq_desc *desc) { }
 #endif
 
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_GENERIC_IRQ_SHOW)
+void irq_proc_calc_prec(void);
+#else
+static inline void irq_proc_calc_prec(void) { }
+#endif
+
 extern bool irq_can_set_affinity_usr(unsigned int irq);
 
 extern int irq_do_set_affinity(struct irq_data *data,
kernel/irq/irqdesc.c +6 -4
@@ -157,13 +157,12 @@ EXPORT_SYMBOL_GPL(irq_get_nr_irqs);
  *
  * Return: @nr.
  */
-unsigned int irq_set_nr_irqs(unsigned int nr)
+unsigned int __init irq_set_nr_irqs(unsigned int nr)
 {
 	total_nr_irqs = nr;
-
+	irq_proc_calc_prec();
 	return nr;
 }
-EXPORT_SYMBOL_GPL(irq_set_nr_irqs);
 
 static DEFINE_MUTEX(sparse_irq_lock);
 static struct maple_tree sparse_irqs = MTREE_INIT_EXT(sparse_irqs,
@@ -544,6 +543,7 @@ static bool irq_expand_nr_irqs(unsigned
 	if (nr > MAX_SPARSE_IRQS)
 		return false;
 	total_nr_irqs = nr;
+	irq_proc_calc_prec();
 	return true;
 }
 
@@ -572,6 +572,7 @@ int __init early_irq_init(void)
 		desc = alloc_desc(i, node, 0, NULL, NULL);
 		irq_insert_desc(i, desc);
 	}
+	irq_proc_calc_prec();
 	return arch_early_irq_init();
 }
 
@@ -592,7 +593,7 @@ int __init early_irq_init(void)
 
 	init_irq_default_affinity();
 
-	printk(KERN_INFO "NR_IRQS: %d\n", NR_IRQS);
+	pr_info("NR_IRQS: %d\n", NR_IRQS);
 
 	count = ARRAY_SIZE(irq_desc);
 
@@ -602,6 +603,7 @@ int __init early_irq_init(void)
 			goto __free_desc_res;
 	}
 
+	irq_proc_calc_prec();
 	return arch_early_irq_init();
 
 __free_desc_res:
kernel/irq/proc.c +23 -10
@@ -456,10 +456,25 @@ int __weak arch_show_interrupts(struct s
 	return 0;
 }
 
+static struct irq_proc_constraints {
+	unsigned int	num_prec;
+} irq_proc_constraints __read_mostly = {
+	.num_prec	= 3,
+};
+
 #ifndef ACTUAL_NR_IRQS
 # define ACTUAL_NR_IRQS total_nr_irqs
 #endif
 
+void irq_proc_calc_prec(void)
+{
+	unsigned int prec, n;
+
+	for (prec = 3, n = 1000; prec < 10 && n <= total_nr_irqs; ++prec)
+		n *= 10;
+	WRITE_ONCE(irq_proc_constraints.num_prec, prec);
+}
+
 /* Same as seq_put_decimal_ull_width(p, " ", cnt, 10) */
 #define ZSTR1 "          0"
 #define ZSTR1_LEN	(sizeof(ZSTR1) - 1)
@@ -499,9 +514,7 @@ void irq_proc_emit_counts(struct seq_fil
 
 int show_interrupts(struct seq_file *p, void *v)
 {
-	const unsigned int nr_irqs = total_nr_irqs;
-	static int prec;
-
+	unsigned int prec = READ_ONCE(irq_proc_constraints.num_prec);
 	int i = *(loff_t *) v, j;
 	struct irqaction *action;
 	struct irq_desc *desc;
@@ -514,9 +527,6 @@ int show_interrupts(struct seq_file *p,
 
 	/* print header and calculate the width of the first column */
 	if (i == 0) {
-		for (prec = 3, j = 1000; prec < 10 && j <= nr_irqs; ++prec)
-			j *= 10;
-
 		seq_printf(p, "%*s", prec + 8, "");
 		for_each_online_cpu(j)
 			seq_printf(p, "CPU%-8d", j);
@@ -552,13 +562,16 @@ int show_interrupts(struct seq_file *p,
 	} else {
 		seq_printf(p, "%8s", "None");
 	}
+
+	seq_putc(p, ' ');
 	if (desc->irq_data.domain)
-		seq_printf(p, " %*lu", prec, desc->irq_data.hwirq);
+		seq_put_decimal_ull_width(p, "", desc->irq_data.hwirq, prec);
 	else
 		seq_printf(p, " %*s", prec, "");
-#ifdef CONFIG_GENERIC_IRQ_SHOW_LEVEL
-	seq_printf(p, " %-8s", irqd_is_level_type(&desc->irq_data) ? "Level" : "Edge");
-#endif
+
+	if (IS_ENABLED(CONFIG_GENERIC_IRQ_SHOW_LEVEL))
+		seq_printf(p, " %-8s", irqd_is_level_type(&desc->irq_data) ? "Level" : "Edge");
+
 	if (desc->name)
 		seq_printf(p, "-%-8s", desc->name);
 
#29 lore raw

[tip: irq/core] genirq: Calculate precision only when required

The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     2d62735f1d4a2832af367c9e3de04bfb280a945c
Gitweb:        https://git.kernel.org/tip/2d62735f1d4a2832af367c9e3de04bfb280a945c
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Sun, 17 May 2026 22:02:24 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Tue, 26 May 2026 16:21:14 +02:00

genirq: Calculate precision only when required

Calculating the precision of the interrupt number column on every initial show_interrupt() invocation is a pointless exercise as the underlying maximum number of interrupts rarely changes.

Calculate it only when that number is modified and let show_interrupts() use the cached value.

---
 kernel/irq/internals.h |  6 ++++++
 kernel/irq/irqdesc.c   | 10 ++++++----
 kernel/irq/proc.c      | 33 +++++++++++++++++++++++----------
 3 files changed, 35 insertions(+), 14 deletions(-)
kernel/irq/internals.h +6 -0
index e5f3d31..be702dd 100644
@@ -134,6 +134,12 @@ static inline void unregister_handler_proc(unsigned int irq,
 static inline void irq_proc_update_valid(struct irq_desc *desc) { }
 #endif
 
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_GENERIC_IRQ_SHOW)
+void irq_proc_calc_prec(void);
+#else
+static inline void irq_proc_calc_prec(void) { }
+#endif
+
 extern bool irq_can_set_affinity_usr(unsigned int irq);
 
 extern int irq_do_set_affinity(struct irq_data *data,
kernel/irq/irqdesc.c +6 -4
index a7ce1e8..90595ee 100644
@@ -157,13 +157,12 @@ EXPORT_SYMBOL_GPL(irq_get_nr_irqs);
  *
  * Return: @nr.
  */
-unsigned int irq_set_nr_irqs(unsigned int nr)
+unsigned int __init irq_set_nr_irqs(unsigned int nr)
 {
 	total_nr_irqs = nr;
-
+	irq_proc_calc_prec();
 	return nr;
 }
-EXPORT_SYMBOL_GPL(irq_set_nr_irqs);
 
 static DEFINE_MUTEX(sparse_irq_lock);
 static struct maple_tree sparse_irqs = MTREE_INIT_EXT(sparse_irqs,
@@ -544,6 +543,7 @@ static bool irq_expand_nr_irqs(unsigned int nr)
 	if (nr > MAX_SPARSE_IRQS)
 		return false;
 	total_nr_irqs = nr;
+	irq_proc_calc_prec();
 	return true;
 }
 
@@ -572,6 +572,7 @@ int __init early_irq_init(void)
 		desc = alloc_desc(i, node, 0, NULL, NULL);
 		irq_insert_desc(i, desc);
 	}
+	irq_proc_calc_prec();
 	return arch_early_irq_init();
 }
 
@@ -592,7 +593,7 @@ int __init early_irq_init(void)
 
 	init_irq_default_affinity();
 
-	printk(KERN_INFO "NR_IRQS: %d\n", NR_IRQS);
+	pr_info("NR_IRQS: %d\n", NR_IRQS);
 
 	count = ARRAY_SIZE(irq_desc);
 
@@ -602,6 +603,7 @@ int __init early_irq_init(void)
 			goto __free_desc_res;
 	}
 
+	irq_proc_calc_prec();
 	return arch_early_irq_init();
 
 __free_desc_res:
kernel/irq/proc.c +23 -10
index cc9c7c9..c635fb6 100644
@@ -457,10 +457,25 @@ int __weak arch_show_interrupts(struct seq_file *p, int prec)
 	return 0;
 }
 
+static struct irq_proc_constraints {
+	unsigned int	num_prec;
+} irq_proc_constraints __read_mostly = {
+	.num_prec	= 3,
+};
+
 #ifndef ACTUAL_NR_IRQS
 # define ACTUAL_NR_IRQS total_nr_irqs
 #endif
 
+void irq_proc_calc_prec(void)
+{
+	unsigned int prec, n;
+
+	for (prec = 3, n = 1000; prec < 10 && n <= total_nr_irqs; ++prec)
+		n *= 10;
+	WRITE_ONCE(irq_proc_constraints.num_prec, prec);
+}
+
 /* Same as seq_put_decimal_ull_width(p, " ", cnt, 10) */
 #define ZSTR1 "          0"
 #define ZSTR1_LEN	(sizeof(ZSTR1) - 1)
@@ -500,9 +515,7 @@ void irq_proc_emit_counts(struct seq_file *p, unsigned int __percpu *cnts)
 
 int show_interrupts(struct seq_file *p, void *v)
 {
-	const unsigned int nr_irqs = total_nr_irqs;
-	static int prec;
-
+	unsigned int prec = READ_ONCE(irq_proc_constraints.num_prec);
 	int i = *(loff_t *) v, j;
 	struct irqaction *action;
 	struct irq_desc *desc;
@@ -515,9 +528,6 @@ int show_interrupts(struct seq_file *p, void *v)
 
 	/* print header and calculate the width of the first column */
 	if (i == 0) {
-		for (prec = 3, j = 1000; prec < 10 && j <= nr_irqs; ++prec)
-			j *= 10;
-
 		seq_printf(p, "%*s", prec + 8, "");
 		for_each_online_cpu(j)
 			seq_printf(p, "CPU%-8d", j);
@@ -553,13 +563,16 @@ int show_interrupts(struct seq_file *p, void *v)
 	} else {
 		seq_printf(p, "%8s", "None");
 	}
+
+	seq_putc(p, ' ');
 	if (desc->irq_data.domain)
-		seq_printf(p, " %*lu", prec, desc->irq_data.hwirq);
+		seq_put_decimal_ull_width(p, "", desc->irq_data.hwirq, prec);
 	else
 		seq_printf(p, " %*s", prec, "");
-#ifdef CONFIG_GENERIC_IRQ_SHOW_LEVEL
-	seq_printf(p, " %-8s", irqd_is_level_type(&desc->irq_data) ? "Level" : "Edge");
-#endif
+
+	if (IS_ENABLED(CONFIG_GENERIC_IRQ_SHOW_LEVEL))
+		seq_printf(p, " %-8s", irqd_is_level_type(&desc->irq_data) ? "Level" : "Edge");
+
 	if (desc->name)
 		seq_printf(p, "-%-8s", desc->name);
 
author maintainer
#30 lore raw

PATCH v6 12/16 genirq/proc: Increase default interrupt number precision to four

In reply to Thomas Gleixner

Quite some architectures have four character wide acronyms for architecture specific interrupts like IPI, NMI, etc.

The default precision of printing the Linux device interrupt numbers is three, which causes quite some code to play games with adding or omitting space after the acronym and the colon in order to keep the per CPU numbers properly aligned.

Increase the default number precision to four in the core code and get rid of the space games all over the place. At the same time align all architecture specific descriptor texts left so that they show up in the same column as the interrupt chip names, which makes the output more uniform accross architectures. Fix up the GDB script to this new scheme as well.

---
V6: New patch after Michael mentioned the various alignment issues.

---
 arch/alpha/kernel/irq.c         |    8 ++++----
 arch/arm/kernel/smp.c           |    3 +--
 arch/arm64/kernel/smp.c         |    5 ++---
 arch/loongarch/kernel/smp.c     |    2 +-
 arch/riscv/kernel/smp.c         |    3 +--
 arch/sh/kernel/irq.c            |    2 +-
 arch/sparc/kernel/irq_32.c      |   12 ++++++------
 arch/sparc/kernel/irq_64.c      |    4 ++--
 arch/um/kernel/irq.c            |    4 ++--
 arch/xtensa/kernel/irq.c        |    2 +-
 kernel/irq/proc.c               |    4 ++--
 scripts/gdb/linux/interrupts.py |   16 ++++++----------
 12 files changed, 29 insertions(+), 36 deletions(-)
arch/alpha/kernel/irq.c +4 -4
@@ -72,16 +72,16 @@ int arch_show_interrupts(struct seq_file
 	int j;
 
 #ifdef CONFIG_SMP
-	seq_puts(p, "IPI: ");
+	seq_puts(p, " IPI: ");
 	for_each_online_cpu(j)
 		seq_printf(p, "%10lu ", cpu_data[j].ipi_count);
 	seq_putc(p, '\n');
 #endif
-	seq_puts(p, "PMI: ");
+	seq_puts(p, " PMI: ");
 	for_each_online_cpu(j)
 		seq_printf(p, "%10lu ", per_cpu(irq_pmi_count, j));
-	seq_puts(p, "          Performance Monitoring\n");
-	seq_printf(p, "ERR: %10lu\n", irq_err_count);
+	seq_puts(p, " Performance Monitoring\n");
+	seq_printf(p, " ERR: %10lu\n", irq_err_count);
 	return 0;
 }
 
arch/arm/kernel/smp.c +1 -2
@@ -551,8 +551,7 @@ void show_ipi_list(struct seq_file *p, i
 		if (!ipi_desc[i])
 			continue;
 
-		seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i,
-			   prec >= 4 ? " " : "");
+		seq_printf(p, "%*s%u:", prec - 1, "IPI", i);
 
 		for_each_online_cpu(cpu)
 			seq_printf(p, "%10u ", irq_desc_kstat_cpu(ipi_desc[i], cpu));
arch/arm64/kernel/smp.c +2 -3
@@ -833,11 +833,10 @@ int arch_show_interrupts(struct seq_file
 	unsigned int cpu, i;
 
 	for (i = 0; i < MAX_IPI; i++) {
-		seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i,
-			   prec >= 4 ? " " : "");
+		seq_printf(p, "%*s%u: ", prec - 1, "IPI", i);
 		for_each_online_cpu(cpu)
 			seq_printf(p, "%10u ", irq_desc_kstat_cpu(get_ipi_desc(cpu, i), cpu));
-		seq_printf(p, "      %s\n", ipi_types[i]);
+		seq_printf(p, " %s\n", ipi_types[i]);
 	}
 
 	seq_printf(p, "%*s: %10lu\n", prec, "Err", irq_err_count);
arch/loongarch/kernel/smp.c +1 -1
@@ -88,7 +88,7 @@ void show_ipi_list(struct seq_file *p, i
 	unsigned int cpu, i;
 
 	for (i = 0; i < NR_IPI; i++) {
-		seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i, prec >= 4 ? " " : "");
+		seq_printf(p, "%*s%u:", prec - 1, "IPI", i);
 		for_each_online_cpu(cpu)
 			seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, cpu).ipi_irqs[i], 10);
 		seq_printf(p, " LoongArch  %d  %s\n", i + 1, ipi_types[i]);
arch/riscv/kernel/smp.c +1 -2
@@ -226,8 +226,7 @@ void show_ipi_stats(struct seq_file *p,
 	unsigned int cpu, i;
 
 	for (i = 0; i < IPI_MAX; i++) {
-		seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i,
-			   prec >= 4 ? " " : "");
+		seq_printf(p, "%*s%u:", prec - 1, "IPI", i);
 		for_each_online_cpu(cpu)
 			seq_printf(p, "%10u ", irq_desc_kstat_cpu(ipi_desc[i], cpu));
 		seq_printf(p, " %s\n", ipi_names[i]);
arch/sh/kernel/irq.c +1 -1
@@ -46,7 +46,7 @@ int arch_show_interrupts(struct seq_file
 	seq_printf(p, "%*s:", prec, "NMI");
 	for_each_online_cpu(j)
 		seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat.__nmi_count, j), 10);
-	seq_printf(p, "  Non-maskable interrupts\n");
+	seq_printf(p, " Non-maskable interrupts\n");
 
 	seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count));
 
arch/sparc/kernel/irq_32.c +6 -6
@@ -199,19 +199,19 @@ int arch_show_interrupts(struct seq_file
 	int j;
 
 #ifdef CONFIG_SMP
-	seq_printf(p, "RES:");
+	seq_printf(p, "%*s:", prec, "RES");
 	for_each_online_cpu(j)
 		seq_put_decimal_ull_width(p, " ", cpu_data(j).irq_resched_count, 10);
-	seq_printf(p, "     IPI rescheduling interrupts\n");
-	seq_printf(p, "CAL:");
+	seq_printf(p, " IPI rescheduling interrupts\n");
+	seq_printf(p, "%*s:", prec, "CAL");
 	for_each_online_cpu(j)
 		seq_put_decimal_ull_width(p, " ", cpu_data(j).irq_call_count, 10);
-	seq_printf(p, "     IPI function call interrupts\n");
+	seq_printf(p, " IPI function call interrupts\n");
 #endif
-	seq_printf(p, "NMI:");
+	seq_printf(p, "%*s:", prec, "NMI");
 	for_each_online_cpu(j)
 		seq_put_decimal_ull_width(p, " ", cpu_data(j).counter, 10);
-	seq_printf(p, "     Non-maskable interrupts\n");
+	seq_printf(p, " Non-maskable interrupts\n");
 	return 0;
 }
 
arch/sparc/kernel/irq_64.c +2 -2
@@ -303,10 +303,10 @@ int arch_show_interrupts(struct seq_file
 {
 	int j;
 
-	seq_printf(p, "NMI:");
+	seq_printf(p, "%*s:", prec, "NMI");
 	for_each_online_cpu(j)
 		seq_put_decimal_ull_width(p, " ", cpu_data(j).__nmi_count, 10);
-	seq_printf(p, "     Non-maskable interrupts\n");
+	seq_printf(p, " Non-maskable interrupts\n");
 	return 0;
 }
 
arch/um/kernel/irq.c +2 -2
@@ -716,12 +716,12 @@ int arch_show_interrupts(struct seq_file
 	seq_printf(p, "%*s: ", prec, "RES");
 	for_each_online_cpu(cpu)
 		seq_printf(p, "%10u ", irq_stats(cpu)->irq_resched_count);
-	seq_puts(p, "  Rescheduling interrupts\n");
+	seq_puts(p, " Rescheduling interrupts\n");
 
 	seq_printf(p, "%*s: ", prec, "CAL");
 	for_each_online_cpu(cpu)
 		seq_printf(p, "%10u ", irq_stats(cpu)->irq_call_count);
-	seq_puts(p, "  Function call interrupts\n");
+	seq_puts(p, " Function call interrupts\n");
 #endif
 
 	return 0;
arch/xtensa/kernel/irq.c +1 -1
@@ -59,7 +59,7 @@ int arch_show_interrupts(struct seq_file
 	seq_printf(p, "%*s:", prec, "NMI");
 	for_each_online_cpu(cpu)
 		seq_printf(p, " %10lu", per_cpu(nmi_count, cpu));
-	seq_puts(p, "   Non-maskable interrupts\n");
+	seq_puts(p, " Non-maskable interrupts\n");
 #endif
 	return 0;
 }
kernel/irq/proc.c +2 -2
@@ -459,7 +459,7 @@ int __weak arch_show_interrupts(struct s
 static struct irq_proc_constraints {
 	unsigned int	num_prec;
 } irq_proc_constraints __read_mostly = {
-	.num_prec	= 3,
+	.num_prec	= 4,
 };
 
 #ifndef ACTUAL_NR_IRQS
@@ -470,7 +470,7 @@ void irq_proc_calc_prec(void)
 {
 	unsigned int prec, n;
 
-	for (prec = 3, n = 1000; prec < 10 && n <= total_nr_irqs; ++prec)
+	for (prec = 4, n = 10000; prec < 10 && n <= total_nr_irqs; ++prec)
 		n *= 10;
 	WRITE_ONCE(irq_proc_constraints.num_prec, prec);
 }
scripts/gdb/linux/interrupts.py +6 -10
@@ -131,23 +131,19 @@ irq_desc_type = utils.CachedType("struct
     if nr_ipi is None or ipi_desc is None or ipi_types is None:
         return text
 
-    if prec >= 4:
-        sep = " "
-    else:
-        sep = ""
-
     for ipi in range(nr_ipi):
-        text += "%*s%u:%s" % (prec - 1, "IPI", ipi, sep)
+        text += "%*s%u: " % (prec - 1, "IPI", ipi)
         desc = ipi_desc[ipi].cast(irq_desc_type.get_type().pointer())
         if desc == 0:
             continue
         for cpu in cpus.each_online_cpu():
-            text += "%10u" % (cpus.per_cpu(desc['kstat_irqs'], cpu)['cnt'])
-        text += "      %s" % (ipi_types[ipi].string())
+            text += "%10u " % (cpus.per_cpu(desc['kstat_irqs'], cpu)['cnt'])
+        text += "%s" % (ipi_types[ipi].string())
         text += "\n"
     return text
 
 def aarch64_show_interrupts(prec):
+    # Does not work for ARM64 as "ipi_desc" is not available there
     text = arm_common_show_interrupts(prec)
     text += "%*s: %10lu\n" % (prec, "ERR", gdb.parse_and_eval("irq_err_count"))
     return text
@@ -175,8 +171,8 @@ irq_desc_type = utils.CachedType("struct
 
     def invoke(self, arg, from_tty):
         nr_irqs = gdb.parse_and_eval("total_nr_irqs")
-        prec = 3
-        j = 1000
+        prec = 4
+        j = 10000
         while prec < 10 and j <= nr_irqs:
             prec += 1
             j *= 10
#31 lore raw

[tip: irq/core] genirq/proc: Increase default interrupt number precision to four

The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     34594da7650d3ea67f96c0f4034ff6b2453f67c3
Gitweb:        https://git.kernel.org/tip/34594da7650d3ea67f96c0f4034ff6b2453f67c3
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Sun, 17 May 2026 22:02:29 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Tue, 26 May 2026 16:21:14 +02:00

genirq/proc: Increase default interrupt number precision to four

Quite some architectures have four character wide acronyms for architecture specific interrupts like IPI, NMI, etc.

The default precision of printing the Linux device interrupt numbers is three, which causes quite some code to play games with adding or omitting space after the acronym and the colon in order to keep the per CPU numbers properly aligned.

Increase the default number precision to four in the core code and get rid of the space games all over the place. At the same time align all architecture specific descriptor texts left so that they show up in the same column as the interrupt chip names, which makes the output more uniform accross architectures. Fix up the GDB script to this new scheme as well.

---
 arch/alpha/kernel/irq.c         |  8 ++++----
 arch/arm/kernel/smp.c           |  3 +--
 arch/arm64/kernel/smp.c         |  5 ++---
 arch/loongarch/kernel/smp.c     |  2 +-
 arch/riscv/kernel/smp.c         |  3 +--
 arch/sh/kernel/irq.c            |  2 +-
 arch/sparc/kernel/irq_32.c      | 12 ++++++------
 arch/sparc/kernel/irq_64.c      |  4 ++--
 arch/um/kernel/irq.c            |  4 ++--
 arch/xtensa/kernel/irq.c        |  2 +-
 kernel/irq/proc.c               |  4 ++--
 scripts/gdb/linux/interrupts.py | 16 ++++++----------
 12 files changed, 29 insertions(+), 36 deletions(-)
arch/alpha/kernel/irq.c +4 -4
index c67047c..4a6a8b1 100644
@@ -72,16 +72,16 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 	int j;
 
 #ifdef CONFIG_SMP
-	seq_puts(p, "IPI: ");
+	seq_puts(p, " IPI: ");
 	for_each_online_cpu(j)
 		seq_printf(p, "%10lu ", cpu_data[j].ipi_count);
 	seq_putc(p, '\n');
 #endif
-	seq_puts(p, "PMI: ");
+	seq_puts(p, " PMI: ");
 	for_each_online_cpu(j)
 		seq_printf(p, "%10lu ", per_cpu(irq_pmi_count, j));
-	seq_puts(p, "          Performance Monitoring\n");
-	seq_printf(p, "ERR: %10lu\n", irq_err_count);
+	seq_puts(p, " Performance Monitoring\n");
+	seq_printf(p, " ERR: %10lu\n", irq_err_count);
 	return 0;
 }
 
arch/arm/kernel/smp.c +1 -2
index 4e8e89a..b5fb469 100644
@@ -551,8 +551,7 @@ void show_ipi_list(struct seq_file *p, int prec)
 		if (!ipi_desc[i])
 			continue;
 
-		seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i,
-			   prec >= 4 ? " " : "");
+		seq_printf(p, "%*s%u:", prec - 1, "IPI", i);
 
 		for_each_online_cpu(cpu)
 			seq_printf(p, "%10u ", irq_desc_kstat_cpu(ipi_desc[i], cpu));
arch/arm64/kernel/smp.c +2 -3
index 1aa3241..1d0e0e6 100644
@@ -833,11 +833,10 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 	unsigned int cpu, i;
 
 	for (i = 0; i < MAX_IPI; i++) {
-		seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i,
-			   prec >= 4 ? " " : "");
+		seq_printf(p, "%*s%u: ", prec - 1, "IPI", i);
 		for_each_online_cpu(cpu)
 			seq_printf(p, "%10u ", irq_desc_kstat_cpu(get_ipi_desc(cpu, i), cpu));
-		seq_printf(p, "      %s\n", ipi_types[i]);
+		seq_printf(p, " %s\n", ipi_types[i]);
 	}
 
 	seq_printf(p, "%*s: %10lu\n", prec, "Err", irq_err_count);
arch/loongarch/kernel/smp.c +1 -1
index 64a048f..5092261 100644
@@ -88,7 +88,7 @@ void show_ipi_list(struct seq_file *p, int prec)
 	unsigned int cpu, i;
 
 	for (i = 0; i < NR_IPI; i++) {
-		seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i, prec >= 4 ? " " : "");
+		seq_printf(p, "%*s%u:", prec - 1, "IPI", i);
 		for_each_online_cpu(cpu)
 			seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, cpu).ipi_irqs[i], 10);
 		seq_printf(p, " LoongArch  %d  %s\n", i + 1, ipi_types[i]);
arch/riscv/kernel/smp.c +1 -2
index 5ed5095..fa66f9c 100644
@@ -226,8 +226,7 @@ void show_ipi_stats(struct seq_file *p, int prec)
 	unsigned int cpu, i;
 
 	for (i = 0; i < IPI_MAX; i++) {
-		seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i,
-			   prec >= 4 ? " " : "");
+		seq_printf(p, "%*s%u:", prec - 1, "IPI", i);
 		for_each_online_cpu(cpu)
 			seq_printf(p, "%10u ", irq_desc_kstat_cpu(ipi_desc[i], cpu));
 		seq_printf(p, " %s\n", ipi_names[i]);
arch/sh/kernel/irq.c +1 -1
index 9022d8a..03c39b5 100644
@@ -46,7 +46,7 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 	seq_printf(p, "%*s:", prec, "NMI");
 	for_each_online_cpu(j)
 		seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat.__nmi_count, j), 10);
-	seq_printf(p, "  Non-maskable interrupts\n");
+	seq_printf(p, " Non-maskable interrupts\n");
 
 	seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count));
 
arch/sparc/kernel/irq_32.c +6 -6
index 5210991..22db727 100644
@@ -199,19 +199,19 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 	int j;
 
 #ifdef CONFIG_SMP
-	seq_printf(p, "RES:");
+	seq_printf(p, "%*s:", prec, "RES");
 	for_each_online_cpu(j)
 		seq_put_decimal_ull_width(p, " ", cpu_data(j).irq_resched_count, 10);
-	seq_printf(p, "     IPI rescheduling interrupts\n");
-	seq_printf(p, "CAL:");
+	seq_printf(p, " IPI rescheduling interrupts\n");
+	seq_printf(p, "%*s:", prec, "CAL");
 	for_each_online_cpu(j)
 		seq_put_decimal_ull_width(p, " ", cpu_data(j).irq_call_count, 10);
-	seq_printf(p, "     IPI function call interrupts\n");
+	seq_printf(p, " IPI function call interrupts\n");
 #endif
-	seq_printf(p, "NMI:");
+	seq_printf(p, "%*s:", prec, "NMI");
 	for_each_online_cpu(j)
 		seq_put_decimal_ull_width(p, " ", cpu_data(j).counter, 10);
-	seq_printf(p, "     Non-maskable interrupts\n");
+	seq_printf(p, " Non-maskable interrupts\n");
 	return 0;
 }
 
arch/sparc/kernel/irq_64.c +2 -2
index c5466a9..3f55c69 100644
@@ -303,10 +303,10 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 {
 	int j;
 
-	seq_printf(p, "NMI:");
+	seq_printf(p, "%*s:", prec, "NMI");
 	for_each_online_cpu(j)
 		seq_put_decimal_ull_width(p, " ", cpu_data(j).__nmi_count, 10);
-	seq_printf(p, "     Non-maskable interrupts\n");
+	seq_printf(p, " Non-maskable interrupts\n");
 	return 0;
 }
 
arch/um/kernel/irq.c +2 -2
index 5929d49..ddfd6e9 100644
@@ -716,12 +716,12 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 	seq_printf(p, "%*s: ", prec, "RES");
 	for_each_online_cpu(cpu)
 		seq_printf(p, "%10u ", irq_stats(cpu)->irq_resched_count);
-	seq_puts(p, "  Rescheduling interrupts\n");
+	seq_puts(p, " Rescheduling interrupts\n");
 
 	seq_printf(p, "%*s: ", prec, "CAL");
 	for_each_online_cpu(cpu)
 		seq_printf(p, "%10u ", irq_stats(cpu)->irq_call_count);
-	seq_puts(p, "  Function call interrupts\n");
+	seq_puts(p, " Function call interrupts\n");
 #endif
 
 	return 0;
arch/xtensa/kernel/irq.c +1 -1
index b1e410f..6f01f53 100644
@@ -59,7 +59,7 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 	seq_printf(p, "%*s:", prec, "NMI");
 	for_each_online_cpu(cpu)
 		seq_printf(p, " %10lu", per_cpu(nmi_count, cpu));
-	seq_puts(p, "   Non-maskable interrupts\n");
+	seq_puts(p, " Non-maskable interrupts\n");
 #endif
 	return 0;
 }
kernel/irq/proc.c +2 -2
index c635fb6..1cb47a8 100644
@@ -460,7 +460,7 @@ int __weak arch_show_interrupts(struct seq_file *p, int prec)
 static struct irq_proc_constraints {
 	unsigned int	num_prec;
 } irq_proc_constraints __read_mostly = {
-	.num_prec	= 3,
+	.num_prec	= 4,
 };
 
 #ifndef ACTUAL_NR_IRQS
@@ -471,7 +471,7 @@ void irq_proc_calc_prec(void)
 {
 	unsigned int prec, n;
 
-	for (prec = 3, n = 1000; prec < 10 && n <= total_nr_irqs; ++prec)
+	for (prec = 4, n = 10000; prec < 10 && n <= total_nr_irqs; ++prec)
 		n *= 10;
 	WRITE_ONCE(irq_proc_constraints.num_prec, prec);
 }
scripts/gdb/linux/interrupts.py +6 -10
index a4e25d2..e967343 100644
@@ -131,23 +131,19 @@ def arm_common_show_interrupts(prec):
     if nr_ipi is None or ipi_desc is None or ipi_types is None:
         return text
 
-    if prec >= 4:
-        sep = " "
-    else:
-        sep = ""
-
     for ipi in range(nr_ipi):
-        text += "%*s%u:%s" % (prec - 1, "IPI", ipi, sep)
+        text += "%*s%u: " % (prec - 1, "IPI", ipi)
         desc = ipi_desc[ipi].cast(irq_desc_type.get_type().pointer())
         if desc == 0:
             continue
         for cpu in cpus.each_online_cpu():
-            text += "%10u" % (cpus.per_cpu(desc['kstat_irqs'], cpu)['cnt'])
-        text += "      %s" % (ipi_types[ipi].string())
+            text += "%10u " % (cpus.per_cpu(desc['kstat_irqs'], cpu)['cnt'])
+        text += "%s" % (ipi_types[ipi].string())
         text += "\n"
     return text
 
 def aarch64_show_interrupts(prec):
+    # Does not work for ARM64 as "ipi_desc" is not available there
     text = arm_common_show_interrupts(prec)
     text += "%*s: %10lu\n" % (prec, "ERR", gdb.parse_and_eval("irq_err_count"))
     return text
@@ -175,8 +171,8 @@ class LxInterruptList(gdb.Command):
 
     def invoke(self, arg, from_tty):
         nr_irqs = gdb.parse_and_eval("total_nr_irqs")
-        prec = 3
-        j = 1000
+        prec = 4
+        j = 10000
         while prec < 10 and j <= nr_irqs:
             prec += 1
             j *= 10
author maintainer
#32 lore raw

PATCH v6 13/16 genirq: Add rcuref count to struct irq_desc

In reply to Thomas Gleixner

From: Thomas Gleixner <[email protected]>

Prepare for a smarter iterator for /proc/interrupts so that the next interrupt descriptor can be cached after lookup.

---
 include/linux/irqdesc.h |    2 ++
 kernel/irq/internals.h  |   17 ++++++++++++++++-
 kernel/irq/irqdesc.c    |   21 +++++++++++++--------
 3 files changed, 31 insertions(+), 9 deletions(-)
include/linux/irqdesc.h +2 -0
@@ -70,6 +70,7 @@ struct irq_redirect {
  *			IRQF_NO_SUSPEND set
  * @force_resume_depth:	number of irqactions on a irq descriptor with
  *			IRQF_FORCE_RESUME set
+ * @refcnt:		Reference count mainly for /proc/interrupts
  * @rcu:		rcu head for delayed free
  * @kobj:		kobject used to represent this struct in sysfs
  * @request_mutex:	mutex to protect request/free before locking desc->lock
@@ -119,6 +120,7 @@ struct irq_desc {
 	struct dentry		*debugfs_file;
 	const char		*dev_name;
 #endif
+	rcuref_t		refcnt;
 #ifdef CONFIG_SPARSE_IRQ
 	struct rcu_head		rcu;
 	struct kobject		kobj;
kernel/irq/internals.h +16 -1
@@ -9,6 +9,7 @@
 #include <linux/irqdesc.h>
 #include <linux/kernel_stat.h>
 #include <linux/pm_runtime.h>
+#include <linux/rcuref.h>
 #include <linux/sched/clock.h>
 
 #ifdef CONFIG_SPARSE_IRQ
@@ -101,9 +102,23 @@ extern void unmask_irq(struct irq_desc *
 extern void unmask_threaded_irq(struct irq_desc *desc);
 
 #ifdef CONFIG_SPARSE_IRQ
-static inline void irq_mark_irq(unsigned int irq) { }
+static __always_inline void irq_mark_irq(unsigned int irq) { }
+void irq_desc_free_rcu(struct irq_desc *desc);
+
+static __always_inline bool irq_desc_get_ref(struct irq_desc *desc)
+{
+	return rcuref_get(&desc->refcnt);
+}
+
+static __always_inline void irq_desc_put_ref(struct irq_desc *desc)
+{
+	if (rcuref_put(&desc->refcnt))
+		irq_desc_free_rcu(desc);
+}
 #else
 extern void irq_mark_irq(unsigned int irq);
+static __always_inline bool irq_desc_get_ref(struct irq_desc *desc) { return true; }
+static __always_inline void irq_desc_put_ref(struct irq_desc *desc) { }
 #endif
 
 irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc);
kernel/irq/irqdesc.c +13 -8
@@ -137,6 +137,7 @@ static void desc_set_defaults(unsigned i
 	desc->tot_count = 0;
 	desc->name = NULL;
 	desc->owner = owner;
+	rcuref_init(&desc->refcnt, 1);
 	desc_smp_init(desc, node, affinity);
 }
 
@@ -465,6 +466,17 @@ static void delayed_free_desc(struct rcu
 	kobject_put(&desc->kobj);
 }
 
+void irq_desc_free_rcu(struct irq_desc *desc)
+{
+	/*
+	 * We free the descriptor, masks and stat fields via RCU. That
+	 * allows demultiplex interrupts to do rcu based management of
+	 * the child interrupts.
+	 * This also allows us to use rcu in kstat_irqs_usr().
+	 */
+	call_rcu(&desc->rcu, delayed_free_desc);
+}
+
 static void free_desc(unsigned int irq)
 {
 	struct irq_desc *desc = irq_to_desc(irq);
@@ -483,14 +495,7 @@ static void free_desc(unsigned int irq)
 	 */
 	irq_sysfs_del(desc);
 	delete_irq_desc(irq);
-
-	/*
-	 * We free the descriptor, masks and stat fields via RCU. That
-	 * allows demultiplex interrupts to do rcu based management of
-	 * the child interrupts.
-	 * This also allows us to use rcu in kstat_irqs_usr().
-	 */
-	call_rcu(&desc->rcu, delayed_free_desc);
+	irq_desc_put_ref(desc);
 }
 
 static int alloc_descs(unsigned int start, unsigned int cnt, int node,
#33 lore raw

[tip: irq/core] genirq: Add rcuref count to struct irq_desc

The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     1d9c4745bfb6773712751f5068c23ebad9cd30a0
Gitweb:        https://git.kernel.org/tip/1d9c4745bfb6773712751f5068c23ebad9cd30a0
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Sun, 17 May 2026 22:02:34 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Tue, 26 May 2026 16:21:14 +02:00

genirq: Add rcuref count to struct irq_desc

Prepare for a smarter iterator for /proc/interrupts so that the next interrupt descriptor can be cached after lookup.

---
 include/linux/irqdesc.h |  2 ++
 kernel/irq/internals.h  | 17 ++++++++++++++++-
 kernel/irq/irqdesc.c    | 21 +++++++++++++--------
 3 files changed, 31 insertions(+), 9 deletions(-)
include/linux/irqdesc.h +2 -0
index 1058786..8080db1 100644
@@ -70,6 +70,7 @@ struct irq_redirect {
  *			IRQF_NO_SUSPEND set
  * @force_resume_depth:	number of irqactions on a irq descriptor with
  *			IRQF_FORCE_RESUME set
+ * @refcnt:		Reference count mainly for /proc/interrupts
  * @rcu:		rcu head for delayed free
  * @kobj:		kobject used to represent this struct in sysfs
  * @request_mutex:	mutex to protect request/free before locking desc->lock
@@ -119,6 +120,7 @@ struct irq_desc {
 	struct dentry		*debugfs_file;
 	const char		*dev_name;
 #endif
+	rcuref_t		refcnt;
 #ifdef CONFIG_SPARSE_IRQ
 	struct rcu_head		rcu;
 	struct kobject		kobj;
kernel/irq/internals.h +16 -1
index be702dd..bacbf40 100644
@@ -9,6 +9,7 @@
 #include <linux/irqdesc.h>
 #include <linux/kernel_stat.h>
 #include <linux/pm_runtime.h>
+#include <linux/rcuref.h>
 #include <linux/sched/clock.h>
 
 #ifdef CONFIG_SPARSE_IRQ
@@ -101,9 +102,23 @@ extern void unmask_irq(struct irq_desc *desc);
 extern void unmask_threaded_irq(struct irq_desc *desc);
 
 #ifdef CONFIG_SPARSE_IRQ
-static inline void irq_mark_irq(unsigned int irq) { }
+static __always_inline void irq_mark_irq(unsigned int irq) { }
+void irq_desc_free_rcu(struct irq_desc *desc);
+
+static __always_inline bool irq_desc_get_ref(struct irq_desc *desc)
+{
+	return rcuref_get(&desc->refcnt);
+}
+
+static __always_inline void irq_desc_put_ref(struct irq_desc *desc)
+{
+	if (rcuref_put(&desc->refcnt))
+		irq_desc_free_rcu(desc);
+}
 #else
 extern void irq_mark_irq(unsigned int irq);
+static __always_inline bool irq_desc_get_ref(struct irq_desc *desc) { return true; }
+static __always_inline void irq_desc_put_ref(struct irq_desc *desc) { }
 #endif
 
 irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc);
kernel/irq/irqdesc.c +13 -8
index 90595ee..23f2176 100644
@@ -137,6 +137,7 @@ static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node,
 	desc->tot_count = 0;
 	desc->name = NULL;
 	desc->owner = owner;
+	rcuref_init(&desc->refcnt, 1);
 	desc_smp_init(desc, node, affinity);
 }
 
@@ -465,6 +466,17 @@ static void delayed_free_desc(struct rcu_head *rhp)
 	kobject_put(&desc->kobj);
 }
 
+void irq_desc_free_rcu(struct irq_desc *desc)
+{
+	/*
+	 * We free the descriptor, masks and stat fields via RCU. That
+	 * allows demultiplex interrupts to do rcu based management of
+	 * the child interrupts.
+	 * This also allows us to use rcu in kstat_irqs_usr().
+	 */
+	call_rcu(&desc->rcu, delayed_free_desc);
+}
+
 static void free_desc(unsigned int irq)
 {
 	struct irq_desc *desc = irq_to_desc(irq);
@@ -483,14 +495,7 @@ static void free_desc(unsigned int irq)
 	 */
 	irq_sysfs_del(desc);
 	delete_irq_desc(irq);
-
-	/*
-	 * We free the descriptor, masks and stat fields via RCU. That
-	 * allows demultiplex interrupts to do rcu based management of
-	 * the child interrupts.
-	 * This also allows us to use rcu in kstat_irqs_usr().
-	 */
-	call_rcu(&desc->rcu, delayed_free_desc);
+	irq_desc_put_ref(desc);
 }
 
 static int alloc_descs(unsigned int start, unsigned int cnt, int node,
author maintainer
#34 lore raw

PATCH v6 14/16 genirq: Expose irq_find_desc_at_or_after() in core code

In reply to Thomas Gleixner

From: Thomas Gleixner <[email protected]>

... in preparation for a smarter iterator for /proc/interrupts.

---
V3: Add missing RCU guard and a lockdep assert - Dmitry

---
 kernel/irq/internals.h |    2 ++
 kernel/irq/irqdesc.c   |   15 ++++++++-------
 2 files changed, 10 insertions(+), 7 deletions(-)
kernel/irq/internals.h +2 -0
@@ -155,6 +155,8 @@ void irq_proc_calc_prec(void);
 static inline void irq_proc_calc_prec(void) { }
 #endif
 
+struct irq_desc *irq_find_desc_at_or_after(unsigned int offset);
+
 extern bool irq_can_set_affinity_usr(unsigned int irq);
 
 extern int irq_do_set_affinity(struct irq_data *data,
kernel/irq/irqdesc.c +8 -7
@@ -181,15 +181,12 @@ static int irq_find_free_area(unsigned i
 	return mas.index;
 }
 
-static unsigned int irq_find_at_or_after(unsigned int offset)
+struct irq_desc *irq_find_desc_at_or_after(unsigned int offset)
 {
 	unsigned long index = offset;
-	struct irq_desc *desc;
-
-	guard(rcu)();
-	desc = mt_find(&sparse_irqs, &index, total_nr_irqs);
 
-	return desc ? irq_desc_get_irq(desc) : total_nr_irqs;
+	lockdep_assert_in_rcu_read_lock();
+	return mt_find(&sparse_irqs, &index, total_nr_irqs);
 }
 
 static void irq_insert_desc(unsigned int irq, struct irq_desc *desc)
@@ -934,7 +931,11 @@ EXPORT_SYMBOL_GPL(__irq_alloc_descs);
  */
 unsigned int irq_get_next_irq(unsigned int offset)
 {
-	return irq_find_at_or_after(offset);
+	struct irq_desc *desc;
+
+	guard(rcu)();
+	desc = irq_find_desc_at_or_after(offset);
+	return desc ? irq_desc_get_irq(desc) : total_nr_irqs;
 }
 
 struct irq_desc *__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus,
#35 lore raw

[tip: irq/core] genirq: Expose irq_find_desc_at_or_after() in core code

The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     7603e0575d8a92318bd4695917fce7ec2c5825a1
Gitweb:        https://git.kernel.org/tip/7603e0575d8a92318bd4695917fce7ec2c5825a1
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Sun, 17 May 2026 22:02:39 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Tue, 26 May 2026 16:21:15 +02:00

genirq: Expose irq_find_desc_at_or_after() in core code

... in preparation for a smarter iterator for /proc/interrupts.

---
 kernel/irq/internals.h |  2 ++
 kernel/irq/irqdesc.c   | 15 ++++++++-------
 2 files changed, 10 insertions(+), 7 deletions(-)
kernel/irq/internals.h +2 -0
index bacbf40..37eec03 100644
@@ -155,6 +155,8 @@ void irq_proc_calc_prec(void);
 static inline void irq_proc_calc_prec(void) { }
 #endif
 
+struct irq_desc *irq_find_desc_at_or_after(unsigned int offset);
+
 extern bool irq_can_set_affinity_usr(unsigned int irq);
 
 extern int irq_do_set_affinity(struct irq_data *data,
kernel/irq/irqdesc.c +8 -7
index 23f2176..80ef4e2 100644
@@ -181,15 +181,12 @@ static int irq_find_free_area(unsigned int from, unsigned int cnt)
 	return mas.index;
 }
 
-static unsigned int irq_find_at_or_after(unsigned int offset)
+struct irq_desc *irq_find_desc_at_or_after(unsigned int offset)
 {
 	unsigned long index = offset;
-	struct irq_desc *desc;
-
-	guard(rcu)();
-	desc = mt_find(&sparse_irqs, &index, total_nr_irqs);
 
-	return desc ? irq_desc_get_irq(desc) : total_nr_irqs;
+	lockdep_assert_in_rcu_read_lock();
+	return mt_find(&sparse_irqs, &index, total_nr_irqs);
 }
 
 static void irq_insert_desc(unsigned int irq, struct irq_desc *desc)
@@ -934,7 +931,11 @@ EXPORT_SYMBOL_GPL(__irq_alloc_descs);
  */
 unsigned int irq_get_next_irq(unsigned int offset)
 {
-	return irq_find_at_or_after(offset);
+	struct irq_desc *desc;
+
+	guard(rcu)();
+	desc = irq_find_desc_at_or_after(offset);
+	return desc ? irq_desc_get_irq(desc) : total_nr_irqs;
 }
 
 struct irq_desc *__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus,
author maintainer
#36 lore raw

PATCH v6 15/16 genirq/proc: Runtime size the chip name

In reply to Thomas Gleixner

From: Thomas Gleixner <[email protected]>

The chip name column in the /proc/interrupt output is 8 characters and right aligned, which causes visual clutter due to the fixed length and the alignment. Many interrupt chips, e.g. PCI/MSI[X] have way longer names.

Update the length when a chip is assigned to an interrupt and utilize this information for the output. Align it left so all chip names start at the begin of the column.

Update the GDB script as well and disentangle the header maze so it actually works with all .config combinations.

---
V6: Fix recursive header inclusion - 0day
V3: New patch

---
 kernel/irq/chip.c               |    6 +++--
 kernel/irq/debugfs.h            |   44 ++++++++++++++++++++++++++++++++++++
 kernel/irq/internals.h          |   48 ++--------------------------------------
 kernel/irq/irqdomain.c          |    5 +++-
 kernel/irq/proc.c               |   33 +++++++++++++++++++++++----
 kernel/irq/proc.h               |   13 ++++++++++
 scripts/gdb/linux/interrupts.py |   23 ++++++++++++-------
 7 files changed, 111 insertions(+), 61 deletions(-)
kernel/irq/chip.c +4 -2
@@ -47,9 +47,11 @@ int irq_set_chip(unsigned int irq, const
 		scoped_irqdesc->irq_data.chip = (struct irq_chip *)(chip ?: &no_irq_chip);
 		ret = 0;
 	}
-	/* For !CONFIG_SPARSE_IRQ make the irq show up in allocated_irqs. */
-	if (!ret)
+	if (!ret) {
+		/* For !CONFIG_SPARSE_IRQ make the irq show up in allocated_irqs. */
 		irq_mark_irq(irq);
+		irq_proc_update_chip(chip);
+	}
 	return ret;
 }
 EXPORT_SYMBOL(irq_set_chip);
kernel/irq/debugfs.h new file +44 -0

44 lines not on this page · raw message

kernel/irq/internals.h +3 -45

63 lines not on this page · raw message

kernel/irq/irqdomain.c +4 -1
@@ -20,6 +20,8 @@
 #include <linux/smp.h>
 #include <linux/fs.h>
 
+#include "proc.h"
+
 static LIST_HEAD(irq_domain_list);
 static DEFINE_MUTEX(irq_domain_mutex);
 
@@ -1532,6 +1534,7 @@ int irq_domain_set_hwirq_and_chip(struct
 	irq_data->chip = (struct irq_chip *)(chip ? chip : &no_irq_chip);
 	irq_data->chip_data = chip_data;
 
+	irq_proc_update_chip(chip);
 	return 0;
 }
 EXPORT_SYMBOL_GPL(irq_domain_set_hwirq_and_chip);
@@ -2081,7 +2084,7 @@ static void irq_domain_free_one_irq(stru
 #endif	/* CONFIG_IRQ_DOMAIN_HIERARCHY */
 
 #ifdef CONFIG_GENERIC_IRQ_DEBUGFS
-#include "internals.h"
+#include "debugfs.h"
 
 static struct dentry *domain_dir;
 
kernel/irq/proc.c +28 -5
@@ -456,10 +456,14 @@ int __weak arch_show_interrupts(struct s
 	return 0;
 }
 
+static DEFINE_RAW_SPINLOCK(irq_proc_constraints_lock);
+
 static struct irq_proc_constraints {
 	unsigned int	num_prec;
+	unsigned int	chip_width;
 } irq_proc_constraints __read_mostly = {
 	.num_prec	= 4,
+	.chip_width	= 8,
 };
 
 #ifndef ACTUAL_NR_IRQS
@@ -472,7 +476,23 @@ void irq_proc_calc_prec(void)
 
 	for (prec = 4, n = 10000; prec < 10 && n <= total_nr_irqs; ++prec)
 		n *= 10;
-	WRITE_ONCE(irq_proc_constraints.num_prec, prec);
+
+	guard(raw_spinlock_irqsave)(&irq_proc_constraints_lock);
+	if (prec > irq_proc_constraints.num_prec)
+		WRITE_ONCE(irq_proc_constraints.num_prec, prec);
+}
+
+void irq_proc_update_chip(const struct irq_chip *chip)
+{
+	unsigned int len = chip && chip->name ? strlen(chip->name) : 0;
+
+	if (!len || len <= READ_ONCE(irq_proc_constraints.chip_width))
+		return;
+
+	/* Can be invoked from interrupt disabled contexts */
+	guard(raw_spinlock_irqsave)(&irq_proc_constraints_lock);
+	if (len > irq_proc_constraints.chip_width)
+		WRITE_ONCE(irq_proc_constraints.chip_width, len);
 }
 
 /* Same as seq_put_decimal_ull_width(p, " ", cnt, 10) */
@@ -514,6 +534,7 @@ void irq_proc_emit_counts(struct seq_fil
 
 int show_interrupts(struct seq_file *p, void *v)
 {
+	unsigned int chip_width = READ_ONCE(irq_proc_constraints.chip_width);
 	unsigned int prec = READ_ONCE(irq_proc_constraints.num_prec);
 	int i = *(loff_t *) v, j;
 	struct irqaction *action;
@@ -549,18 +570,20 @@ int show_interrupts(struct seq_file *p,
 		irq_proc_emit_counts(p, &desc->kstat_irqs->cnt);
 	else
 		irq_proc_emit_zero_counts(p, num_online_cpus());
-	seq_putc(p, ' ');
+
+	/* Enforce a visual gap */
+	seq_write(p, "  ", 2);
 
 	guard(raw_spinlock_irq)(&desc->lock);
 	if (desc->irq_data.chip) {
 		if (desc->irq_data.chip->irq_print_chip)
 			desc->irq_data.chip->irq_print_chip(&desc->irq_data, p);
 		else if (desc->irq_data.chip->name)
-			seq_printf(p, "%8s", desc->irq_data.chip->name);
+			seq_printf(p, "%-*s", chip_width, desc->irq_data.chip->name);
 		else
-			seq_printf(p, "%8s", "-");
+			seq_printf(p, "%-*s", chip_width, "-");
 	} else {
-		seq_printf(p, "%8s", "None");
+		seq_printf(p, "%-*s", chip_width, "None");
 	}
 
 	seq_putc(p, ' ');
kernel/irq/proc.h new file +13 -0
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _KERNEL_IRQ_PROC_H
+#define _KERNEL_IRQ_PROC_H
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_GENERIC_IRQ_SHOW)
+void irq_proc_calc_prec(void);
+void irq_proc_update_chip(const struct irq_chip *chip);
+#else
+static inline void irq_proc_calc_prec(void) { }
+static inline void irq_proc_update_chip(const struct irq_chip *chip) { }
+#endif
+
+#endif
scripts/gdb/linux/interrupts.py +15 -8
@@ -20,7 +20,7 @@ irq_desc_type = utils.CachedType("struct
 def irqd_is_level(desc):
     return desc['irq_data']['common']['state_use_accessors'] & constants.LX_IRQD_LEVEL
 
-def show_irq_desc(prec, irq):
+def show_irq_desc(prec, chip_width, irq):
     text = ""
 
     desc = mapletree.mtree_load(gdb.parse_and_eval("&sparse_irqs"), irq)
@@ -58,7 +58,7 @@ irq_desc_type = utils.CachedType("struct
         else:
             name = "-"
 
-    text += "  %-8s" % (name)
+    text += "  %-*s" % (chip_width, name)
 
     if desc['irq_data']['domain']:
         text += "  %*lu" % (prec, desc['irq_data']['hwirq'])
@@ -171,11 +171,18 @@ irq_desc_type = utils.CachedType("struct
 
     def invoke(self, arg, from_tty):
         nr_irqs = gdb.parse_and_eval("total_nr_irqs")
-        prec = 4
-        j = 10000
-        while prec < 10 and j <= nr_irqs:
-            prec += 1
-            j *= 10
+        constr = utils.gdb_eval_or_none('irq_proc_constraints')
+
+        if constr:
+            prec = int(constr['num_prec'])
+            chip_width = int(constr['chip_width'])
+        else:
+            prec = 4
+            j = 10000
+            while prec < 10 and j <= nr_irqs:
+                prec += 1
+                j *= 10
+            chip_width = 8
 
         gdb.write("%*s" % (prec + 8, ""))
         for cpu in cpus.each_online_cpu():
@@ -186,7 +193,7 @@ irq_desc_type = utils.CachedType("struct
             raise gdb.GdbError("Unable to find the sparse IRQ tree, is CONFIG_SPARSE_IRQ enabled?")
 
         for irq in range(nr_irqs):
-            gdb.write(show_irq_desc(prec, irq))
+            gdb.write(show_irq_desc(prec, chip_width, irq))
         gdb.write(arch_show_interrupts(prec))
 
 
#37 lore raw

[tip: irq/core] genirq/proc: Runtime size the chip name

The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     61b51a167c524b65a59b1342e70c2008d514a796
Gitweb:        https://git.kernel.org/tip/61b51a167c524b65a59b1342e70c2008d514a796
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Sun, 17 May 2026 22:02:44 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Tue, 26 May 2026 16:21:15 +02:00

genirq/proc: Runtime size the chip name

The chip name column in the /proc/interrupt output is 8 characters and right aligned, which causes visual clutter due to the fixed length and the alignment. Many interrupt chips, e.g. PCI/MSI[X] have way longer names.

Update the length when a chip is assigned to an interrupt and utilize this information for the output. Align it left so all chip names start at the begin of the column.

Update the GDB script as well and disentangle the header maze so it actually works with all .config combinations.

---
 kernel/irq/chip.c               |  6 ++--
 kernel/irq/debugfs.h            | 44 +++++++++++++++++++++++++++++-
 kernel/irq/internals.h          | 48 ++------------------------------
 kernel/irq/irqdomain.c          |  5 ++-
 kernel/irq/proc.c               | 33 ++++++++++++++++++----
 kernel/irq/proc.h               | 13 +++++++++-
 scripts/gdb/linux/interrupts.py | 23 +++++++++------
 7 files changed, 111 insertions(+), 61 deletions(-)
 create mode 100644 kernel/irq/debugfs.h
 create mode 100644 kernel/irq/proc.h
kernel/irq/chip.c +4 -2
index 2d283d1..9347779 100644
@@ -47,9 +47,11 @@ int irq_set_chip(unsigned int irq, const struct irq_chip *chip)
 		scoped_irqdesc->irq_data.chip = (struct irq_chip *)(chip ?: &no_irq_chip);
 		ret = 0;
 	}
-	/* For !CONFIG_SPARSE_IRQ make the irq show up in allocated_irqs. */
-	if (!ret)
+	if (!ret) {
+		/* For !CONFIG_SPARSE_IRQ make the irq show up in allocated_irqs. */
 		irq_mark_irq(irq);
+		irq_proc_update_chip(chip);
+	}
 	return ret;
 }
 EXPORT_SYMBOL(irq_set_chip);
kernel/irq/debugfs.h new file +44 -0

44 lines not on this page · raw message

kernel/irq/internals.h +3 -45

63 lines not on this page · raw message

kernel/irq/irqdomain.c +4 -1
index cc93abf..f15c9f1 100644
@@ -20,6 +20,8 @@
 #include <linux/smp.h>
 #include <linux/fs.h>
 
+#include "proc.h"
+
 static LIST_HEAD(irq_domain_list);
 static DEFINE_MUTEX(irq_domain_mutex);
 
@@ -1532,6 +1534,7 @@ int irq_domain_set_hwirq_and_chip(struct irq_domain *domain, unsigned int virq,
 	irq_data->chip = (struct irq_chip *)(chip ? chip : &no_irq_chip);
 	irq_data->chip_data = chip_data;
 
+	irq_proc_update_chip(chip);
 	return 0;
 }
 EXPORT_SYMBOL_GPL(irq_domain_set_hwirq_and_chip);
@@ -2081,7 +2084,7 @@ static void irq_domain_free_one_irq(struct irq_domain *domain, unsigned int virq
 #endif	/* CONFIG_IRQ_DOMAIN_HIERARCHY */
 
 #ifdef CONFIG_GENERIC_IRQ_DEBUGFS
-#include "internals.h"
+#include "debugfs.h"
 
 static struct dentry *domain_dir;
 
kernel/irq/proc.c +28 -5
index 1cb47a8..9a96800 100644
@@ -457,10 +457,14 @@ int __weak arch_show_interrupts(struct seq_file *p, int prec)
 	return 0;
 }
 
+static DEFINE_RAW_SPINLOCK(irq_proc_constraints_lock);
+
 static struct irq_proc_constraints {
 	unsigned int	num_prec;
+	unsigned int	chip_width;
 } irq_proc_constraints __read_mostly = {
 	.num_prec	= 4,
+	.chip_width	= 8,
 };
 
 #ifndef ACTUAL_NR_IRQS
@@ -473,7 +477,23 @@ void irq_proc_calc_prec(void)
 
 	for (prec = 4, n = 10000; prec < 10 && n <= total_nr_irqs; ++prec)
 		n *= 10;
-	WRITE_ONCE(irq_proc_constraints.num_prec, prec);
+
+	guard(raw_spinlock_irqsave)(&irq_proc_constraints_lock);
+	if (prec > irq_proc_constraints.num_prec)
+		WRITE_ONCE(irq_proc_constraints.num_prec, prec);
+}
+
+void irq_proc_update_chip(const struct irq_chip *chip)
+{
+	unsigned int len = chip && chip->name ? strlen(chip->name) : 0;
+
+	if (!len || len <= READ_ONCE(irq_proc_constraints.chip_width))
+		return;
+
+	/* Can be invoked from interrupt disabled contexts */
+	guard(raw_spinlock_irqsave)(&irq_proc_constraints_lock);
+	if (len > irq_proc_constraints.chip_width)
+		WRITE_ONCE(irq_proc_constraints.chip_width, len);
 }
 
 /* Same as seq_put_decimal_ull_width(p, " ", cnt, 10) */
@@ -515,6 +535,7 @@ void irq_proc_emit_counts(struct seq_file *p, unsigned int __percpu *cnts)
 
 int show_interrupts(struct seq_file *p, void *v)
 {
+	unsigned int chip_width = READ_ONCE(irq_proc_constraints.chip_width);
 	unsigned int prec = READ_ONCE(irq_proc_constraints.num_prec);
 	int i = *(loff_t *) v, j;
 	struct irqaction *action;
@@ -550,18 +571,20 @@ int show_interrupts(struct seq_file *p, void *v)
 		irq_proc_emit_counts(p, &desc->kstat_irqs->cnt);
 	else
 		irq_proc_emit_zero_counts(p, num_online_cpus());
-	seq_putc(p, ' ');
+
+	/* Enforce a visual gap */
+	seq_write(p, "  ", 2);
 
 	guard(raw_spinlock_irq)(&desc->lock);
 	if (desc->irq_data.chip) {
 		if (desc->irq_data.chip->irq_print_chip)
 			desc->irq_data.chip->irq_print_chip(&desc->irq_data, p);
 		else if (desc->irq_data.chip->name)
-			seq_printf(p, "%8s", desc->irq_data.chip->name);
+			seq_printf(p, "%-*s", chip_width, desc->irq_data.chip->name);
 		else
-			seq_printf(p, "%8s", "-");
+			seq_printf(p, "%-*s", chip_width, "-");
 	} else {
-		seq_printf(p, "%8s", "None");
+		seq_printf(p, "%-*s", chip_width, "None");
 	}
 
 	seq_putc(p, ' ');
kernel/irq/proc.h new file +13 -0
new file mode 100644
index 0000000..0631d57
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _KERNEL_IRQ_PROC_H
+#define _KERNEL_IRQ_PROC_H
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_GENERIC_IRQ_SHOW)
+void irq_proc_calc_prec(void);
+void irq_proc_update_chip(const struct irq_chip *chip);
+#else
+static inline void irq_proc_calc_prec(void) { }
+static inline void irq_proc_update_chip(const struct irq_chip *chip) { }
+#endif
+
+#endif
scripts/gdb/linux/interrupts.py +15 -8
index e967343..a68ae91 100644
@@ -20,7 +20,7 @@ def irq_desc_is_chained(desc):
 def irqd_is_level(desc):
     return desc['irq_data']['common']['state_use_accessors'] & constants.LX_IRQD_LEVEL
 
-def show_irq_desc(prec, irq):
+def show_irq_desc(prec, chip_width, irq):
     text = ""
 
     desc = mapletree.mtree_load(gdb.parse_and_eval("&sparse_irqs"), irq)
@@ -58,7 +58,7 @@ def show_irq_desc(prec, irq):
         else:
             name = "-"
 
-    text += "  %-8s" % (name)
+    text += "  %-*s" % (chip_width, name)
 
     if desc['irq_data']['domain']:
         text += "  %*lu" % (prec, desc['irq_data']['hwirq'])
@@ -171,11 +171,18 @@ class LxInterruptList(gdb.Command):
 
     def invoke(self, arg, from_tty):
         nr_irqs = gdb.parse_and_eval("total_nr_irqs")
-        prec = 4
-        j = 10000
-        while prec < 10 and j <= nr_irqs:
-            prec += 1
-            j *= 10
+        constr = utils.gdb_eval_or_none('irq_proc_constraints')
+
+        if constr:
+            prec = int(constr['num_prec'])
+            chip_width = int(constr['chip_width'])
+        else:
+            prec = 4
+            j = 10000
+            while prec < 10 and j <= nr_irqs:
+                prec += 1
+                j *= 10
+            chip_width = 8
 
         gdb.write("%*s" % (prec + 8, ""))
         for cpu in cpus.each_online_cpu():
@@ -186,7 +193,7 @@ class LxInterruptList(gdb.Command):
             raise gdb.GdbError("Unable to find the sparse IRQ tree, is CONFIG_SPARSE_IRQ enabled?")
 
         for irq in range(nr_irqs):
-            gdb.write(show_irq_desc(prec, irq))
+            gdb.write(show_irq_desc(prec, chip_width, irq))
         gdb.write(arch_show_interrupts(prec))
 
 
author maintainer
#38 lore raw

PATCH v6 16/16 genirq/proc: Speed up /proc/interrupts iteration

In reply to Thomas Gleixner

From: Thomas Gleixner <[email protected]>

Reading /proc/interrupts iterates over the interrupt number space one by one and looks up the descriptors one by one. That's just a waste of time.

When CONFIG_GENERIC_IRQ_SHOW is enabled this can utilize the maple tree and cache the descriptor pointer efficiently for the sequence file operations.

Implement a CONFIG_GENERIC_IRQ_SHOW specific version in the core code and leave the fs/proc/ variant for the legacy architectures which ignore generic code.

This reduces the time wasted for looking up the next record significantly.

---
V5: Reorder the header print - Dmitry

---
 fs/proc/Makefile  |    4 +
 kernel/irq/proc.c |  118 ++++++++++++++++++++++++++++++++++++++++++------------
 2 files changed, 95 insertions(+), 27 deletions(-)
fs/proc/Makefile +3 -1
@@ -16,7 +16,9 @@ proc-y	+= cmdline.o
 proc-y	+= consoles.o
 proc-y	+= cpuinfo.o
 proc-y	+= devices.o
-proc-y	+= interrupts.o
+ifneq ($(CONFIG_GENERIC_IRQ_SHOW),y)
+proc-y += interrupts.o
+endif
 proc-y	+= loadavg.o
 proc-y	+= meminfo.o
 proc-y	+= stat.o
kernel/irq/proc.c +92 -26

160 lines not on this page · raw message

#39 lore raw

[tip: irq/core] genirq/proc: Speed up /proc/interrupts iteration

The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     171cc0d9eed1cad5de7ce6a212efbeda390edb0f
Gitweb:        https://git.kernel.org/tip/171cc0d9eed1cad5de7ce6a212efbeda390edb0f
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Sun, 17 May 2026 22:02:49 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Tue, 26 May 2026 16:21:15 +02:00

genirq/proc: Speed up /proc/interrupts iteration

Reading /proc/interrupts iterates over the interrupt number space one by one and looks up the descriptors one by one. That's just a waste of time.

When CONFIG_GENERIC_IRQ_SHOW is enabled this can utilize the maple tree and cache the descriptor pointer efficiently for the sequence file operations.

Implement a CONFIG_GENERIC_IRQ_SHOW specific version in the core code and leave the fs/proc/ variant for the legacy architectures which ignore generic code.

This reduces the time wasted for looking up the next record significantly.

---
 fs/proc/Makefile  |   4 +-
 kernel/irq/proc.c | 116 +++++++++++++++++++++++++++++++++++----------
 2 files changed, 94 insertions(+), 26 deletions(-)
fs/proc/Makefile +3 -1
index 7b4db9c..8bc615f 100644
@@ -16,7 +16,9 @@ proc-y	+= cmdline.o
 proc-y	+= consoles.o
 proc-y	+= cpuinfo.o
 proc-y	+= devices.o
-proc-y	+= interrupts.o
+ifneq ($(CONFIG_GENERIC_IRQ_SHOW),y)
+proc-y += interrupts.o
+endif
 proc-y	+= loadavg.o
 proc-y	+= meminfo.o
 proc-y	+= stat.o
kernel/irq/proc.c +91 -25

159 lines not on this page · raw message

Tested-by Michael Kelley on the v6 series
#40 lore raw
In reply to Thomas Gleixner

From: Thomas Gleixner <[email protected]> Sent: Sunday, May 17, 2026 1:01 PM

Quoted 7 lines

This is a follow up to v5 which can be found here:

https://lore.kernel.org/[email protected]

The v1 cover letter contains a full analysis, explanation and numbers:

Quoted 7 lines

https://lore.kernel.org/[email protected]

TLDR:

- The performance of reading of /proc/interrupts has been improved piecewise over the years, but most of the low hanging fruit has been left on the table.

As I did previously with v2 and v5 of the patch series, I tested in Hyper-V guests on x86/x64 and arm64. Did basic smoke tests of taking a CPU offline, and removing a PCI device along with its IRQs, then adding them back again. Everything looks good. The improved alignment looks 100% good. The alignment nits I called out against v5 are fixed, and I did not find anything new to be picky about.

I did not do anything with the new binary interface or the gdb Python script.

Quoted 20 lines

Changes vs. V5:

- Rebased against v7.1-rc2

- Addressed some formatting/alignment details - Radu, Michael

- Fixed some 0-day fallout vs. various Kconfig combinations

- Picked up tags where appropriate

Delta patch against v5 is below.

The series applies on top of v7.1-rc2 and is also available via git:

git://git.kernel.org/pub/scm/linux/kernel/git/tglx/devel.git irq-proc-v6

Thanks,

tglx

#41 lore raw
In reply to Thomas Gleixner

Hi Thomas.

Quoted 6 lines · Thomas Gleixner

This is a follow up to v5 which can be found here:

   https://lore.kernel.org/[email protected]

The v1 cover letter contains a full analysis, explanation and numbers:

Quoted 8 lines
   https://lore.kernel.org/[email protected]

TLDR:

- The performance of reading of /proc/interrupts has been improved piecewise over the years, but most of the low hanging fruit has been left on the table.

Ran this on powerVM box with 240 CPUs.

Ran perf stat -r 1000 cat /proc/interrupts > tmp.txt and Observed minimal improvement with series.

Base:
Performance counter stats for 'cat /proc/interrupts' (1000 runs):

              0.32 msec task-clock:HG                    #    0.617 CPUs utilized               ( +-  0.17% )
                 0      context-switches:HG              #    0.000 /sec
                 0      cpu-migrations:HG                #    0.000 /sec
                44      page-faults:HG                   #  136.122 K/sec                       ( +-  0.03% )
         1,313,263      cycles:HG                        #    4.063 GHz                         ( +-  0.17% )
         2,172,511      instructions:HG                  #    1.65  insn per cycle              ( +-  0.05% )
           371,171      branches:HG                      #    1.148 G/sec                       ( +-  0.05% )
             4,918      branch-misses:HG                 #    1.32% of all branches             ( +-  0.35% )
       0.000523661 +- 0.000000914 seconds time elapsed  ( +-  0.17% )

v6 series:

Performance counter stats for 'cat /proc/interrupts' (1000 runs):

              0.30 msec task-clock:HG                    #    0.591 CPUs utilized               ( +-  0.25% )
                 0      context-switches:HG              #    0.000 /sec
                 0      cpu-migrations:HG                #    0.000 /sec
                44      page-faults:HG                   #  145.802 K/sec                       ( +-  0.03% )
         1,224,666      cycles:HG                        #    4.058 GHz                         ( +-  0.25% )
         1,667,435      instructions:HG                  #    1.36  insn per cycle              ( +-  0.08% )
           277,534      branches:HG                      #  919.660 M/sec                       ( +-  0.09% )
             5,066      branch-misses:HG                 #    1.83% of all branches             ( +-  0.45% )
        0.00051099 +- 0.00000110 seconds time elapsed  ( +-  0.21% )       << 3-4% improvement

Looking at powerpc arch_show_interrupts,
It could use the similar set of optimizations.
- move to array based
- use irq_proc_emit_counts
- some interrupts such as machine check, is hardly set. set skip_vector.

Copilot suggested below diff to quickly try irq_proc_emit_counts integration. It showed little gains compared to v6. So it maybe worth fixing that in the right way. (similar to x86 stuff you have done)

Performance counter stats for 'cat /proc/interrupts' (1000 runs):

              0.29 msec task-clock:HG                    #    0.586 CPUs utilized               ( +-  0.22% )
                 0      context-switches:HG              #    0.000 /sec
                 0      cpu-migrations:HG                #    0.000 /sec
                44      page-faults:HG                   #  153.067 K/sec                       ( +-  0.03% )
         1,166,567      cycles:HG                        #    4.058 GHz                         ( +-  0.22% )
         1,475,365      instructions:HG                  #    1.26  insn per cycle              ( +-  0.09% )
           249,051      branches:HG                      #  866.397 M/sec                       ( +-  0.10% )
             5,104      branch-misses:HG                 #    2.05% of all branches             ( +-  0.33% )
       0.000490211 +- 0.000000992 seconds time elapsed  ( +-  0.20% )   <<< 3-4% improvements.
arch/powerpc/kernel/irq.c +21 -18
index a0e8b998c9b5..19c9f28c39d3 100644
@@ -83,6 +83,18 @@ u32 tau_interrupts(unsigned long cpu);
 #endif
 #endif /* CONFIG_PPC32 */
 
+
+/*
+ * Return a percpu pointer to a given unsigned int member of irq_stat.
+ */
+static __always_inline unsigned int __percpu *ppc_irq_stat_member(size_t off)
+{
+    return (unsigned int __percpu *)((char __percpu *)&irq_stat + off);
+}
+
+#define PPC_IRQ_STAT_PCPU(member) \
+    ppc_irq_stat_member(offsetof(irq_cpustat_t, member))
+
 int arch_show_interrupts(struct seq_file *p, int prec)
 {
        int j;
@@ -97,33 +109,27 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 #endif /* CONFIG_PPC32 && CONFIG_TAU_INT */
 
        seq_printf(p, "%*s:", prec, "LOC");
-       for_each_online_cpu(j)
-               seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).timer_irqs_event, 10);
+       irq_proc_emit_counts(p, PPC_IRQ_STAT_PCPU(timer_irqs_event));
         seq_printf(p, "  Local timer interrupts for timer event device\n");
 
        seq_printf(p, "%*s:", prec, "BCT");
-       for_each_online_cpu(j)
-               seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).broadcast_irqs_event, 10);
+       irq_proc_emit_counts(p, PPC_IRQ_STAT_PCPU(broadcast_irqs_event));
        seq_printf(p, "  Broadcast timer interrupts for timer event device\n");
 
        seq_printf(p, "%*s:", prec, "LOC");
-       for_each_online_cpu(j)
-               seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).timer_irqs_others, 10);
+       irq_proc_emit_counts(p, PPC_IRQ_STAT_PCPU(timer_irqs_others));
         seq_printf(p, "  Local timer interrupts for others\n");
 
        seq_printf(p, "%*s:", prec, "SPU");
-       for_each_online_cpu(j)
-               seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).spurious_irqs, 10);
+       irq_proc_emit_counts(p, PPC_IRQ_STAT_PCPU(spurious_irqs));
        seq_printf(p, "  Spurious interrupts\n");
 
        seq_printf(p, "%*s:", prec, "PMI");
-       for_each_online_cpu(j)
-               seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).pmu_irqs, 10);
+       irq_proc_emit_counts(p, PPC_IRQ_STAT_PCPU(pmu_irqs));
        seq_printf(p, "  Performance monitoring interrupts\n");
 
        seq_printf(p, "%*s:", prec, "MCE");
-       for_each_online_cpu(j)
-               seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).mce_exceptions, 10);
+       irq_proc_emit_counts(p, PPC_IRQ_STAT_PCPU(mce_exceptions));
        seq_printf(p, "  Machine check exceptions\n");
 
 #ifdef CONFIG_PPC_BOOK3S_64
@@ -136,22 +142,19 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 #endif
 
        seq_printf(p, "%*s:", prec, "NMI");
-       for_each_online_cpu(j)
-               seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).sreset_irqs, 10);
+       irq_proc_emit_counts(p, PPC_IRQ_STAT_PCPU(sreset_irqs));
        seq_printf(p, "  System Reset interrupts\n");
 
 #ifdef CONFIG_PPC_WATCHDOG
        seq_printf(p, "%*s:", prec, "WDG");
-       for_each_online_cpu(j)
-               seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).soft_nmi_irqs, 10);
+       irq_proc_emit_counts(p, PPC_IRQ_STAT_PCPU(soft_nmi_irqs));
        seq_printf(p, "  Watchdog soft-NMI interrupts\n");
 #endif
 
 #ifdef CONFIG_PPC_DOORBELL
        if (cpu_has_feature(CPU_FTR_DBELL)) {
                seq_printf(p, "%*s:", prec, "DBL");
-               for_each_online_cpu(j)
-                       seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).doorbell_irqs, 10);
+               irq_proc_emit_counts(p, PPC_IRQ_STAT_PCPU(doorbell_irqs));
                seq_printf(p, "  Doorbell interrupts\n");
        }
 #endif
author maintainer
#42 lore raw

Shrikanth!

Quoted 4 lines · Shrikanth Hegde

On 5/18/26 1:31 AM, Thomas Gleixner wrote:

Ran perf stat -r 1000 cat /proc/interrupts > tmp.txt and Observed minimal improvement with series.

Can you redirect it to /dev/null instead to take the file operations out of the picture?

Quoted 7 lines

Base:

          1,313,263      cycles:HG                        #    4.063 GHz                         ( +-  0.17% )
          2,172,511      instructions:HG                  #    1.65  insn per cycle              ( +-  0.05% )

v6 series:

          1,224,666      cycles:HG                        #    4.058 GHz                         ( +-  0.25% )
          1,667,435      instructions:HG                  #    1.36  insn per cycle              ( +-  0.08% )

Interesting. The number of instructions goes down by 20+%, but at the same time IPC drops too.

Quoted 22 lines

Looking at powerpc arch_show_interrupts,
It could use the similar set of optimizations.
- move to array based
- use irq_proc_emit_counts
- some interrupts such as machine check, is hardly set. set skip_vector.

Copilot suggested below diff to quickly try irq_proc_emit_counts integration. It showed little gains compared to v6. So it maybe worth fixing that in the right way. (similar to x86 stuff you have done)

Performance counter stats for 'cat /proc/interrupts' (1000 runs):

               0.29 msec task-clock:HG                    #    0.586 CPUs utilized               ( +-  0.22% )
                  0      context-switches:HG              #    0.000 /sec
                  0      cpu-migrations:HG                #    0.000 /sec
                 44      page-faults:HG                   #  153.067 K/sec                       ( +-  0.03% )
          1,166,567      cycles:HG                        #    4.058 GHz                         ( +-  0.22% )
          1,475,365      instructions:HG                  #    1.26  insn per cycle              ( +-  0.09% )
            249,051      branches:HG                      #  866.397 M/sec                       ( +-  0.10% )
              5,104      branch-misses:HG                 #    2.05% of all branches             ( +-  0.33% )
Quoted 1 line
        0.000490211 +- 0.000000992 seconds time elapsed  ( +-  0.20% )   <<< 3-4% improvements.

Again IPC drops ....

#43 lore raw

Hi Thomas.

Quoted 11 lines · Thomas Gleixner

Shrikanth!

Quoted 4 lines · Shrikanth Hegde

On 5/18/26 1:31 AM, Thomas Gleixner wrote:

Ran perf stat -r 1000 cat /proc/interrupts > tmp.txt and Observed minimal improvement with series.

Can you redirect it to /dev/null instead to take the file operations out of the picture?

Yes. Did "perf stat -r 1000 cat /proc/interrupts > /dev/null". It shows better improvement with the series compared to file write.

Quoted 11 lines
Quoted 7 lines

Base:

           1,313,263      cycles:HG                        #    4.063 GHz                         ( +-  0.17% )
           2,172,511      instructions:HG                  #    1.65  insn per cycle              ( +-  0.05% )

v6 series:

           1,224,666      cycles:HG                        #    4.058 GHz                         ( +-  0.25% )
           1,667,435      instructions:HG                  #    1.36  insn per cycle              ( +-  0.08% )

Interesting. The number of instructions goes down by 20+%, but at the same time IPC drops too.

base:

Performance counter stats for 'cat /proc/interrupts' (1000 runs):

              0.32 msec task-clock:HG                    #    0.615 CPUs utilized               ( +-  0.17% )
                 0      context-switches:HG              #    0.000 /sec
                 0      cpu-migrations:HG                #    0.000 /sec
                44      page-faults:HG                   #  136.347 K/sec                       ( +-  0.03% )
         1,310,621      cycles:HG                        #    4.061 GHz                         ( +-  0.17% )
         2,182,042      instructions:HG                  #    1.66  insn per cycle              ( +-  0.04% )
           372,189      branches:HG                      #    1.153 G/sec                       ( +-  0.04% )
             4,710      branch-misses:HG                 #    1.27% of all branches             ( +-  0.33% )
       0.000525061 +- 0.000000889 seconds time elapsed  ( +-  0.17% )

v6:
Performance counter stats for 'cat /proc/interrupts' (1000 runs):

              0.28 msec task-clock:HG                    #    0.577 CPUs utilized               ( +-  0.25% )
                 0      context-switches:HG              #    0.000 /sec
                 0      cpu-migrations:HG                #    0.000 /sec
                44      page-faults:HG                   #  155.906 K/sec                       ( +-  0.03% )
         1,144,964      cycles:HG                        #    4.057 GHz                         ( +-  0.24% )
         1,628,375      instructions:HG                  #    1.42  insn per cycle              ( +-  0.07% )
           271,934      branches:HG                      #  963.546 M/sec                       ( +-  0.07% )
             4,683      branch-misses:HG                 #    1.72% of all branches             ( +-  0.49% )
        0.00048895 +- 0.00000114 seconds time elapsed  ( +-  0.23% )   << 7-8% improvement.

v6+ ppc_hack

Performance counter stats for 'cat /proc/interrupts' (1000 runs):

              0.27 msec task-clock:HG                    #    0.582 CPUs utilized               ( +-  0.15% )
                 0      context-switches:HG              #    0.000 /sec
                 0      cpu-migrations:HG                #    0.000 /sec
                44      page-faults:HG                   #  160.232 K/sec                       ( +-  0.03% )
         1,113,983      cycles:HG                        #    4.057 GHz                         ( +-  0.15% )
         1,501,868      instructions:HG                  #    1.35  insn per cycle              ( +-  0.07% )
           251,432      branches:HG                      #  915.627 M/sec                       ( +-  0.06% )
             4,528      branch-misses:HG                 #    1.80% of all branches             ( +-  0.18% )
       0.000472057 +- 0.000000668 seconds time elapsed  ( +-  0.14% )  << only slightly better.
Quoted 22 lines
Quoted 22 lines

Looking at powerpc arch_show_interrupts,
It could use the similar set of optimizations.
- move to array based
- use irq_proc_emit_counts
- some interrupts such as machine check, is hardly set. set skip_vector.

Copilot suggested below diff to quickly try irq_proc_emit_counts integration. It showed little gains compared to v6. So it maybe worth fixing that in the right way. (similar to x86 stuff you have done)

Performance counter stats for 'cat /proc/interrupts' (1000 runs):

                0.29 msec task-clock:HG                    #    0.586 CPUs utilized               ( +-  0.22% )
                   0      context-switches:HG              #    0.000 /sec
                   0      cpu-migrations:HG                #    0.000 /sec
                  44      page-faults:HG                   #  153.067 K/sec                       ( +-  0.03% )
           1,166,567      cycles:HG                        #    4.058 GHz                         ( +-  0.22% )
           1,475,365      instructions:HG                  #    1.26  insn per cycle              ( +-  0.09% )
             249,051      branches:HG                      #  866.397 M/sec                       ( +-  0.10% )
               5,104      branch-misses:HG                 #    2.05% of all branches             ( +-  0.33% )
Quoted 3 lines
Quoted 1 line
         0.000490211 +- 0.000000992 seconds time elapsed  ( +-  0.20% )   <<< 3-4% improvements.

Again IPC drops ....

Yes. IPC dropping is consistent. I see the same trend in (PATCH 1/16) in the series. Copying that snippet below.

Before:
8,932,242 instructions # 1.66 insn per cycle ( +- 0.34% )
After:
7,020,982 instructions # 1.30 insn per cycle ( +- 0.52% )

So it might be common pattern across archs. Maybe perf stat subsystem is slow enough it doesn't shows the aboslute benefit.

In addition, I ran "perf stat -a -r 1000 cat /proc/interrupts > /dev/null" It is now 10x slower. IPC is same with series And improvement vanishes. So heavier the infra testing it, gains are getting minimal i guess. But i don't see any regression.

As you said in the cover-letter, the micro loops you ran maybe the best way to evaluate it. If you have the code in shareable form, I can give it a try.

Other than that, code improvement looks good to me.

author maintainer
#44 lore raw
Quoted 1 line
Quoted 1 line

Shrikanth!

Quoted 6 lines · Shrikanth Hegde
Quoted 2 lines · Thomas Gleixner

Can you redirect it to /dev/null instead to take the file operations out of the picture?

Yes. Did "perf stat -r 1000 cat /proc/interrupts > /dev/null". It shows better improvement with the series compared to file write.

Unsurprisingly :)

Quoted 7 lines
Quoted 3 lines
Quoted 1 line
         0.000490211 +- 0.000000992 seconds time elapsed  ( +-  0.20% )   <<< 3-4% improvements.

Again IPC drops ....

Yes. IPC dropping is consistent. I see the same trend in (PATCH 1/16) in the series. Copying that snippet below.

Quoted 7 lines

Before:
8,932,242 instructions # 1.66 insn per cycle ( +- 0.34% )
After:
7,020,982 instructions # 1.30 insn per cycle ( +- 0.52% )

So it might be common pattern across archs. Maybe perf stat subsystem is slow enough it doesn't shows the aboslute benefit.

The problem is that the overhead of starting and tearing down 'cat' is accounted as well. That's constant, obviously.

But for the use cases like irqbalanced or similar things, there is no startup/teardown cost involved. The process is up and running and they care about the actual read performance.

It's clearly to observe by comparing the perf data with the read loop timing data:

Base line v6
Perf 3072.21 us 1564.40 us
Loop 1310.36 us 209.90 us

It doesn't add up completely, but the trend is there. And you can trick perf to reveal the startup/teardown overhead it by comparing:

perf stat -r 1000 head -q -c -0 /proc/interrupts >/dev/null perf stat -r 1000 head -q -c 0 /proc/interrupts >/dev/null

Quoted 3 lines

In addition, I ran "perf stat -a -r 1000 cat /proc/interrupts > /dev/null" It is now 10x slower. IPC is same with series And improvement vanishes. So heavier the infra testing it, gains are getting minimal i guess.

As often :)

Quoted 4 lines

But i don't see any regression.

As you said in the cover-letter, the micro loops you ran maybe the best way to evaluate it. If you have the code in shareable form, I can give it a try.

See below. I thought I would come around some day to actually use perf directly in the test program, but that never happened due to -ENOTIME.

Thanks,

tglx
---
#include <fcntl.h>
#include <math.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>

static char buf[1024*1024];

#define NSECS_PER_SEC (1000L * 1000L * 1000L)

#define LOOPS 1000

static float td[LOOPS];

int main(int argc, char *argv[])

{
	int fd = open("/proc/interrupts", O_RDONLY);
	long tsum = 0, rs = 0;

	for (int i = 0; i < LOOPS; i++) {
		long r;

		do {
			r = read(fd, buf, sizeof(buf));
		} while (r);
		lseek(fd, 0, 0);
	}

	for (int i = 0; i < LOOPS; i++) {
		struct timespec t0, t1;
		unsigned long delta;
		long r;

		clock_gettime(CLOCK_MONOTONIC, &t0);
		do {
			r = read(fd, buf, sizeof(buf));
			rs += r;
		} while (r);
		clock_gettime(CLOCK_MONOTONIC, &t1);

		delta = t1.tv_nsec + t1.tv_sec * NSECS_PER_SEC;
		delta -= t0.tv_nsec + t0.tv_sec * NSECS_PER_SEC;
		tsum += delta;
		td[i] = delta * 1.0;

		lseek(fd, 0, 0);
	}

	float mean = tsum / LOOPS;
	float calc = 0;

	for (int i = 0; i < LOOPS; i++) {
		float tmp = td[i] - mean;

		calc += tmp * tmp;
	}

	calc /= LOOPS;

	float std = sqrt(calc * 1.0);

	printf("%lu %lu %5.3f\n", tsum / LOOPS, rs / LOOPS, (std / mean) * 100.0);
	return 0;
}
reviewer Tested-by
#45 lore raw
Quoted 27 lines · Thomas Gleixner
Quoted 1 line
Quoted 1 line

Shrikanth!

Quoted 6 lines · Shrikanth Hegde
Quoted 2 lines · Thomas Gleixner

Can you redirect it to /dev/null instead to take the file operations out of the picture?

Yes. Did "perf stat -r 1000 cat /proc/interrupts > /dev/null". It shows better improvement with the series compared to file write.

Unsurprisingly :)

Quoted 7 lines
Quoted 3 lines
Quoted 1 line
          0.000490211 +- 0.000000992 seconds time elapsed  ( +-  0.20% )   <<< 3-4% improvements.

Again IPC drops ....

Yes. IPC dropping is consistent. I see the same trend in (PATCH 1/16) in the series. Copying that snippet below.

Quoted 7 lines

Before:

   8,932,242      instructions      #    1.66  insn per cycle  ( +-  0.34% )
After:
   7,020,982      instructions      #    1.30  insn per cycle  ( +-  0.52% )

So it might be common pattern across archs. Maybe perf stat subsystem is slow enough it doesn't shows the aboslute benefit.

Quoted 7 lines

The problem is that the overhead of starting and tearing down 'cat' is accounted as well. That's constant, obviously.

But for the use cases like irqbalanced or similar things, there is no startup/teardown cost involved. The process is up and running and they care about the actual read performance.

true.

Quoted 7 lines

It's clearly to observe by comparing the perf data with the read loop timing data:

Base line v6
Perf 3072.21 us 1564.40 us
Loop 1310.36 us 209.90 us

Quoted 6 lines

It doesn't add up completely, but the trend is there. And you can trick perf to reveal the startup/teardown overhead it by comparing:

perf stat -r 1000 head -q -c -0 /proc/interrupts >/dev/null perf stat -r 1000 head -q -c 0 /proc/interrupts >/dev/null

Tried it, but doesn't affect much.

Quoted 76 lines
Quoted 3 lines

In addition, I ran "perf stat -a -r 1000 cat /proc/interrupts > /dev/null" It is now 10x slower. IPC is same with series And improvement vanishes. So heavier the infra testing it, gains are getting minimal i guess.

As often :)

Quoted 4 lines

But i don't see any regression.

As you said in the cover-letter, the micro loops you ran maybe the best way to evaluate it. If you have the code in shareable form, I can give it a try.

See below. I thought I would come around some day to actually use perf directly in the test program, but that never happened due to -ENOTIME.

Thanks,

tglx
---
#include <fcntl.h>
#include <math.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>

static char buf[1024*1024];

#define NSECS_PER_SEC (1000L * 1000L * 1000L)

#define LOOPS 1000

static float td[LOOPS];

int main(int argc, char *argv[])

{
	int fd = open("/proc/interrupts", O_RDONLY);
	long tsum = 0, rs = 0;

	for (int i = 0; i < LOOPS; i++) {
		long r;

		do {
			r = read(fd, buf, sizeof(buf));
		} while (r);
		lseek(fd, 0, 0);
	}

	for (int i = 0; i < LOOPS; i++) {
		struct timespec t0, t1;
		unsigned long delta;
		long r;

		clock_gettime(CLOCK_MONOTONIC, &t0);
		do {
			r = read(fd, buf, sizeof(buf));
			rs += r;
		} while (r);
		clock_gettime(CLOCK_MONOTONIC, &t1);

		delta = t1.tv_nsec + t1.tv_sec * NSECS_PER_SEC;
		delta -= t0.tv_nsec + t0.tv_sec * NSECS_PER_SEC;
		tsum += delta;
		td[i] = delta * 1.0;

		lseek(fd, 0, 0);
	}

	float mean = tsum / LOOPS;
	float calc = 0;

	for (int i = 0; i < LOOPS; i++) {
		float tmp = td[i] - mean;

		calc += tmp * tmp;
	}
Quoted 7 lines
	calc /= LOOPS;

	float std = sqrt(calc * 1.0);

	printf("%lu %lu %5.3f\n", tsum / LOOPS, rs / LOOPS, (std / mean) * 100.0);
	return 0;
}

This shows real benefits indeed.

base v6 v6+ppc_hack
101us 65us 57us

So doing a proper powerpc fix indeed would make sense. I think it is going to be similar. Let me go and read your series again.

For the genirq bits of the series, consider the tag if applicable

author maintainer
#46 lore raw
Quoted 8 lines · Shrikanth Hegde
Quoted 6 lines · Thomas Gleixner

It doesn't add up completely, but the trend is there. And you can trick perf to reveal the startup/teardown overhead it by comparing:

perf stat -r 1000 head -q -c -0 /proc/interrupts >/dev/null perf stat -r 1000 head -q -c 0 /proc/interrupts >/dev/null

Tried it, but doesn't affect much.

Weird. On a v6 patched kernel:

$ perf stat -r 1000 head -q -c -0 /proc/interrupts >/dev/null
        0.00130456 +- 0.00000306 seconds time elapsed  ( +-  0.23% )
$ perf stat -r 1000 head -q -c 0 /proc/interrupts >/dev/null
        0.00108667 +- 0.00000367 seconds time elapsed  ( +-  0.34% )

The -c -0 one reads the full output while the -c 0 one just opens the file and closes it again, which means the actual read takes ~0.3ms while the startup/teardown takes 1ms.

Quoted 4 lines
Quoted 3 lines
	printf("%lu %lu %5.3f\n", tsum / LOOPS, rs / LOOPS, (std / mean) * 100.0);
	return 0;
}
Quoted 8 lines

This shows real benefits indeed.

base v6 v6+ppc_hack
101us 65us 57us

So doing a proper powerpc fix indeed would make sense. I think it is going to be similar. Let me go and read your series again.

Have fun!

Quoted 3 lines

For the genirq bits of the series, consider the tag if applicable

Thank you!

tglx