* [PATCH 0/3] powerpc/irq: Use optimizations for /proc/interrupts
@ 2026-05-23 17:40 Shrikanth Hegde
2026-05-23 17:40 ` [PATCH 1/3] powerpc/irq: Move __softirq_pending out of irq_stat Shrikanth Hegde
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Shrikanth Hegde @ 2026-05-23 17:40 UTC (permalink / raw)
To: maddy, linuxppc-dev, tglx; +Cc: sshegde, christophe.leroy, linux-kernel
This series is based on work by Thomas Gleixner[1].
There are several optimization which helps to bring the time taken to
read /proc/interrupts. Though it is not in fastpaths, still many tools
read it often and this reduction in time will save meaningful amount of
cycle over the long duration.
[1]: https://lore.kernel.org/all/20260517194421.705253664@kernel.org/
Bulk of the design/code is copied from Thomas's work on x86. powerpc had
exact pattern which made it quite easy to port.
Using micro-loops[2] of reading /proc/interrupts shows real benefit of
the series. Bulk of the gain is already achieved using Thomas's
series[1]. This series on top provide some more benefits. As the system
size goes up, benefits could be more.
[2]: https://lore.kernel.org/all/87jysxw65f.ffs@tglx/
======== Performance Data ==================
Time taken to read /proc/interrupts 1000 times[2]
Base : 103us
v6 : 63us
v6+patch 1+2 : 57us
v6+patch 1+2+3 : 54us
Base: tip/master at c991e3897ead ("Merge branch into tip/master: 'x86/tdx'")
Depends on v6[1] to be applied first.
Shrikanth Hegde (3):
powerpc/irq: Move __softirq_pending out of irq_stat
powerpc/irq: Make irqstats array based
powerpc/irq: Suppress unlikely interrupt stats by default
arch/powerpc/include/asm/hardirq.h | 31 ++++---
arch/powerpc/kernel/dbell.c | 2 +-
arch/powerpc/kernel/irq.c | 131 +++++++++++++++--------------
arch/powerpc/kernel/time.c | 6 +-
arch/powerpc/kernel/traps.c | 11 +--
arch/powerpc/kernel/watchdog.c | 2 +-
6 files changed, 95 insertions(+), 88 deletions(-)
--
2.47.3
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH 1/3] powerpc/irq: Move __softirq_pending out of irq_stat 2026-05-23 17:40 [PATCH 0/3] powerpc/irq: Use optimizations for /proc/interrupts Shrikanth Hegde @ 2026-05-23 17:40 ` Shrikanth Hegde 2026-05-29 7:43 ` Christophe Leroy (CS GROUP) 2026-05-23 17:40 ` [PATCH 2/3] powerpc/irq: Make irqstats array based Shrikanth Hegde 2026-05-23 17:40 ` [PATCH 3/3] powerpc/irq: Suppress unlikely interrupt stats by default Shrikanth Hegde 2 siblings, 1 reply; 8+ messages in thread From: Shrikanth Hegde @ 2026-05-23 17:40 UTC (permalink / raw) To: maddy, linuxppc-dev, tglx; +Cc: sshegde, christophe.leroy, linux-kernel __softirq_pending isn't part of arch specific irq_stats. It is used by softirq core for various decision making such as whether to kick off ksoftirqd. Move it out of irq_cpustat_t. This makes it simple to make irq_cpustat_t array based approach. Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com> --- arch/powerpc/include/asm/hardirq.h | 3 ++- arch/powerpc/kernel/irq.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/hardirq.h b/arch/powerpc/include/asm/hardirq.h index f133b5930ae1..bf3926a0c69c 100644 --- a/arch/powerpc/include/asm/hardirq.h +++ b/arch/powerpc/include/asm/hardirq.h @@ -6,7 +6,6 @@ #include <linux/irq.h> typedef struct { - unsigned int __softirq_pending; unsigned int timer_irqs_event; unsigned int broadcast_irqs_event; unsigned int timer_irqs_others; @@ -23,6 +22,8 @@ typedef struct { } ____cacheline_aligned irq_cpustat_t; DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat); +DECLARE_PER_CPU(unsigned int, __softirq_pending); +#define local_softirq_pending_ref __softirq_pending #define __ARCH_IRQ_STAT #define __ARCH_IRQ_EXIT_IRQS_DISABLED diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index a0e8b998c9b5..f33df5e5c23f 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -73,6 +73,7 @@ DEFINE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat); EXPORT_PER_CPU_SYMBOL(irq_stat); +DEFINE_PER_CPU(unsigned int, __softirq_pending); #ifdef CONFIG_PPC32 atomic_t ppc_n_lost_interrupts; -- 2.47.3 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 1/3] powerpc/irq: Move __softirq_pending out of irq_stat 2026-05-23 17:40 ` [PATCH 1/3] powerpc/irq: Move __softirq_pending out of irq_stat Shrikanth Hegde @ 2026-05-29 7:43 ` Christophe Leroy (CS GROUP) 0 siblings, 0 replies; 8+ messages in thread From: Christophe Leroy (CS GROUP) @ 2026-05-29 7:43 UTC (permalink / raw) To: Shrikanth Hegde, maddy, linuxppc-dev, tglx; +Cc: christophe.leroy, linux-kernel Le 23/05/2026 à 19:40, Shrikanth Hegde a écrit : > __softirq_pending isn't part of arch specific irq_stats. It is used > by softirq core for various decision making such as whether to kick off > ksoftirqd. > > Move it out of irq_cpustat_t. This makes it simple to make irq_cpustat_t > array based approach. Some reference to commit 0fd7d8628529 ("softirq/core: Consolidate default local_softirq_pending() implementations") should be made I think for better understanding. > > Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com> Reviewed-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org> > --- > arch/powerpc/include/asm/hardirq.h | 3 ++- > arch/powerpc/kernel/irq.c | 1 + > 2 files changed, 3 insertions(+), 1 deletion(-) > > diff --git a/arch/powerpc/include/asm/hardirq.h b/arch/powerpc/include/asm/hardirq.h > index f133b5930ae1..bf3926a0c69c 100644 > --- a/arch/powerpc/include/asm/hardirq.h > +++ b/arch/powerpc/include/asm/hardirq.h > @@ -6,7 +6,6 @@ > #include <linux/irq.h> > > typedef struct { > - unsigned int __softirq_pending; > unsigned int timer_irqs_event; > unsigned int broadcast_irqs_event; > unsigned int timer_irqs_others; > @@ -23,6 +22,8 @@ typedef struct { > } ____cacheline_aligned irq_cpustat_t; > > DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat); > +DECLARE_PER_CPU(unsigned int, __softirq_pending); > +#define local_softirq_pending_ref __softirq_pending > > #define __ARCH_IRQ_STAT > #define __ARCH_IRQ_EXIT_IRQS_DISABLED > diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c > index a0e8b998c9b5..f33df5e5c23f 100644 > --- a/arch/powerpc/kernel/irq.c > +++ b/arch/powerpc/kernel/irq.c > @@ -73,6 +73,7 @@ > > DEFINE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat); > EXPORT_PER_CPU_SYMBOL(irq_stat); > +DEFINE_PER_CPU(unsigned int, __softirq_pending); > > #ifdef CONFIG_PPC32 > atomic_t ppc_n_lost_interrupts; ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/3] powerpc/irq: Make irqstats array based 2026-05-23 17:40 [PATCH 0/3] powerpc/irq: Use optimizations for /proc/interrupts Shrikanth Hegde 2026-05-23 17:40 ` [PATCH 1/3] powerpc/irq: Move __softirq_pending out of irq_stat Shrikanth Hegde @ 2026-05-23 17:40 ` Shrikanth Hegde 2026-05-29 7:46 ` Christophe Leroy (CS GROUP) 2026-05-23 17:40 ` [PATCH 3/3] powerpc/irq: Suppress unlikely interrupt stats by default Shrikanth Hegde 2 siblings, 1 reply; 8+ messages in thread From: Shrikanth Hegde @ 2026-05-23 17:40 UTC (permalink / raw) To: maddy, linuxppc-dev, tglx; +Cc: sshegde, christophe.leroy, linux-kernel Current irq_cpustat_t has separate member for handling each arch specific interrupt type. The same can be achieved with array instead indexed by corresponding irq counter type. This helps to, - Make it easy to integrate into genirq improvements by calling genirq provided irq_proc_emit_counts. That speeds up quite a bit by printing all 0's once as much as possible. - Adding a new vector or software counter only requires to update the table and everything just works - Remove ifdef usage a bit. - Instead of going through each member, it simply becomes an array traversal. Time taken to read /proc/interrupts 1000 times. Base and v6 details can be found in cover-letter. Base : 103us v6 : 63us v6+this_patch : 57us A Decent 10% reduction can be seen in a system 240 CPUs. As the system size increases the gain would be more as emitting 0 would reduce more and more. Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com> --- arch/powerpc/include/asm/hardirq.h | 27 +++++--- arch/powerpc/kernel/dbell.c | 2 +- arch/powerpc/kernel/irq.c | 107 ++++++++++------------------- arch/powerpc/kernel/time.c | 6 +- arch/powerpc/kernel/traps.c | 11 ++- arch/powerpc/kernel/watchdog.c | 2 +- 6 files changed, 64 insertions(+), 91 deletions(-) diff --git a/arch/powerpc/include/asm/hardirq.h b/arch/powerpc/include/asm/hardirq.h index bf3926a0c69c..38098e35b241 100644 --- a/arch/powerpc/include/asm/hardirq.h +++ b/arch/powerpc/include/asm/hardirq.h @@ -5,26 +5,33 @@ #include <linux/threads.h> #include <linux/irq.h> -typedef struct { - unsigned int timer_irqs_event; - unsigned int broadcast_irqs_event; - unsigned int timer_irqs_others; - unsigned int pmu_irqs; - unsigned int mce_exceptions; - unsigned int spurious_irqs; - unsigned int sreset_irqs; +enum irq_stat_counts { + IRQ_COUNT_LOC_TIMER, + IRQ_COUNT_BCT_TIMER, + IRQ_COUNT_OTHER_TIMER, + IRQ_COUNT_SPURIOUS, + IRQ_COUNT_PMI, + IRQ_COUNT_MCE, + IRQ_COUNT_NMI_SRESET, #ifdef CONFIG_PPC_WATCHDOG - unsigned int soft_nmi_irqs; + IRQ_COUNT_WATCHDOG, #endif #ifdef CONFIG_PPC_DOORBELL - unsigned int doorbell_irqs; + IRQ_COUNT_DOORBELL, #endif + IRQ_COUNT_MAX, +}; + +typedef struct { + unsigned int counts[IRQ_COUNT_MAX]; } ____cacheline_aligned irq_cpustat_t; DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat); DECLARE_PER_CPU(unsigned int, __softirq_pending); #define local_softirq_pending_ref __softirq_pending +#define inc_irq_stat(index) __this_cpu_inc(irq_stat.counts[IRQ_COUNT_##index]) + #define __ARCH_IRQ_STAT #define __ARCH_IRQ_EXIT_IRQS_DISABLED diff --git a/arch/powerpc/kernel/dbell.c b/arch/powerpc/kernel/dbell.c index 5712dd846263..f5e298a4c4c0 100644 --- a/arch/powerpc/kernel/dbell.c +++ b/arch/powerpc/kernel/dbell.c @@ -31,7 +31,7 @@ DEFINE_INTERRUPT_HANDLER_ASYNC(doorbell_exception) do_hard_irq_enable(); kvmppc_clear_host_ipi(smp_processor_id()); - __this_cpu_inc(irq_stat.doorbell_irqs); + inc_irq_stat(DOORBELL); smp_ipi_demux_relaxed(); /* already performed the barrier */ diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index f33df5e5c23f..e67a18f62142 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -84,79 +84,57 @@ u32 tau_interrupts(unsigned long cpu); #endif #endif /* CONFIG_PPC32 */ +struct irq_stat_info { + const char *symbol; + const char *text; +}; + +#define ISE(idx, sym, txt)[IRQ_COUNT_##idx] = { .symbol = sym, .text = txt} + +static struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] __ro_after_init = { + ISE(LOC_TIMER, "LOC", " Local timer interrupts for timer event device\n"), + ISE(BCT_TIMER, "BCT", " Broadcast timer interrupts for timer event device\n"), + ISE(OTHER_TIMER, "LOC", " Local timer interrupts for others\n"), + ISE(SPURIOUS, "SPU", " Spurious interrupts\n"), + ISE(PMI, "PMI", " Performance monitoring interrupts\n"), + ISE(MCE, "MCE", " Machine check exceptions\n"), + ISE(NMI_SRESET, "NMI", " System Reset interrupts\n"), +#ifdef CONFIG_PPC_WATCHDOG + ISE(WATCHDOG, "WDG", " Watchdog soft-NMI interrupts\n"), +#endif +#ifdef CONFIG_PPC_DOORBELL + ISE(DOORBELL, "DBL", " Doorbell interrupts\n"), +#endif +}; + int arch_show_interrupts(struct seq_file *p, int prec) { - int j; + const struct irq_stat_info *info = irq_stat_info; + + for (unsigned int i = 0; i < ARRAY_SIZE(irq_stat_info); i++, info++) { + seq_printf(p, "%*s:", prec, info->symbol); + irq_proc_emit_counts(p, &irq_stat.counts[i]); + seq_puts(p, info->text); + } #if defined(CONFIG_PPC32) && defined(CONFIG_TAU_INT) if (tau_initialized) { + int j; seq_printf(p, "%*s:", prec, "TAU"); for_each_online_cpu(j) seq_put_decimal_ull_width(p, " ", tau_interrupts(j), 10); seq_puts(p, " PowerPC Thermal Assist (cpu temp)\n"); } #endif /* CONFIG_PPC32 && CONFIG_TAU_INT */ - - seq_printf(p, "%*s:", prec, "LOC"); - for_each_online_cpu(j) - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).timer_irqs_event, 10); - seq_printf(p, " Local timer interrupts for timer event device\n"); - - seq_printf(p, "%*s:", prec, "BCT"); - for_each_online_cpu(j) - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).broadcast_irqs_event, 10); - seq_printf(p, " Broadcast timer interrupts for timer event device\n"); - - seq_printf(p, "%*s:", prec, "LOC"); - for_each_online_cpu(j) - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).timer_irqs_others, 10); - seq_printf(p, " Local timer interrupts for others\n"); - - seq_printf(p, "%*s:", prec, "SPU"); - for_each_online_cpu(j) - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).spurious_irqs, 10); - seq_printf(p, " Spurious interrupts\n"); - - seq_printf(p, "%*s:", prec, "PMI"); - for_each_online_cpu(j) - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).pmu_irqs, 10); - seq_printf(p, " Performance monitoring interrupts\n"); - - seq_printf(p, "%*s:", prec, "MCE"); - for_each_online_cpu(j) - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).mce_exceptions, 10); - seq_printf(p, " Machine check exceptions\n"); - #ifdef CONFIG_PPC_BOOK3S_64 if (cpu_has_feature(CPU_FTR_HVMODE)) { + int j; seq_printf(p, "%*s:", prec, "HMI"); for_each_online_cpu(j) seq_put_decimal_ull_width(p, " ", paca_ptrs[j]->hmi_irqs, 10); seq_printf(p, " Hypervisor Maintenance Interrupts\n"); } #endif - - seq_printf(p, "%*s:", prec, "NMI"); - for_each_online_cpu(j) - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).sreset_irqs, 10); - seq_printf(p, " System Reset interrupts\n"); - -#ifdef CONFIG_PPC_WATCHDOG - seq_printf(p, "%*s:", prec, "WDG"); - for_each_online_cpu(j) - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).soft_nmi_irqs, 10); - seq_printf(p, " Watchdog soft-NMI interrupts\n"); -#endif - -#ifdef CONFIG_PPC_DOORBELL - if (cpu_has_feature(CPU_FTR_DBELL)) { - seq_printf(p, "%*s:", prec, "DBL"); - for_each_online_cpu(j) - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).doorbell_irqs, 10); - seq_printf(p, " Doorbell interrupts\n"); - } -#endif - return 0; } @@ -165,24 +143,15 @@ int arch_show_interrupts(struct seq_file *p, int prec) */ u64 arch_irq_stat_cpu(unsigned int cpu) { - u64 sum = per_cpu(irq_stat, cpu).timer_irqs_event; + irq_cpustat_t *p = per_cpu_ptr(&irq_stat, cpu); + u64 sum = 0; + + for (unsigned int i = 0; i < ARRAY_SIZE(irq_stat_info); i++) + sum += p->counts[i]; - sum += per_cpu(irq_stat, cpu).broadcast_irqs_event; - sum += per_cpu(irq_stat, cpu).pmu_irqs; - sum += per_cpu(irq_stat, cpu).mce_exceptions; - sum += per_cpu(irq_stat, cpu).spurious_irqs; - sum += per_cpu(irq_stat, cpu).timer_irqs_others; #ifdef CONFIG_PPC_BOOK3S_64 sum += paca_ptrs[cpu]->hmi_irqs; #endif - sum += per_cpu(irq_stat, cpu).sreset_irqs; -#ifdef CONFIG_PPC_WATCHDOG - sum += per_cpu(irq_stat, cpu).soft_nmi_irqs; -#endif -#ifdef CONFIG_PPC_DOORBELL - sum += per_cpu(irq_stat, cpu).doorbell_irqs; -#endif - return sum; } @@ -248,7 +217,7 @@ static void __do_irq(struct pt_regs *regs, unsigned long oldsp) /* And finally process it */ if (unlikely(!irq)) - __this_cpu_inc(irq_stat.spurious_irqs); + inc_irq_stat(SPURIOUS); else generic_handle_irq(irq); diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 4bbeb8644d3d..44da7be36199 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -572,13 +572,13 @@ DEFINE_INTERRUPT_HANDLER_ASYNC(timer_interrupt) now = get_tb(); if (now >= *next_tb) { evt->event_handler(evt); - __this_cpu_inc(irq_stat.timer_irqs_event); + inc_irq_stat(LOC_TIMER); } else { now = *next_tb - now; if (now > decrementer_max) now = decrementer_max; set_dec_or_work(now); - __this_cpu_inc(irq_stat.timer_irqs_others); + inc_irq_stat(OTHER_TIMER); } trace_timer_interrupt_exit(regs); @@ -591,7 +591,7 @@ EXPORT_SYMBOL(timer_interrupt); void timer_broadcast_interrupt(void) { tick_receive_broadcast(); - __this_cpu_inc(irq_stat.broadcast_irqs_event); + inc_irq_stat(BCT_TIMER); } #endif diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index cb8e9357383e..a8f15154bd9a 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -459,8 +459,7 @@ DEFINE_INTERRUPT_HANDLER_NMI(system_reset_exception) } hv_nmi_check_nonrecoverable(regs); - - __this_cpu_inc(irq_stat.sreset_irqs); + inc_irq_stat(NMI_SRESET); /* See if any machine dependent calls */ if (ppc_md.system_reset_exception) { @@ -817,7 +816,7 @@ static void __machine_check_exception(struct pt_regs *regs) { int recover = 0; - __this_cpu_inc(irq_stat.mce_exceptions); + inc_irq_stat(MCE); add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); @@ -1932,8 +1931,7 @@ DEFINE_INTERRUPT_HANDLER(vsx_unavailable_tm) DECLARE_INTERRUPT_HANDLER_NMI(performance_monitor_exception_nmi); DEFINE_INTERRUPT_HANDLER_NMI(performance_monitor_exception_nmi) { - __this_cpu_inc(irq_stat.pmu_irqs); - + inc_irq_stat(PMI); perf_irq(regs); return 0; @@ -1943,8 +1941,7 @@ DEFINE_INTERRUPT_HANDLER_NMI(performance_monitor_exception_nmi) DECLARE_INTERRUPT_HANDLER_ASYNC(performance_monitor_exception_async); DEFINE_INTERRUPT_HANDLER_ASYNC(performance_monitor_exception_async) { - __this_cpu_inc(irq_stat.pmu_irqs); - + inc_irq_stat(PMI); perf_irq(regs); } diff --git a/arch/powerpc/kernel/watchdog.c b/arch/powerpc/kernel/watchdog.c index 764001deb060..f516eeccc9f6 100644 --- a/arch/powerpc/kernel/watchdog.c +++ b/arch/powerpc/kernel/watchdog.c @@ -381,7 +381,7 @@ DEFINE_INTERRUPT_HANDLER_NMI(soft_nmi_interrupt) if (!cpumask_test_cpu(cpu, &wd_cpus_enabled)) return 0; - __this_cpu_inc(irq_stat.soft_nmi_irqs); + inc_irq_stat(WATCHDOG); tb = get_tb(); if (tb - per_cpu(wd_timer_tb, cpu) >= wd_panic_timeout_tb) { -- 2.47.3 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 2/3] powerpc/irq: Make irqstats array based 2026-05-23 17:40 ` [PATCH 2/3] powerpc/irq: Make irqstats array based Shrikanth Hegde @ 2026-05-29 7:46 ` Christophe Leroy (CS GROUP) 0 siblings, 0 replies; 8+ messages in thread From: Christophe Leroy (CS GROUP) @ 2026-05-29 7:46 UTC (permalink / raw) To: Shrikanth Hegde, maddy, linuxppc-dev, tglx; +Cc: linux-kernel Le 23/05/2026 à 19:40, Shrikanth Hegde a écrit : > Current irq_cpustat_t has separate member for handling each arch > specific interrupt type. The same can be achieved with array instead > indexed by corresponding irq counter type. > > This helps to, > > - Make it easy to integrate into genirq improvements by calling > genirq provided irq_proc_emit_counts. That speeds up quite a bit > by printing all 0's once as much as possible. > > - Adding a new vector or software counter only requires to update the table > and everything just works > > - Remove ifdef usage a bit. > > - Instead of going through each member, it simply becomes an array > traversal. > > Time taken to read /proc/interrupts 1000 times. > Base and v6 details can be found in cover-letter. > Base : 103us > v6 : 63us > v6+this_patch : 57us > > A Decent 10% reduction can be seen in a system 240 CPUs. As the system > size increases the gain would be more as emitting 0 would reduce more > and more. > > Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com> Reviewed-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org> > --- > arch/powerpc/include/asm/hardirq.h | 27 +++++--- > arch/powerpc/kernel/dbell.c | 2 +- > arch/powerpc/kernel/irq.c | 107 ++++++++++------------------- > arch/powerpc/kernel/time.c | 6 +- > arch/powerpc/kernel/traps.c | 11 ++- > arch/powerpc/kernel/watchdog.c | 2 +- > 6 files changed, 64 insertions(+), 91 deletions(-) > > diff --git a/arch/powerpc/include/asm/hardirq.h b/arch/powerpc/include/asm/hardirq.h > index bf3926a0c69c..38098e35b241 100644 > --- a/arch/powerpc/include/asm/hardirq.h > +++ b/arch/powerpc/include/asm/hardirq.h > @@ -5,26 +5,33 @@ > #include <linux/threads.h> > #include <linux/irq.h> > > -typedef struct { > - unsigned int timer_irqs_event; > - unsigned int broadcast_irqs_event; > - unsigned int timer_irqs_others; > - unsigned int pmu_irqs; > - unsigned int mce_exceptions; > - unsigned int spurious_irqs; > - unsigned int sreset_irqs; > +enum irq_stat_counts { > + IRQ_COUNT_LOC_TIMER, > + IRQ_COUNT_BCT_TIMER, > + IRQ_COUNT_OTHER_TIMER, > + IRQ_COUNT_SPURIOUS, > + IRQ_COUNT_PMI, > + IRQ_COUNT_MCE, > + IRQ_COUNT_NMI_SRESET, > #ifdef CONFIG_PPC_WATCHDOG > - unsigned int soft_nmi_irqs; > + IRQ_COUNT_WATCHDOG, > #endif > #ifdef CONFIG_PPC_DOORBELL > - unsigned int doorbell_irqs; > + IRQ_COUNT_DOORBELL, > #endif > + IRQ_COUNT_MAX, > +}; > + > +typedef struct { > + unsigned int counts[IRQ_COUNT_MAX]; > } ____cacheline_aligned irq_cpustat_t; > > DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat); > DECLARE_PER_CPU(unsigned int, __softirq_pending); > #define local_softirq_pending_ref __softirq_pending > > +#define inc_irq_stat(index) __this_cpu_inc(irq_stat.counts[IRQ_COUNT_##index]) > + > #define __ARCH_IRQ_STAT > #define __ARCH_IRQ_EXIT_IRQS_DISABLED > > diff --git a/arch/powerpc/kernel/dbell.c b/arch/powerpc/kernel/dbell.c > index 5712dd846263..f5e298a4c4c0 100644 > --- a/arch/powerpc/kernel/dbell.c > +++ b/arch/powerpc/kernel/dbell.c > @@ -31,7 +31,7 @@ DEFINE_INTERRUPT_HANDLER_ASYNC(doorbell_exception) > do_hard_irq_enable(); > > kvmppc_clear_host_ipi(smp_processor_id()); > - __this_cpu_inc(irq_stat.doorbell_irqs); > + inc_irq_stat(DOORBELL); > > smp_ipi_demux_relaxed(); /* already performed the barrier */ > > diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c > index f33df5e5c23f..e67a18f62142 100644 > --- a/arch/powerpc/kernel/irq.c > +++ b/arch/powerpc/kernel/irq.c > @@ -84,79 +84,57 @@ u32 tau_interrupts(unsigned long cpu); > #endif > #endif /* CONFIG_PPC32 */ > > +struct irq_stat_info { > + const char *symbol; > + const char *text; > +}; > + > +#define ISE(idx, sym, txt)[IRQ_COUNT_##idx] = { .symbol = sym, .text = txt} > + > +static struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] __ro_after_init = { > + ISE(LOC_TIMER, "LOC", " Local timer interrupts for timer event device\n"), > + ISE(BCT_TIMER, "BCT", " Broadcast timer interrupts for timer event device\n"), > + ISE(OTHER_TIMER, "LOC", " Local timer interrupts for others\n"), > + ISE(SPURIOUS, "SPU", " Spurious interrupts\n"), > + ISE(PMI, "PMI", " Performance monitoring interrupts\n"), > + ISE(MCE, "MCE", " Machine check exceptions\n"), > + ISE(NMI_SRESET, "NMI", " System Reset interrupts\n"), > +#ifdef CONFIG_PPC_WATCHDOG > + ISE(WATCHDOG, "WDG", " Watchdog soft-NMI interrupts\n"), > +#endif > +#ifdef CONFIG_PPC_DOORBELL > + ISE(DOORBELL, "DBL", " Doorbell interrupts\n"), > +#endif > +}; > + > int arch_show_interrupts(struct seq_file *p, int prec) > { > - int j; > + const struct irq_stat_info *info = irq_stat_info; > + > + for (unsigned int i = 0; i < ARRAY_SIZE(irq_stat_info); i++, info++) { > + seq_printf(p, "%*s:", prec, info->symbol); > + irq_proc_emit_counts(p, &irq_stat.counts[i]); > + seq_puts(p, info->text); > + } > > #if defined(CONFIG_PPC32) && defined(CONFIG_TAU_INT) > if (tau_initialized) { > + int j; > seq_printf(p, "%*s:", prec, "TAU"); > for_each_online_cpu(j) > seq_put_decimal_ull_width(p, " ", tau_interrupts(j), 10); > seq_puts(p, " PowerPC Thermal Assist (cpu temp)\n"); > } > #endif /* CONFIG_PPC32 && CONFIG_TAU_INT */ > - > - seq_printf(p, "%*s:", prec, "LOC"); > - for_each_online_cpu(j) > - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).timer_irqs_event, 10); > - seq_printf(p, " Local timer interrupts for timer event device\n"); > - > - seq_printf(p, "%*s:", prec, "BCT"); > - for_each_online_cpu(j) > - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).broadcast_irqs_event, 10); > - seq_printf(p, " Broadcast timer interrupts for timer event device\n"); > - > - seq_printf(p, "%*s:", prec, "LOC"); > - for_each_online_cpu(j) > - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).timer_irqs_others, 10); > - seq_printf(p, " Local timer interrupts for others\n"); > - > - seq_printf(p, "%*s:", prec, "SPU"); > - for_each_online_cpu(j) > - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).spurious_irqs, 10); > - seq_printf(p, " Spurious interrupts\n"); > - > - seq_printf(p, "%*s:", prec, "PMI"); > - for_each_online_cpu(j) > - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).pmu_irqs, 10); > - seq_printf(p, " Performance monitoring interrupts\n"); > - > - seq_printf(p, "%*s:", prec, "MCE"); > - for_each_online_cpu(j) > - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).mce_exceptions, 10); > - seq_printf(p, " Machine check exceptions\n"); > - > #ifdef CONFIG_PPC_BOOK3S_64 > if (cpu_has_feature(CPU_FTR_HVMODE)) { > + int j; > seq_printf(p, "%*s:", prec, "HMI"); > for_each_online_cpu(j) > seq_put_decimal_ull_width(p, " ", paca_ptrs[j]->hmi_irqs, 10); > seq_printf(p, " Hypervisor Maintenance Interrupts\n"); > } > #endif > - > - seq_printf(p, "%*s:", prec, "NMI"); > - for_each_online_cpu(j) > - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).sreset_irqs, 10); > - seq_printf(p, " System Reset interrupts\n"); > - > -#ifdef CONFIG_PPC_WATCHDOG > - seq_printf(p, "%*s:", prec, "WDG"); > - for_each_online_cpu(j) > - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).soft_nmi_irqs, 10); > - seq_printf(p, " Watchdog soft-NMI interrupts\n"); > -#endif > - > -#ifdef CONFIG_PPC_DOORBELL > - if (cpu_has_feature(CPU_FTR_DBELL)) { > - seq_printf(p, "%*s:", prec, "DBL"); > - for_each_online_cpu(j) > - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).doorbell_irqs, 10); > - seq_printf(p, " Doorbell interrupts\n"); > - } > -#endif > - > return 0; > } > > @@ -165,24 +143,15 @@ int arch_show_interrupts(struct seq_file *p, int prec) > */ > u64 arch_irq_stat_cpu(unsigned int cpu) > { > - u64 sum = per_cpu(irq_stat, cpu).timer_irqs_event; > + irq_cpustat_t *p = per_cpu_ptr(&irq_stat, cpu); > + u64 sum = 0; > + > + for (unsigned int i = 0; i < ARRAY_SIZE(irq_stat_info); i++) > + sum += p->counts[i]; > > - sum += per_cpu(irq_stat, cpu).broadcast_irqs_event; > - sum += per_cpu(irq_stat, cpu).pmu_irqs; > - sum += per_cpu(irq_stat, cpu).mce_exceptions; > - sum += per_cpu(irq_stat, cpu).spurious_irqs; > - sum += per_cpu(irq_stat, cpu).timer_irqs_others; > #ifdef CONFIG_PPC_BOOK3S_64 > sum += paca_ptrs[cpu]->hmi_irqs; > #endif > - sum += per_cpu(irq_stat, cpu).sreset_irqs; > -#ifdef CONFIG_PPC_WATCHDOG > - sum += per_cpu(irq_stat, cpu).soft_nmi_irqs; > -#endif > -#ifdef CONFIG_PPC_DOORBELL > - sum += per_cpu(irq_stat, cpu).doorbell_irqs; > -#endif > - > return sum; > } > > @@ -248,7 +217,7 @@ static void __do_irq(struct pt_regs *regs, unsigned long oldsp) > > /* And finally process it */ > if (unlikely(!irq)) > - __this_cpu_inc(irq_stat.spurious_irqs); > + inc_irq_stat(SPURIOUS); > else > generic_handle_irq(irq); > > diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c > index 4bbeb8644d3d..44da7be36199 100644 > --- a/arch/powerpc/kernel/time.c > +++ b/arch/powerpc/kernel/time.c > @@ -572,13 +572,13 @@ DEFINE_INTERRUPT_HANDLER_ASYNC(timer_interrupt) > now = get_tb(); > if (now >= *next_tb) { > evt->event_handler(evt); > - __this_cpu_inc(irq_stat.timer_irqs_event); > + inc_irq_stat(LOC_TIMER); > } else { > now = *next_tb - now; > if (now > decrementer_max) > now = decrementer_max; > set_dec_or_work(now); > - __this_cpu_inc(irq_stat.timer_irqs_others); > + inc_irq_stat(OTHER_TIMER); > } > > trace_timer_interrupt_exit(regs); > @@ -591,7 +591,7 @@ EXPORT_SYMBOL(timer_interrupt); > void timer_broadcast_interrupt(void) > { > tick_receive_broadcast(); > - __this_cpu_inc(irq_stat.broadcast_irqs_event); > + inc_irq_stat(BCT_TIMER); > } > #endif > > diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c > index cb8e9357383e..a8f15154bd9a 100644 > --- a/arch/powerpc/kernel/traps.c > +++ b/arch/powerpc/kernel/traps.c > @@ -459,8 +459,7 @@ DEFINE_INTERRUPT_HANDLER_NMI(system_reset_exception) > } > > hv_nmi_check_nonrecoverable(regs); > - > - __this_cpu_inc(irq_stat.sreset_irqs); > + inc_irq_stat(NMI_SRESET); > > /* See if any machine dependent calls */ > if (ppc_md.system_reset_exception) { > @@ -817,7 +816,7 @@ static void __machine_check_exception(struct pt_regs *regs) > { > int recover = 0; > > - __this_cpu_inc(irq_stat.mce_exceptions); > + inc_irq_stat(MCE); > > add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); > > @@ -1932,8 +1931,7 @@ DEFINE_INTERRUPT_HANDLER(vsx_unavailable_tm) > DECLARE_INTERRUPT_HANDLER_NMI(performance_monitor_exception_nmi); > DEFINE_INTERRUPT_HANDLER_NMI(performance_monitor_exception_nmi) > { > - __this_cpu_inc(irq_stat.pmu_irqs); > - > + inc_irq_stat(PMI); > perf_irq(regs); > > return 0; > @@ -1943,8 +1941,7 @@ DEFINE_INTERRUPT_HANDLER_NMI(performance_monitor_exception_nmi) > DECLARE_INTERRUPT_HANDLER_ASYNC(performance_monitor_exception_async); > DEFINE_INTERRUPT_HANDLER_ASYNC(performance_monitor_exception_async) > { > - __this_cpu_inc(irq_stat.pmu_irqs); > - > + inc_irq_stat(PMI); > perf_irq(regs); > } > > diff --git a/arch/powerpc/kernel/watchdog.c b/arch/powerpc/kernel/watchdog.c > index 764001deb060..f516eeccc9f6 100644 > --- a/arch/powerpc/kernel/watchdog.c > +++ b/arch/powerpc/kernel/watchdog.c > @@ -381,7 +381,7 @@ DEFINE_INTERRUPT_HANDLER_NMI(soft_nmi_interrupt) > if (!cpumask_test_cpu(cpu, &wd_cpus_enabled)) > return 0; > > - __this_cpu_inc(irq_stat.soft_nmi_irqs); > + inc_irq_stat(WATCHDOG); > > tb = get_tb(); > if (tb - per_cpu(wd_timer_tb, cpu) >= wd_panic_timeout_tb) { ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 3/3] powerpc/irq: Suppress unlikely interrupt stats by default 2026-05-23 17:40 [PATCH 0/3] powerpc/irq: Use optimizations for /proc/interrupts Shrikanth Hegde 2026-05-23 17:40 ` [PATCH 1/3] powerpc/irq: Move __softirq_pending out of irq_stat Shrikanth Hegde 2026-05-23 17:40 ` [PATCH 2/3] powerpc/irq: Make irqstats array based Shrikanth Hegde @ 2026-05-23 17:40 ` Shrikanth Hegde 2026-05-29 7:51 ` Christophe Leroy (CS GROUP) 2 siblings, 1 reply; 8+ messages in thread From: Shrikanth Hegde @ 2026-05-23 17:40 UTC (permalink / raw) To: maddy, linuxppc-dev, tglx; +Cc: sshegde, christophe.leroy, linux-kernel Some interrupts are always zero and that is expected since they occur very rarely and are mostly error indications. Don't print them by default. "MCE" - "Machine check exceptions" "NMI" - "System Reset interrupts" Print them if they occur once. Maintain a bitmap to know which interrupts are to be printed. Time taken to read /proc/interrupts 1000 times. Base and v6 details can be found in cover-letter. Base : 103us v6 : 63us v6+patch 1+2 : 57us v6+patch 1+2+3 : 54us Patch 3 shows an additional 5% gain compared to patch 1+2. So it does make sense to print them only if they are ever set. Note: Since /proc/interrupts depend on kconfig and arch dependent, userspace tools don't make explicit assumptions. Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com> --- arch/powerpc/include/asm/hardirq.h | 1 + arch/powerpc/kernel/irq.c | 37 +++++++++++++++++++++++++++--- arch/powerpc/kernel/traps.c | 4 ++-- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/include/asm/hardirq.h b/arch/powerpc/include/asm/hardirq.h index 38098e35b241..be6cd5aab016 100644 --- a/arch/powerpc/include/asm/hardirq.h +++ b/arch/powerpc/include/asm/hardirq.h @@ -31,6 +31,7 @@ DECLARE_PER_CPU(unsigned int, __softirq_pending); #define local_softirq_pending_ref __softirq_pending #define inc_irq_stat(index) __this_cpu_inc(irq_stat.counts[IRQ_COUNT_##index]) +void inc_irq_stat_and_enable(enum irq_stat_counts which); #define __ARCH_IRQ_STAT #define __ARCH_IRQ_EXIT_IRQS_DISABLED diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index e67a18f62142..048ddfa66fc4 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -87,9 +87,13 @@ u32 tau_interrupts(unsigned long cpu); struct irq_stat_info { const char *symbol; const char *text; + int skip; }; -#define ISE(idx, sym, txt)[IRQ_COUNT_##idx] = { .symbol = sym, .text = txt} +/* ISE - IRQ STAT ENABLED, ISC - IRQ STAT CONDITIONAL */ +#define ISE(idx, sym, txt)[IRQ_COUNT_##idx] = { .symbol = sym, .text = txt, .skip = 0} +#define ISC(idx, sym, txt)[IRQ_COUNT_##idx] = { .symbol = sym, .text = txt, .skip = 1} + static struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] __ro_after_init = { ISE(LOC_TIMER, "LOC", " Local timer interrupts for timer event device\n"), @@ -97,8 +101,8 @@ static struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] __ro_after_init = { ISE(OTHER_TIMER, "LOC", " Local timer interrupts for others\n"), ISE(SPURIOUS, "SPU", " Spurious interrupts\n"), ISE(PMI, "PMI", " Performance monitoring interrupts\n"), - ISE(MCE, "MCE", " Machine check exceptions\n"), - ISE(NMI_SRESET, "NMI", " System Reset interrupts\n"), + ISC(MCE, "MCE", " Machine check exceptions\n"), + ISC(NMI_SRESET, "NMI", " System Reset interrupts\n"), #ifdef CONFIG_PPC_WATCHDOG ISE(WATCHDOG, "WDG", " Watchdog soft-NMI interrupts\n"), #endif @@ -107,11 +111,25 @@ static struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] __ro_after_init = { #endif }; +/* + * Used for default disabled counters to increment the stats and to enable the + * entry for /proc/interrupts output. + */ +static DECLARE_BITMAP(irq_stat_count_show, IRQ_COUNT_MAX) __read_mostly; +void inc_irq_stat_and_enable(enum irq_stat_counts which) +{ + __this_cpu_inc(irq_stat.counts[which]); + set_bit(which, irq_stat_count_show); +} + 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 (!test_bit(i, irq_stat_count_show)) + continue; + seq_printf(p, "%*s:", prec, info->symbol); irq_proc_emit_counts(p, &irq_stat.counts[i]); seq_puts(p, info->text); @@ -138,6 +156,19 @@ int arch_show_interrupts(struct seq_file *p, int prec) return 0; } +static int __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 == 0) + set_bit(i, irq_stat_count_show); + } + + return 0; +} +late_initcall(irq_init_stats); + /* * /proc/stat helpers */ diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index a8f15154bd9a..3eacbd20fc80 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -459,7 +459,7 @@ DEFINE_INTERRUPT_HANDLER_NMI(system_reset_exception) } hv_nmi_check_nonrecoverable(regs); - inc_irq_stat(NMI_SRESET); + inc_irq_stat_and_enable(IRQ_COUNT_NMI_SRESET); /* See if any machine dependent calls */ if (ppc_md.system_reset_exception) { @@ -816,7 +816,7 @@ static void __machine_check_exception(struct pt_regs *regs) { int recover = 0; - inc_irq_stat(MCE); + inc_irq_stat_and_enable(IRQ_COUNT_MCE); add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); -- 2.47.3 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 3/3] powerpc/irq: Suppress unlikely interrupt stats by default 2026-05-23 17:40 ` [PATCH 3/3] powerpc/irq: Suppress unlikely interrupt stats by default Shrikanth Hegde @ 2026-05-29 7:51 ` Christophe Leroy (CS GROUP) 2026-05-29 8:09 ` Shrikanth Hegde 0 siblings, 1 reply; 8+ messages in thread From: Christophe Leroy (CS GROUP) @ 2026-05-29 7:51 UTC (permalink / raw) To: Shrikanth Hegde, maddy, linuxppc-dev, tglx; +Cc: linux-kernel Le 23/05/2026 à 19:40, Shrikanth Hegde a écrit : > Some interrupts are always zero and that is expected since they occur > very rarely and are mostly error indications. Don't print them by > default. > > "MCE" - "Machine check exceptions" > "NMI" - "System Reset interrupts" > > Print them if they occur once. Maintain a bitmap to know which > interrupts are to be printed. Is that bitmap needed at all ? Can't we just print them as soon as they are not zero ? > > Time taken to read /proc/interrupts 1000 times. > Base and v6 details can be found in cover-letter. > Base : 103us > v6 : 63us > v6+patch 1+2 : 57us > v6+patch 1+2+3 : 54us > > Patch 3 shows an additional 5% gain compared to patch 1+2. So it does > make sense to print them only if they are ever set. > > Note: Since /proc/interrupts depend on kconfig and arch dependent, > userspace tools don't make explicit assumptions. > > Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com> > --- > arch/powerpc/include/asm/hardirq.h | 1 + > arch/powerpc/kernel/irq.c | 37 +++++++++++++++++++++++++++--- > arch/powerpc/kernel/traps.c | 4 ++-- > 3 files changed, 37 insertions(+), 5 deletions(-) > > diff --git a/arch/powerpc/include/asm/hardirq.h b/arch/powerpc/include/asm/hardirq.h > index 38098e35b241..be6cd5aab016 100644 > --- a/arch/powerpc/include/asm/hardirq.h > +++ b/arch/powerpc/include/asm/hardirq.h > @@ -31,6 +31,7 @@ DECLARE_PER_CPU(unsigned int, __softirq_pending); > #define local_softirq_pending_ref __softirq_pending > > #define inc_irq_stat(index) __this_cpu_inc(irq_stat.counts[IRQ_COUNT_##index]) > +void inc_irq_stat_and_enable(enum irq_stat_counts which); > > #define __ARCH_IRQ_STAT > #define __ARCH_IRQ_EXIT_IRQS_DISABLED > diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c > index e67a18f62142..048ddfa66fc4 100644 > --- a/arch/powerpc/kernel/irq.c > +++ b/arch/powerpc/kernel/irq.c > @@ -87,9 +87,13 @@ u32 tau_interrupts(unsigned long cpu); > struct irq_stat_info { > const char *symbol; > const char *text; > + int skip; I'd call it 'optional' instead, and then during the print, if value 0 and optional then don't print. > }; > > -#define ISE(idx, sym, txt)[IRQ_COUNT_##idx] = { .symbol = sym, .text = txt} > +/* ISE - IRQ STAT ENABLED, ISC - IRQ STAT CONDITIONAL */ > +#define ISE(idx, sym, txt)[IRQ_COUNT_##idx] = { .symbol = sym, .text = txt, .skip = 0} > +#define ISC(idx, sym, txt)[IRQ_COUNT_##idx] = { .symbol = sym, .text = txt, .skip = 1} > + > > static struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] __ro_after_init = { > ISE(LOC_TIMER, "LOC", " Local timer interrupts for timer event device\n"), > @@ -97,8 +101,8 @@ static struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] __ro_after_init = { > ISE(OTHER_TIMER, "LOC", " Local timer interrupts for others\n"), > ISE(SPURIOUS, "SPU", " Spurious interrupts\n"), > ISE(PMI, "PMI", " Performance monitoring interrupts\n"), > - ISE(MCE, "MCE", " Machine check exceptions\n"), > - ISE(NMI_SRESET, "NMI", " System Reset interrupts\n"), > + ISC(MCE, "MCE", " Machine check exceptions\n"), > + ISC(NMI_SRESET, "NMI", " System Reset interrupts\n"), > #ifdef CONFIG_PPC_WATCHDOG > ISE(WATCHDOG, "WDG", " Watchdog soft-NMI interrupts\n"), > #endif > @@ -107,11 +111,25 @@ static struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] __ro_after_init = { > #endif > }; > > +/* > + * Used for default disabled counters to increment the stats and to enable the > + * entry for /proc/interrupts output. > + */ > +static DECLARE_BITMAP(irq_stat_count_show, IRQ_COUNT_MAX) __read_mostly; > +void inc_irq_stat_and_enable(enum irq_stat_counts which) > +{ > + __this_cpu_inc(irq_stat.counts[which]); > + set_bit(which, irq_stat_count_show); > +} > + > 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 (!test_bit(i, irq_stat_count_show)) > + continue; > + > seq_printf(p, "%*s:", prec, info->symbol); > irq_proc_emit_counts(p, &irq_stat.counts[i]); > seq_puts(p, info->text); > @@ -138,6 +156,19 @@ int arch_show_interrupts(struct seq_file *p, int prec) > return 0; > } > > +static int __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 == 0) > + set_bit(i, irq_stat_count_show); > + } > + > + return 0; > +} > +late_initcall(irq_init_stats); > + > /* > * /proc/stat helpers > */ > diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c > index a8f15154bd9a..3eacbd20fc80 100644 > --- a/arch/powerpc/kernel/traps.c > +++ b/arch/powerpc/kernel/traps.c > @@ -459,7 +459,7 @@ DEFINE_INTERRUPT_HANDLER_NMI(system_reset_exception) > } > > hv_nmi_check_nonrecoverable(regs); > - inc_irq_stat(NMI_SRESET); > + inc_irq_stat_and_enable(IRQ_COUNT_NMI_SRESET); > > /* See if any machine dependent calls */ > if (ppc_md.system_reset_exception) { > @@ -816,7 +816,7 @@ static void __machine_check_exception(struct pt_regs *regs) > { > int recover = 0; > > - inc_irq_stat(MCE); > + inc_irq_stat_and_enable(IRQ_COUNT_MCE); > > add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 3/3] powerpc/irq: Suppress unlikely interrupt stats by default 2026-05-29 7:51 ` Christophe Leroy (CS GROUP) @ 2026-05-29 8:09 ` Shrikanth Hegde 0 siblings, 0 replies; 8+ messages in thread From: Shrikanth Hegde @ 2026-05-29 8:09 UTC (permalink / raw) To: Christophe Leroy (CS GROUP), maddy, linuxppc-dev, tglx; +Cc: linux-kernel Hi Christophe. Thanks for reviewing the patch series. On 5/29/26 1:21 PM, Christophe Leroy (CS GROUP) wrote: > > > Le 23/05/2026 à 19:40, Shrikanth Hegde a écrit : >> Some interrupts are always zero and that is expected since they occur >> very rarely and are mostly error indications. Don't print them by >> default. >> >> "MCE" - "Machine check exceptions" >> "NMI" - "System Reset interrupts" >> >> Print them if they occur once. Maintain a bitmap to know which >> interrupts are to be printed. > > Is that bitmap needed at all ? Can't we just print them as soon as they > are not zero ? > I think Yes. Otherwise need to traverse all the per_cpu count and to see if it is zero or not. >> >> Time taken to read /proc/interrupts 1000 times. >> Base and v6 details can be found in cover-letter. >> Base : 103us >> v6 : 63us >> v6+patch 1+2 : 57us >> v6+patch 1+2+3 : 54us >> >> Patch 3 shows an additional 5% gain compared to patch 1+2. So it does >> make sense to print them only if they are ever set. >> >> Note: Since /proc/interrupts depend on kconfig and arch dependent, >> userspace tools don't make explicit assumptions. >> >> Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com> >> --- >> arch/powerpc/include/asm/hardirq.h | 1 + >> arch/powerpc/kernel/irq.c | 37 +++++++++++++++++++++++++++--- >> arch/powerpc/kernel/traps.c | 4 ++-- >> 3 files changed, 37 insertions(+), 5 deletions(-) >> >> diff --git a/arch/powerpc/include/asm/hardirq.h b/arch/powerpc/ >> include/asm/hardirq.h >> index 38098e35b241..be6cd5aab016 100644 >> --- a/arch/powerpc/include/asm/hardirq.h >> +++ b/arch/powerpc/include/asm/hardirq.h >> @@ -31,6 +31,7 @@ DECLARE_PER_CPU(unsigned int, __softirq_pending); >> #define local_softirq_pending_ref __softirq_pending >> #define inc_irq_stat(index) >> __this_cpu_inc(irq_stat.counts[IRQ_COUNT_##index]) >> +void inc_irq_stat_and_enable(enum irq_stat_counts which); >> #define __ARCH_IRQ_STAT >> #define __ARCH_IRQ_EXIT_IRQS_DISABLED >> diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c >> index e67a18f62142..048ddfa66fc4 100644 >> --- a/arch/powerpc/kernel/irq.c >> +++ b/arch/powerpc/kernel/irq.c >> @@ -87,9 +87,13 @@ u32 tau_interrupts(unsigned long cpu); >> struct irq_stat_info { >> const char *symbol; >> const char *text; >> + int skip; > > I'd call it 'optional' instead, and then during the print, if value 0 > and optional then don't print. > Ok. Makes sense. Will do it in v2. Will add below way if (!(info->optional && test_bit(i, irq_stat_count_show))) continue; >> }; >> -#define ISE(idx, sym, txt)[IRQ_COUNT_##idx] = { .symbol = sym, .text >> = txt} >> +/* ISE - IRQ STAT ENABLED, ISC - IRQ STAT CONDITIONAL */ >> +#define ISE(idx, sym, txt)[IRQ_COUNT_##idx] = { .symbol = sym, .text >> = txt, .skip = 0} >> +#define ISC(idx, sym, txt)[IRQ_COUNT_##idx] = { .symbol = sym, .text >> = txt, .skip = 1} >> + >> static struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] >> __ro_after_init = { >> ISE(LOC_TIMER, "LOC", " Local timer interrupts for timer >> event device\n"), >> @@ -97,8 +101,8 @@ static struct irq_stat_info >> irq_stat_info[IRQ_COUNT_MAX] __ro_after_init = { >> ISE(OTHER_TIMER, "LOC", " Local timer interrupts for >> others\n"), >> ISE(SPURIOUS, "SPU", " Spurious interrupts\n"), >> ISE(PMI, "PMI", " Performance monitoring interrupts\n"), >> - ISE(MCE, "MCE", " Machine check exceptions\n"), >> - ISE(NMI_SRESET, "NMI", " System Reset interrupts\n"), >> + ISC(MCE, "MCE", " Machine check exceptions\n"), >> + ISC(NMI_SRESET, "NMI", " System Reset interrupts\n"), >> #ifdef CONFIG_PPC_WATCHDOG >> ISE(WATCHDOG, "WDG", " Watchdog soft-NMI interrupts\n"), >> #endif >> @@ -107,11 +111,25 @@ static struct irq_stat_info >> irq_stat_info[IRQ_COUNT_MAX] __ro_after_init = { >> #endif >> }; >> +/* >> + * Used for default disabled counters to increment the stats and to >> enable the >> + * entry for /proc/interrupts output. >> + */ >> +static DECLARE_BITMAP(irq_stat_count_show, IRQ_COUNT_MAX) __read_mostly; >> +void inc_irq_stat_and_enable(enum irq_stat_counts which) >> +{ >> + __this_cpu_inc(irq_stat.counts[which]); >> + set_bit(which, irq_stat_count_show); >> +} >> + >> 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 (!test_bit(i, irq_stat_count_show)) >> + continue; >> + >> seq_printf(p, "%*s:", prec, info->symbol); >> irq_proc_emit_counts(p, &irq_stat.counts[i]); >> seq_puts(p, info->text); >> @@ -138,6 +156,19 @@ int arch_show_interrupts(struct seq_file *p, int >> prec) >> return 0; >> } >> +static int __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 == 0) >> + set_bit(i, irq_stat_count_show); >> + } >> + >> + return 0; >> +} >> +late_initcall(irq_init_stats); >> + >> /* >> * /proc/stat helpers >> */ >> diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c >> index a8f15154bd9a..3eacbd20fc80 100644 >> --- a/arch/powerpc/kernel/traps.c >> +++ b/arch/powerpc/kernel/traps.c >> @@ -459,7 +459,7 @@ DEFINE_INTERRUPT_HANDLER_NMI(system_reset_exception) >> } >> hv_nmi_check_nonrecoverable(regs); >> - inc_irq_stat(NMI_SRESET); >> + inc_irq_stat_and_enable(IRQ_COUNT_NMI_SRESET); >> /* See if any machine dependent calls */ >> if (ppc_md.system_reset_exception) { >> @@ -816,7 +816,7 @@ static void __machine_check_exception(struct >> pt_regs *regs) >> { >> int recover = 0; >> - inc_irq_stat(MCE); >> + inc_irq_stat_and_enable(IRQ_COUNT_MCE); >> add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); > ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-05-29 8:09 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-23 17:40 [PATCH 0/3] powerpc/irq: Use optimizations for /proc/interrupts Shrikanth Hegde 2026-05-23 17:40 ` [PATCH 1/3] powerpc/irq: Move __softirq_pending out of irq_stat Shrikanth Hegde 2026-05-29 7:43 ` Christophe Leroy (CS GROUP) 2026-05-23 17:40 ` [PATCH 2/3] powerpc/irq: Make irqstats array based Shrikanth Hegde 2026-05-29 7:46 ` Christophe Leroy (CS GROUP) 2026-05-23 17:40 ` [PATCH 3/3] powerpc/irq: Suppress unlikely interrupt stats by default Shrikanth Hegde 2026-05-29 7:51 ` Christophe Leroy (CS GROUP) 2026-05-29 8:09 ` Shrikanth Hegde
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox