[patch V5 06/15] x86/irq: Move IOAPIC misrouted and PIC/APIC error counts into irq_stats

From: Thomas Gleixner

Date: Wed Apr 01 2026 - 17:58:20 EST


From: Thomas Gleixner <tglx@xxxxxxxxxx>

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.

Signed-off-by: Thomas Gleixner <tglx@xxxxxxxxxx>
---
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(-)
--- a/arch/x86/include/asm/hardirq.h
+++ b/arch/x86/include/asm/hardirq.h
@@ -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);
--- a/arch/x86/include/asm/hw_irq.h
+++ b/arch/x86/include/asm/hw_irq.h
@@ -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[];
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -2180,7 +2180,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);

--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -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);
}

--- a/arch/x86/kernel/i8259.c
+++ b/arch/x86/kernel/i8259.c
@@ -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
--- a/arch/x86/kernel/irq.c
+++ b/arch/x86/kernel/irq.c
@@ -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,
--- a/fs/proc/stat.c
+++ b/fs/proc/stat.c
@@ -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));