[tip: irq/core] genirq: Ignore tot_count for handle_percpu_irq() interrupts
From: tip-bot2 for Rosen Penev
Date: Sun Nov 08 2026 - 11:44:38 EST
The following commit has been merged into the irq/core branch of tip:
Commit-ID: 39512299c085723a334a5c50b999d661d9de64af
Gitweb: https://git.kernel.org/tip/39512299c085723a334a5c50b999d661d9de64af
Author: Rosen Penev <rosenp@xxxxxxxxx>
AuthorDate: Thu, 05 Nov 2026 21:19:59 -08:00
Committer: Thomas Gleixner <tglx@xxxxxxxxxx>
CommitterDate: Sun, 08 Nov 2026 17:39:43 +01:00
genirq: Ignore tot_count for handle_percpu_irq() interrupts
handle_percpu_irq() only increments the per CPU statistics and leaves
desc::tot_count untouched, but nothing marks the descriptors that use it
as per CPU. Both /proc/interrupts and kstat_irqs_desc() treat such an
interrupt as a device interrupt and trust desc::tot_count, which stays
zero.
Since /proc/interrupts started to emit zeros for device interrupts with
a zero desc::tot_count, every MIPS CPU interrupt reads as zero there,
including the timer and every device behind it. /proc/interrupts also
missed NMIs handled by handle_fasteoi_nmi(), which kstat_irqs_desc()
already excluded. The /proc/stat counts of handle_percpu_irq() users
have been zero since kstat_irqs_desc() started to use desc::tot_count.
Add irq_desc_has_tot_count(), which covers per CPU interrupts, NMIs and
handle_percpu_irq() users, and use it in both places.
Seen on ASUS RT-N16 and TP-LINK Archer C7 v2 (both MIPS 74K) router, where
the "gpio, ttyS0" lines stayed at zero while the GPIO interrupts chained
behind it counted.
Fixes: 0179464391af ("genirq/proc: Utilize irq_desc::tot_count to avoid evaluation")
Fixes: 1136b0728969 ("genirq: Avoid summation loops for /proc/stat")
Signed-off-by: Rosen Penev <rosenp@xxxxxxxxx>
Signed-off-by: Thomas Gleixner <tglx@xxxxxxxxxx>
Cc: stable@xxxxxxxxxxxxxxx
Link: https://patch.msgid.link/20261106051959.901861-1-rosenp@xxxxxxxxx
---
kernel/irq/internals.h | 12 ++++++++++++
kernel/irq/irqdesc.c | 4 +---
kernel/irq/proc.c | 7 +++----
3 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h
index daa1507..13c247d 100644
--- a/kernel/irq/internals.h
+++ b/kernel/irq/internals.h
@@ -302,6 +302,18 @@ static inline bool irq_is_nmi(struct irq_desc *desc)
return desc->istate & IRQS_NMI;
}
+/*
+ * Per CPU interrupts, NMIs and interrupts handled by handle_percpu_irq()
+ * only update the per CPU statistics and leave desc::tot_count alone.
+ */
+static inline bool irq_desc_has_tot_count(struct irq_desc *desc)
+{
+ return !irq_settings_is_per_cpu_devid(desc) &&
+ !irq_settings_is_per_cpu(desc) &&
+ !irq_is_nmi(desc) &&
+ desc->handle_irq != handle_percpu_irq;
+}
+
#ifdef CONFIG_PM_SLEEP
void irq_pm_handle_wakeup(struct irq_desc *desc);
void irq_pm_install_action(struct irq_desc *desc, struct irqaction *action);
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index 7cbcc3f..3f7bfe0 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -1020,9 +1020,7 @@ static unsigned int kstat_irqs_desc(struct irq_desc *desc, const struct cpumask
unsigned int sum = 0;
int cpu;
- if (!irq_settings_is_per_cpu_devid(desc) &&
- !irq_settings_is_per_cpu(desc) &&
- !irq_is_nmi(desc))
+ if (irq_desc_has_tot_count(desc))
return data_race(desc->tot_count);
for_each_cpu(cpu, cpumask)
diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c
index 0c0df16..c985515 100644
--- a/kernel/irq/proc.c
+++ b/kernel/irq/proc.c
@@ -559,11 +559,10 @@ static int irq_seq_show(struct seq_file *p, void *v)
seq_putc(p, ':');
/*
- * Always output per CPU interrupts. Output device interrupts only when
- * desc::tot_count is not zero.
+ * Always output interrupts which do not maintain desc::tot_count.
+ * Output the others 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))
+ if (!irq_desc_has_tot_count(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());