Re: [patch V3 04/14] x86/irq: Make irqstats array based

From: Radu Rendec

Date: Sat Mar 28 2026 - 13:07:05 EST


On Thu, 2026-03-26 at 22:56 +0100, Thomas Gleixner wrote:
> 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.
>
> Signed-off-by: Thomas Gleixner <tglx@xxxxxxxxxx>
> ---
> 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/irq.h          |    2
>  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               |  251 ++++++++++++------------------------
>  arch/x86/kernel/irq_work.c          |    2
>  arch/x86/kernel/irqinit.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
>  29 files changed, 170 insertions(+), 233 deletions(-)
>
> --- a/arch/x86/events/amd/core.c
> +++ b/arch/x86/events/amd/core.c
> @@ -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;
> --- a/arch/x86/events/amd/ibs.c
> +++ b/arch/x86/events/amd/ibs.c
> @@ -1403,7 +1403,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);
>  
> --- a/arch/x86/events/core.c
> +++ b/arch/x86/events/core.c
> @@ -1747,7 +1747,7 @@ int x86_pmu_handle_irq(struct pt_regs *r
>   }
>  
>   if (handled)
> - inc_irq_stat(apic_perf_irqs);
> + inc_perf_irq_stat();
>  
>   return handled;
>  }
> --- a/arch/x86/events/intel/core.c
> +++ b/arch/x86/events/intel/core.c
> @@ -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
> --- a/arch/x86/events/intel/knc.c
> +++ b/arch/x86/events/intel/knc.c
> @@ -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];
> --- a/arch/x86/events/intel/p4.c
> +++ b/arch/x86/events/intel/p4.c
> @@ -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
> --- a/arch/x86/events/zhaoxin/core.c
> +++ b/arch/x86/events/zhaoxin/core.c
> @@ -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
> --- a/arch/x86/hyperv/hv_init.c
> +++ b/arch/x86/hyperv/hv_init.c
> @@ -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);
>  }
>  
> --- a/arch/x86/include/asm/hardirq.h
> +++ b/arch/x86/include/asm/hardirq.h
> @@ -4,51 +4,60 @@
>  
>  #include <linux/threads.h>
>  
> -typedef struct {
> -#if IS_ENABLED(CONFIG_CPU_MITIGATIONS) && IS_ENABLED(CONFIG_KVM_INTEL)
> - u8      kvm_cpu_l1tf_flush_l1d;
> -#endif
> - unsigned int __nmi_count; /* arch dependent */
> +enum irq_stat_counts {
> + IRQ_COUNT_NMI,
>  #ifdef CONFIG_X86_LOCAL_APIC
> - unsigned int apic_timer_irqs; /* arch dependent */
> - unsigned int irq_spurious_count;
> - unsigned int icr_read_retry_count;
> -#endif
> -#if IS_ENABLED(CONFIG_KVM)
> - unsigned int kvm_posted_intr_ipis;
> - unsigned int kvm_posted_intr_wakeup_ipis;
> - unsigned int kvm_posted_intr_nested_ipis;
> + IRQ_COUNT_APIC_TIMER,
> + IRQ_COUNT_SPURIOUS,
> + IRQ_COUNT_APIC_PERF,
> + IRQ_COUNT_IRQ_WORK,
> + IRQ_COUNT_ICR_READ_RETRY,
> + IRQ_COUNT_X86_PLATFORM_IPI,
>  #endif
> -#ifdef CONFIG_GUEST_PERF_EVENTS
> - unsigned int perf_guest_mediated_pmis;
> -#endif
> - unsigned int x86_platform_ipis; /* arch dependent */
> - unsigned int apic_perf_irqs;
> - unsigned int apic_irq_work_irqs;
>  #ifdef CONFIG_SMP
> - unsigned int irq_resched_count;
> - unsigned int irq_call_count;
> + IRQ_COUNT_RESCHEDULE,
> + IRQ_COUNT_CALL_FUNCTION,
>  #endif
> - unsigned int irq_tlb_count;
> + IRQ_COUNT_TLB,
>  #ifdef CONFIG_X86_THERMAL_VECTOR
> - unsigned int irq_thermal_count;
> + IRQ_COUNT_THERMAL_APIC,
>  #endif
>  #ifdef CONFIG_X86_MCE_THRESHOLD
> - unsigned int irq_threshold_count;
> + IRQ_COUNT_THRESHOLD_APIC,
>  #endif
>  #ifdef CONFIG_X86_MCE_AMD
> - unsigned int irq_deferred_error_count;
> + IRQ_COUNT_DEFERRED_ERROR,
> +#endif
> +#ifdef CONFIG_X86_MCE
> + IRQ_COUNT_MCE_EXCEPTION,
> + IRQ_COUNT_MCE_POLL,
>  #endif
>  #ifdef CONFIG_X86_HV_CALLBACK_VECTOR
> - unsigned int irq_hv_callback_count;
> + IRQ_COUNT_HYPERVISOR_CALLBACK,
>  #endif
>  #if IS_ENABLED(CONFIG_HYPERV)
> - unsigned int irq_hv_reenlightenment_count;
> - unsigned int hyperv_stimer0_count;
> + IRQ_COUNT_HYPERV_REENLIGHTENMENT,
> + IRQ_COUNT_HYPERV_STIMER0,
> +#endif
> +#if IS_ENABLED(CONFIG_KVM)
> + IRQ_COUNT_POSTED_INTR,
> + IRQ_COUNT_POSTED_INTR_NESTED,
> + IRQ_COUNT_POSTED_INTR_WAKEUP,
> +#endif
> +#ifdef CONFIG_GUEST_PERF_EVENTS
> + IRQ_COUNT_PERF_GUEST_MEDIATED_PMI,
>  #endif
>  #ifdef CONFIG_X86_POSTED_MSI
> - unsigned int posted_msi_notification_count;
> + IRQ_COUNT_POSTED_MSI_NOTIFICATION,
>  #endif
> + IRQ_COUNT_MAX,
> +};
> +
> +typedef struct {
> +#if IS_ENABLED(CONFIG_CPU_MITIGATIONS) && IS_ENABLED(CONFIG_KVM_INTEL)
> + u8      kvm_cpu_l1tf_flush_l1d;
> +#endif
> + unsigned int counts[IRQ_COUNT_MAX];
>  } ____cacheline_aligned irq_cpustat_t;
>  
>  DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);
> @@ -58,15 +67,23 @@ DECLARE_PER_CPU_ALIGNED(struct pi_desc,
>  #endif
>  #define __ARCH_IRQ_STAT
>  
> -#define inc_irq_stat(member) this_cpu_inc(irq_stat.member)
> +#define inc_irq_stat(index) this_cpu_inc(irq_stat.counts[IRQ_COUNT_##index])
> +
> +#ifdef CONFIG_X86_LOCAL_APIC
> +#define inc_perf_irq_stat() inc_irq_stat(APIC_PERF)
> +#else
> +#define inc_perf_irq_stat() do { } while (0)
> +#endif
>  
>  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);
>  #define local_softirq_pending_ref       __softirq_pending
> --- a/arch/x86/include/asm/irq.h
> +++ b/arch/x86/include/asm/irq.h
> @@ -47,4 +47,6 @@ void arch_trigger_cpumask_backtrace(cons
>  #define arch_trigger_cpumask_backtrace arch_trigger_cpumask_backtrace
>  #endif
>  
> +void irq_init_stats(void);
> +
>  #endif /* _ASM_X86_IRQ_H */
> --- a/arch/x86/include/asm/mce.h
> +++ b/arch/x86/include/asm/mce.h
> @@ -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);
>  
> --- a/arch/x86/kernel/apic/apic.c
> +++ b/arch/x86/kernel/apic/apic.c
> @@ -1040,7 +1040,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);
>  }
> @@ -2108,7 +2108,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
> --- a/arch/x86/kernel/apic/ipi.c
> +++ b/arch/x86/kernel/apic/ipi.c
> @@ -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;
> --- a/arch/x86/kernel/cpu/acrn.c
> +++ b/arch/x86/kernel/cpu/acrn.c
> @@ -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();
> --- a/arch/x86/kernel/cpu/mce/amd.c
> +++ b/arch/x86/kernel/cpu/mce/amd.c
> @@ -840,7 +840,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();
> --- a/arch/x86/kernel/cpu/mce/core.c
> +++ b/arch/x86/kernel/cpu/mce/core.c
> @@ -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;
> --- a/arch/x86/kernel/cpu/mce/threshold.c
> +++ b/arch/x86/kernel/cpu/mce/threshold.c
> @@ -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();
> --- a/arch/x86/kernel/cpu/mshyperv.c
> +++ b/arch/x86/kernel/cpu/mshyperv.c
> @@ -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();
>  
> @@ -191,7 +191,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);
> --- a/arch/x86/kernel/irq.c
> +++ b/arch/x86/kernel/irq.c
> @@ -62,156 +62,105 @@ 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;
> +struct irq_stat_info {
> + unsigned int skip_vector;
> + const char *symbol;
> + const char *text;
> +};
>  
> - seq_put_decimal_ull_width(p, delimiter, num, width);
> -}
> +#define ISS(idx, sym, txt) [IRQ_COUNT_##idx] = { .symbol = sym, .text = txt }
>  
> -#define irq_stats(x) (&per_cpu(irq_stat, x))
> -/*
> - * /proc/interrupts printing for arch specific interrupts
> - */
> -int arch_show_interrupts(struct seq_file *p, int prec)
> -{
> - int j;
> +#define ITS(idx, sym, txt) [IRQ_COUNT_##idx] = \
> + { .skip_vector = idx## _VECTOR, .symbol = sym, .text = txt }
>  
> - seq_printf(p, "%*s:", prec, "NMI");
> - for_each_online_cpu(j)
> - put_decimal(p, irq_stats(j)->__nmi_count);
> - seq_puts(p, "  Non-maskable interrupts\n");
> +static struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] __ro_after_init = {
> + ISS(NMI, "NMI", "  Non-maskable interrupts\n"),
>  #ifdef CONFIG_X86_LOCAL_APIC
> - seq_printf(p, "%*s:", prec, "LOC");
> - for_each_online_cpu(j)
> - put_decimal(p, irq_stats(j)->apic_timer_irqs);
> - seq_puts(p, "  Local timer interrupts\n");
> -
> - seq_printf(p, "%*s:", prec, "SPU");
> - for_each_online_cpu(j)
> - put_decimal(p, irq_stats(j)->irq_spurious_count);
> - seq_puts(p, "  Spurious interrupts\n");
> - seq_printf(p, "%*s:", prec, "PMI");
> - for_each_online_cpu(j)
> - put_decimal(p, irq_stats(j)->apic_perf_irqs);
> - seq_puts(p, "  Performance monitoring interrupts\n");
> - seq_printf(p, "%*s:", prec, "IWI");
> - for_each_online_cpu(j)
> - put_decimal(p, irq_stats(j)->apic_irq_work_irqs);
> - seq_puts(p, "  IRQ work interrupts\n");
> - seq_printf(p, "%*s:", prec, "RTR");
> - for_each_online_cpu(j)
> - 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");
> - for_each_online_cpu(j)
> - put_decimal(p, irq_stats(j)->x86_platform_ipis);
> - seq_puts(p, "  Platform interrupts\n");
> - }
> + ISS(APIC_TIMER, "LOC", "  Local timer interrupts\n"),
> + ISS(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"),
> + ISS(X86_PLATFORM_IPI, "PLT", "  Platform interrupts\n"),
>  #endif
>  #ifdef CONFIG_SMP
> - seq_printf(p, "%*s:", prec, "RES");
> - for_each_online_cpu(j)
> - put_decimal(p, irq_stats(j)->irq_resched_count);
> - seq_puts(p, "  Rescheduling interrupts\n");
> - seq_printf(p, "%*s:", prec, "CAL");
> - for_each_online_cpu(j)
> - put_decimal(p, irq_stats(j)->irq_call_count);
> - seq_puts(p, "  Function call interrupts\n");
> - seq_printf(p, "%*s:", prec, "TLB");
> - for_each_online_cpu(j)
> - put_decimal(p, irq_stats(j)->irq_tlb_count);
> - seq_puts(p, "  TLB shootdowns\n");
> + ISS(RESCHEDULE, "RES", "  Rescheduling interrupts\n"),
> + ISS(CALL_FUNCTION, "CAL", "  Function call interrupts\n"),
>  #endif
> + ISS(TLB, "TLB", "  TLB shootdowns\n"),
>  #ifdef CONFIG_X86_THERMAL_VECTOR
> - seq_printf(p, "%*s:", prec, "TRM");
> - for_each_online_cpu(j)
> - put_decimal(p, irq_stats(j)->irq_thermal_count);
> - seq_puts(p, "  Thermal event interrupts\n");
> + ISS(THERMAL_APIC, "TRM", "  Thermal event interrupt\n"),
>  #endif
>  #ifdef CONFIG_X86_MCE_THRESHOLD
> - seq_printf(p, "%*s:", prec, "THR");
> - for_each_online_cpu(j)
> - put_decimal(p, irq_stats(j)->irq_threshold_count);
> - seq_puts(p, "  Threshold APIC interrupts\n");
> + ISS(THRESHOLD_APIC, "THR", "  Threshold APIC interrupts\n"),
>  #endif
>  #ifdef CONFIG_X86_MCE_AMD
> - seq_printf(p, "%*s:", prec, "DFR");
> - for_each_online_cpu(j)
> - put_decimal(p, irq_stats(j)->irq_deferred_error_count);
> - seq_puts(p, "  Deferred Error APIC interrupts\n");
> + ISS(DEFERRED_ERROR, "DFR", "  Deferred Error APIC interrupts\n"),
>  #endif
>  #ifdef CONFIG_X86_MCE
> - seq_printf(p, "%*s:", prec, "MCE");
> - for_each_online_cpu(j)
> - put_decimal(p, per_cpu(mce_exception_count, j));
> - seq_puts(p, "  Machine check exceptions\n");
> - seq_printf(p, "%*s:", prec, "MCP");
> - for_each_online_cpu(j)
> - put_decimal(p, per_cpu(mce_poll_count, j));
> - seq_puts(p, "  Machine check polls\n");
> + ISS(MCE_EXCEPTION, "MCE", "  Machine check exceptions\n"),
> + ISS(MCE_POLL, "MCP", "  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");
> - for_each_online_cpu(j)
> - put_decimal(p, irq_stats(j)->irq_hv_callback_count);
> - seq_puts(p, "  Hypervisor callback interrupts\n");
> - }
> + ITS(HYPERVISOR_CALLBACK, "HYP", "  Hypervisor callback interrupts\n"),
>  #endif
>  #if IS_ENABLED(CONFIG_HYPERV)
> - if (test_bit(HYPERV_REENLIGHTENMENT_VECTOR, system_vectors)) {
> - seq_printf(p, "%*s:", prec, "HRE");
> - for_each_online_cpu(j)
> - 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");
> - for_each_online_cpu(j)
> - put_decimal(p, irq_stats(j)->hyperv_stimer0_count);
> - seq_puts(p, "  Hyper-V stimer0 interrupts\n");
> - }
> -#endif
> - seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count));
> -#if defined(CONFIG_X86_IO_APIC)
> - seq_printf(p, "%*s: %10u\n", prec, "MIS", atomic_read(&irq_mis_count));
> + ITS(HYPERV_REENLIGHTENMENT, "HRE", "  Hyper-V reenlightenment interrupts\n"),
> + ITS(HYPERV_STIMER0, "HVS", "  Hyper-V stimer0 interrupts\n"),
>  #endif
>  #if IS_ENABLED(CONFIG_KVM)
> - seq_printf(p, "%*s:", prec, "PIN");
> - for_each_online_cpu(j)
> - put_decimal(p, irq_stats(j)->kvm_posted_intr_ipis);
> - seq_puts(p, "  Posted-interrupt notification event\n");
> -
> - seq_printf(p, "%*s:", prec, "NPI");
> - for_each_online_cpu(j)
> - 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");
> - for_each_online_cpu(j)
> - put_decimal(p, irq_stats(j)->kvm_posted_intr_wakeup_ipis);
> - seq_puts(p, "  Posted-interrupt wakeup event\n");
> + ITS(POSTED_INTR, "PIN", "  Posted-interrupt notification event\n"),
> + ITS(POSTED_INTR_NESTED, "NPI", "  Nested posted-interrupt event\n"),
> + ITS(POSTED_INTR_WAKEUP, "PIW", "  Posted-interrupt wakeup event\n"),
>  #endif
>  #ifdef CONFIG_GUEST_PERF_EVENTS
> - seq_printf(p, "%*s:", prec, "VPMI");
> - for_each_online_cpu(j)
> - put_decimal(p, irq_stats(j)->perf_guest_mediated_pmis);
> - seq_puts(p, " Perf Guest Mediated PMI\n");
> + ISS(PERF_GUEST_MEDIATED_PMI, "VPMI", " Perf Guest Mediated PMI\n"),
>  #endif
>  #ifdef CONFIG_X86_POSTED_MSI
> - seq_printf(p, "%*s:", prec, "PMN");
> - for_each_online_cpu(j)
> - put_decimal(p, irq_stats(j)->posted_msi_notification_count);
> - seq_puts(p, "  Posted MSI notification event\n");
> + ISS(POSTED_MSI_NOTIFICATION, "PMN", "  Posted MSI notification event\n"),
>  #endif
> +};
> +
> +void __init irq_init_stats(void)
> +{
> + 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;
> + }
> +
> +#ifdef CONFIG_X86_LOCAL_APIC
> + if (!x86_platform_ipi_callback)
> + irq_stat_info[IRQ_COUNT_X86_PLATFORM_IPI].skip_vector = 1;
> +#endif
> +
> +#ifdef CONFIG_X86_POSTED_MSI
> + if (!posted_msi_enabled())
> + irq_stat_info[IRQ_COUNT_POSTED_MSI_NOTIFICATION].skip_vector = 1;
> +#endif
> +}
> +
> +#ifdef CONFIG_PROC_FS
> +/*
> + * /proc/interrupts printing for arch specific interrupts
> + */
> +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);
> + 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;
>  }
>  
> @@ -220,38 +169,11 @@ int arch_show_interrupts(struct seq_file
>   */
>  u64 arch_irq_stat_cpu(unsigned int cpu)
>  {
> - u64 sum = irq_stats(cpu)->__nmi_count;
> + irq_cpustat_t *p = per_cpu_ptr(&irq_stat, cpu);
> + u64 sum = 0;
>  
> -#ifdef CONFIG_X86_LOCAL_APIC
> - sum += irq_stats(cpu)->apic_timer_irqs;
> - sum += irq_stats(cpu)->irq_spurious_count;
> - sum += irq_stats(cpu)->apic_perf_irqs;
> - sum += irq_stats(cpu)->apic_irq_work_irqs;
> - sum += irq_stats(cpu)->icr_read_retry_count;
> - if (x86_platform_ipi_callback)
> - sum += irq_stats(cpu)->x86_platform_ipis;
> -#endif
> -#ifdef CONFIG_SMP
> - sum += irq_stats(cpu)->irq_resched_count;
> - sum += irq_stats(cpu)->irq_call_count;
> -#endif
> -#ifdef CONFIG_X86_THERMAL_VECTOR
> - sum += irq_stats(cpu)->irq_thermal_count;
> -#endif
> -#ifdef CONFIG_X86_MCE_THRESHOLD
> - sum += irq_stats(cpu)->irq_threshold_count;
> -#endif
> -#ifdef CONFIG_X86_HV_CALLBACK_VECTOR
> - sum += irq_stats(cpu)->irq_hv_callback_count;
> -#endif
> -#if IS_ENABLED(CONFIG_HYPERV)
> - sum += irq_stats(cpu)->irq_hv_reenlightenment_count;
> - sum += irq_stats(cpu)->hyperv_stimer0_count;
> -#endif
> -#ifdef CONFIG_X86_MCE
> - sum += per_cpu(mce_exception_count, cpu);
> - sum += per_cpu(mce_poll_count, cpu);
> -#endif
> + for (unsigned int i = 0; i < ARRAY_SIZE(irq_stat_info); i++)
> + sum += p->counts[i];
>   return sum;
>  }
>  
> @@ -260,6 +182,7 @@ 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,
>          struct pt_regs *regs)
> @@ -344,7 +267,7 @@ DEFINE_IDTENTRY_IRQ(common_interrupt)
>  
>  #ifdef CONFIG_X86_LOCAL_APIC
>  /* Function pointer for generic interrupt vector handling */
> -void (*x86_platform_ipi_callback)(void) = NULL;
> +void (*x86_platform_ipi_callback)(void) __ro_after_init = NULL;
>  /*
>   * Handler for X86_PLATFORM_IPI_VECTOR.
>   */
> @@ -354,7 +277,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_x86_platfo
>  
>   apic_eoi();
>   trace_x86_platform_ipi_entry(X86_PLATFORM_IPI_VECTOR);
> - inc_irq_stat(x86_platform_ipis);
> + inc_irq_stat(X86_PLATFORM_IPI);
>   if (x86_platform_ipi_callback)
>   x86_platform_ipi_callback();
>   trace_x86_platform_ipi_exit(X86_PLATFORM_IPI_VECTOR);
> @@ -369,7 +292,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_x86_platfo
>  DEFINE_IDTENTRY_SYSVEC(sysvec_perf_guest_mediated_pmi_handler)
>  {
>   apic_eoi();
> - inc_irq_stat(perf_guest_mediated_pmis);
> + inc_irq_stat(PERF_GUEST_MEDIATED_PMI);
>   perf_guest_handle_mediated_pmi();
>  }
>  #endif
> @@ -395,7 +318,7 @@ EXPORT_SYMBOL_FOR_KVM(kvm_set_posted_int
>  DEFINE_IDTENTRY_SYSVEC_SIMPLE(sysvec_kvm_posted_intr_ipi)
>  {
>   apic_eoi();
> - inc_irq_stat(kvm_posted_intr_ipis);
> + inc_irq_stat(POSTED_INTR);
>  }
>  
>  /*
> @@ -404,7 +327,7 @@ DEFINE_IDTENTRY_SYSVEC_SIMPLE(sysvec_kvm
>  DEFINE_IDTENTRY_SYSVEC(sysvec_kvm_posted_intr_wakeup_ipi)
>  {
>   apic_eoi();
> - inc_irq_stat(kvm_posted_intr_wakeup_ipis);
> + inc_irq_stat(POSTED_INTR_WAKEUP);
>   kvm_posted_intr_wakeup_handler();
>  }
>  
> @@ -414,7 +337,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_kvm_posted
>  DEFINE_IDTENTRY_SYSVEC_SIMPLE(sysvec_kvm_posted_intr_nested_ipi)
>  {
>   apic_eoi();
> - inc_irq_stat(kvm_posted_intr_nested_ipis);
> + inc_irq_stat(POSTED_INTR_NESTED);
>  }
>  #endif
>  
> @@ -488,7 +411,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_posted_msi
>  
>   /* Mark the handler active for intel_ack_posted_msi_irq() */
>   __this_cpu_write(posted_msi_handler_active, true);
> - inc_irq_stat(posted_msi_notification_count);
> + inc_irq_stat(POSTED_MSI_NOTIFICATION);
>   irq_enter();
>  
>   /*
> @@ -583,7 +506,7 @@ static void smp_thermal_vector(void)
>  DEFINE_IDTENTRY_SYSVEC(sysvec_thermal)
>  {
>   trace_thermal_apic_entry(THERMAL_APIC_VECTOR);
> - inc_irq_stat(irq_thermal_count);
> + inc_irq_stat(THERMAL_APIC);
>   smp_thermal_vector();
>   trace_thermal_apic_exit(THERMAL_APIC_VECTOR);
>   apic_eoi();
> --- a/arch/x86/kernel/irq_work.c
> +++ b/arch/x86/kernel/irq_work.c
> @@ -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);
>  }
> --- a/arch/x86/kernel/irqinit.c
> +++ b/arch/x86/kernel/irqinit.c
> @@ -104,6 +104,8 @@ void __init native_init_IRQ(void)
>   if (!cpu_feature_enabled(X86_FEATURE_FRED))
>   idt_setup_apic_and_irq_gates();
>  
> + irq_init_stats();
> +
>   lapic_assign_system_vectors();
>  
>   if (!acpi_ioapic && !of_ioapic && nr_legacy_irqs()) {
> --- a/arch/x86/kernel/kvm.c
> +++ b/arch/x86/kernel/kvm.c
> @@ -310,7 +310,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);
> --- a/arch/x86/kernel/nmi.c
> +++ b/arch/x86/kernel/nmi.c
> @@ -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);
> --- a/arch/x86/kernel/smp.c
> +++ b/arch/x86/kernel/smp.c
> @@ -249,7 +249,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);
>  }
> @@ -258,7 +258,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);
>  }
> @@ -267,7 +267,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);
>  }
> --- a/arch/x86/mm/tlb.c
> +++ b/arch/x86/mm/tlb.c
> @@ -1144,7 +1144,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);
>   }
>  
> --- a/arch/x86/xen/enlighten_hvm.c
> +++ b/arch/x86/xen/enlighten_hvm.c
> @@ -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();
>  
> --- a/arch/x86/xen/enlighten_pv.c
> +++ b/arch/x86/xen/enlighten_pv.c
> @@ -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();
>  
> --- a/arch/x86/xen/smp.c
> +++ b/arch/x86/xen/smp.c
> @@ -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;
>  }
> --- a/arch/x86/xen/smp_pv.c
> +++ b/arch/x86/xen/smp_pv.c
> @@ -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;
>  }

Reviewed-by: Radu Rendec <radu@xxxxxxxxxx>