[patch V4 02/15] genirq/proc: Avoid formatting zero counts in /proc/interrupts

Thomas Gleixner
2026-03-31T07:25:38Z

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.

Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Reviewed-by: Dmitry Ilvokhin <d@ilvokhin.com>
Reviewed-by: Radu Rendec <radu@rendec.net>
---
V2: Use sizeof() for ZSTR1_LEN - Dmitry
---
include/linux/interrupt.h | 1 +
kernel/irq/proc.c | 42 +++++++++++++++++++++++++++++++++++++-----
2 files changed, 38 insertions(+), 5 deletions(-)
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -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);
--- a/kernel/irq/proc.c
+++ b/kernel/irq/proc.c
@@ -450,6 +450,42 @@ int __weak arch_show_interrupts(struct s
# define ACTUAL_NR_IRQS irq_get_nr_irqs()
#endif

+#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 +521,7 @@ int show_interrupts(struct seq_file *p,
return 0;

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);