Re: [PATCH] genirq/proc: Fix missing CPU header in /proc/interrupts
From: Radu Rendec
Date: Thu Sep 17 2026 - 22:55:52 EST
On Fri, 2026-09-11 at 13:31 -0500, Haris Okanovic wrote:
> irq_seq_show() must be idempotent since it may be called multiple times
> to render the same record, e.g. when seq_file's buffer is exceeded. It
> emits the CPU header behind the one-shot print_header flag, which the
> first pass clears, so the header is lost with the discarded buffer.
>
> This can be seen on large systems, such as 8g.48xl (192 CPUs) and
> 8i.96xl (384 CPUs) AWS instance types:
>
> $ head -1 /proc/interrupts
> 9: 0 0 ... 0 GICv3 25 Level vgic
>
> Replace print_header with the position of the first record and include
> the header whenever that record is emitted.
>
> Fixes: 171cc0d9eed1 ("genirq/proc: Speed up /proc/interrupts iteration")
> Signed-off-by: Haris Okanovic <harisokn@xxxxxxxxxx>
> ---
> kernel/irq/proc.c | 24 +++++++++++++++++-------
> 1 file changed, 17 insertions(+), 7 deletions(-)
>
> diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c
> index 1b835725f7b1c..0c0df16de5816 100644
> --- a/kernel/irq/proc.c
> +++ b/kernel/irq/proc.c
> @@ -462,7 +462,7 @@ int __weak arch_show_interrupts(struct seq_file *p, int prec)
> static DEFINE_RAW_SPINLOCK(irq_proc_constraints_lock);
>
> static struct irq_proc_constraints {
> - bool print_header;
> + loff_t first_pos;
> unsigned int num_prec;
> unsigned int chip_width;
> } irq_proc_constraints __read_mostly = {
> @@ -543,14 +543,13 @@ static int irq_seq_show(struct seq_file *p, void *v)
> struct irqaction *action;
>
> /* Print header for the first interrupt? */
> - if (constr->print_header) {
> + if (p->index == constr->first_pos) {
> unsigned int cpu;
>
> seq_printf(p, "%*s", constr->num_prec + 8, "");
> for_each_online_cpu(cpu)
> seq_printf(p, "CPU%-8d", cpu);
> seq_putc(p, '\n');
> - constr->print_header = false;
> }
>
> if (desc == ARCH_PROC_IRQDESC)
> @@ -635,14 +634,25 @@ static void *irq_seq_next_desc(loff_t *pos)
>
> static void *irq_seq_start(struct seq_file *f, loff_t *pos)
> {
> - if (!*pos) {
> - struct irq_proc_constraints *constr = f->private;
> + struct irq_proc_constraints *constr = f->private;
> + bool first = !*pos;
> + void *ret;
>
> + if (first) {
> constr->num_prec = READ_ONCE(irq_proc_constraints.num_prec);
> constr->chip_width = READ_ONCE(irq_proc_constraints.chip_width);
> - constr->print_header = true;
> }
> - return irq_seq_next_desc(pos);
> +
> + ret = irq_seq_next_desc(pos);
> +
> + /*
> + * Record position of the first output record so that irq_seq_show()
> + * knows where to write the header.
> + */
> + if (first)
> + constr->first_pos = *pos;
> +
> + return ret;
> }
>
> static void *irq_seq_next(struct seq_file *f, void *v, loff_t *pos)
>
> ---
> base-commit: 08df884136f1c1197bab2a27814404fd329d9aac
> change-id: 20260911-dev-harisokn-proc-interrupts-fix-b4-7498f7e18db5
Since I don't have access to a machine with so many CPUs, I tested
locally in a regular VM and simulated the large number of CPUs by
adding artificial buffer writes to both the header part and the IRQ
counter part of irq_seq_show(). I could reproduce the problem and
validate the fix. Also, semantically the fix looks good to me.
Thanks for catching this and for the patch!
Reviewed-by: Radu Rendec <radu@xxxxxxxxxx>