Improve /proc/interrupts further
Subsystems (11)
Thomas Gleixner
maintainerv5 cover Improve /proc/interrupts further
This is a follow up to v4 which can be found here:
https://lore.kernel.org/[email protected]
The v1 cover letter contains a full analysis, explanation and numbers:
https://lore.kernel.org/[email protected]
TLDR:
- The performance of reading of /proc/interrupts has been improved piecewise over the years, but most of the low hanging fruit has been left on the table.
Changes vs. V4:
- Moved the header printing ahead of the architecture and update comment
- Dmitry
- Fix the gdbscript so it works with a bitmap larger than one entry and bring show_irq_err_count() back as it's required by MIPS
- Removed unused variable and resort hunks so it's bisectable.
Delta patch against v4 is below.
The series applies on top of v7.0-rc3 and is also available via git:
git://git.kernel.org/pub/scm/linux/kernel/git/tglx/devel.git irq-proc-v5
Thanks,
tglx
---
kernel/irq/manage.c +0 -1
@@ -2066,7 +2066,6 @@ static const void *__cleanup_nmi(unsigned int irq, struct irq_desc *desc)const void *free_nmi (unsigned int irq,void *dev_id) {struct irq_desc *desc =irq_to_desc (irq); -void *ret;if (!desc ||WARN_ON (!irq_is_nmi (desc)))return NULL ;
kernel/irq/proc.c +7 -7
@@ -540,10 +540,7 @@ static int irq_seq_show(struct seq_file *p, void *v)struct irq_desc *desc = v;struct irqaction *action; -if (desc ==ARCH_PROC_IRQDESC ) -return arch_show_interrupts (p, constr->num_prec); - -/* print header for the first interrupt indicated by !p>private */ +/* Print header for the first interrupt? */ if (constr->print_header) {unsigned int cpu;
@@ -554,6 +551,9 @@ static int irq_seq_show(struct seq_file *p, void *v) constr->print_header = false; } +if (desc ==ARCH_PROC_IRQDESC ) +return arch_show_interrupts (p, constr->num_prec); +seq_put_decimal_ull_width (p,"" ,irq_desc_get_irq (desc), constr->num_prec);seq_putc (p,':' );
@@ -617,9 +617,9 @@ static void *irq_seq_next_desc(loff_t *pos)if (desc) { *pos =irq_desc_get_irq (desc);/* -* If valid for output try to acquire a reference count -* on the descriptor so that it can't be freed after -* dropping RCU read lock on return. +* If valid for output then try to acquire a reference +* count on the descriptor so that it can't be freed +* after dropping RCU read lock on return. */ if (irq_settings_proc_valid (desc) &&irq_desc_get_ref (desc))return desc;
scripts/gdb/linux/interrupts.py +10 -3
@@ -103,12 +103,12 @@ def x86_show_interupts(prec): info_type = gdb.lookup_type('struct irq_stat_info') info = gdb.parse_and_eval('irq_stat_info') bitmap = gdb.parse_and_eval('irq_stat_count_show') - nbits = 8 * int(bitmap.type.sizeof) + bitsperlong = 8 * int(bitmap.type.target().sizeof) text = "" for idx in range(int(info.type.sizeof / info_type.sizeof)): - show = bitmap[idx / nbits] - if not show & 1 << (idx % nbits): + show = bitmap[int(idx / bitsperlong)] + if not show & 1 << int(idx % bitsperlong): continue pfx = info[idx]['symbol'].string() desc = info[idx]['text'].string()
@@ -145,6 +145,13 @@ def aarch64_show_interrupts(prec): text += "%*s: %10lu\n" % (prec, "ERR", gdb.parse_and_eval("irq_err_count")) return text +def show_irq_err_count(prec): + cnt = utils.gdb_eval_or_none("irq_err_count") + text = "" + if cnt is not None: + text += "%*s: %10u\n" % (prec, "ERR", cnt['counter']) + return text + def arch_show_interrupts(prec): text = "" if utils.is_target_arch("x86"):
Thomas Gleixner
maintainerPATCH v5 1/15 x86/irq: Optimize interrupts decimals printing
From: Dmitry Ilvokhin <[email protected]>
Monitoring tools periodically scan /proc/interrupts to export metrics as a timeseries for future analysis and investigation.
In large fleets, /proc/interrupts is polled (often every few seconds) on every machine. The cumulative overhead adds up quickly across thousands of nodes, so reducing the cost of generating these stats does have a measurable operational impact. With the ongoing trend toward higher core counts per machine, this cost becomes even more noticeable over time, since interrupt counters are per-CPU. In Meta's fleet, we have observed this overhead at scale.
Although a binary /proc interface would be a better long-term solution due to lower formatting (kernel side) and parsing (userspace side) overhead, the text interface will remain in use for some time, even if better solutions will be available. Optimizing the /proc/interrupts printing code is therefore still beneficial.
Function seq_printf() supports rich format string for decimals printing, but it doesn't required for printing /proc/interrupts per CPU counters, seq_put_decimal_ull_width() function can be used instead to print per CPU counters, because very limited formatting is required for this case. Similar optimization idea is already used in show_interrupts().
As a side effect this aligns the x86 decriptions with the generic interrupts event descriptions.
Performance counter stats (truncated) for 'sh -c cat /proc/interrupts
Before:
3.42 msec task-clock # 0.802 CPUs utilized ( +- 0.05% )
1 context-switches # 291.991 /sec ( +- 0.74% )
0 cpu-migrations # 0.000 /sec
343 page-faults # 100.153 K/sec ( +- 0.01% )
8,932,242 instructions # 1.66 insn per cycle ( +- 0.34% )
5,374,427 cycles # 1.569 GHz ( +- 0.04% )
1,483,154 branches # 433.068 M/sec ( +- 0.22% )
28,768 branch-misses # 1.94% of all branches ( +- 0.31% )0.00427182 +- 0.00000215 seconds time elapsed ( +- 0.05% )
After:
2.39 msec task-clock # 0.796 CPUs utilized ( +- 0.06% )
1 context-switches # 418.541 /sec ( +- 0.70% )
0 cpu-migrations # 0.000 /sec
343 page-faults # 143.560 K/sec ( +- 0.01% )
7,020,982 instructions # 1.30 insn per cycle ( +- 0.52% )
5,397,266 cycles # 2.259 GHz ( +- 0.06% )
1,569,648 branches # 656.962 M/sec ( +- 0.08% )
25,419 branch-misses # 1.62% of all branches ( +- 0.72% )0.00299996 +- 0.00000206 seconds time elapsed ( +- 0.07% )
Relative speed up in time elapsed is around 29%.
[ tglx: Fixed it up so it applies to current mainline ]
---
Changes v2:
- Expanded commit message: add more rationale for the proposed change.
- Renamed helper put_spaced_decimal() -> put_decimal() primarely to make checkpatch.pl --strict pass.
arch/x86/kernel/irq.c | 112 ++++++++++++++++++++++++++------------------------ 1 file changed, 59 insertions(+), 53 deletions(-)
arch/x86/kernel/irq.c +59 -53
@@ -62,6 +62,18 @@ 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 ; + +seq_put_decimal_ull_width (p, delimiter, num, width); +} +#define irq_stats (x) (&per_cpu(irq_stat, x))/* * /proc/interrupts printing for arch specific interrupts
@@ -70,103 +82,101 @@ int arch_show_interrupts(struct seq_file {int j; -seq_printf (p,"%*s: " , prec,"NMI" ); +seq_printf (p,"%*s:" , prec,"NMI" );for_each_online_cpu (j) -seq_printf (p,"%10u " ,irq_stats (j)->__nmi_count); +put_decimal (p,irq_stats (j)->__nmi_count);seq_puts (p," Non-maskable interrupts\n" );#ifdef CONFIG_X86_LOCAL_APIC -seq_printf (p,"%*s: " , prec,"LOC" ); +seq_printf (p,"%*s:" , prec,"LOC" );for_each_online_cpu (j) -seq_printf (p,"%10u " ,irq_stats (j)->apic_timer_irqs); +put_decimal (p,irq_stats (j)->apic_timer_irqs);seq_puts (p," Local timer interrupts\n" ); -seq_printf (p,"%*s: " , prec,"SPU" ); +seq_printf (p,"%*s:" , prec,"SPU" );for_each_online_cpu (j) -seq_printf (p,"%10u " ,irq_stats (j)->irq_spurious_count); +put_decimal (p,irq_stats (j)->irq_spurious_count);seq_puts (p," Spurious interrupts\n" ); -seq_printf (p,"%*s: " , prec,"PMI" ); +seq_printf (p,"%*s:" , prec,"PMI" );for_each_online_cpu (j) -seq_printf (p,"%10u " ,irq_stats (j)->apic_perf_irqs); +put_decimal (p,irq_stats (j)->apic_perf_irqs);seq_puts (p," Performance monitoring interrupts\n" ); -seq_printf (p,"%*s: " , prec,"IWI" ); +seq_printf (p,"%*s:" , prec,"IWI" );for_each_online_cpu (j) -seq_printf (p,"%10u " ,irq_stats (j)->apic_irq_work_irqs); +put_decimal (p,irq_stats (j)->apic_irq_work_irqs);seq_puts (p," IRQ work interrupts\n" ); -seq_printf (p,"%*s: " , prec,"RTR" ); +seq_printf (p,"%*s:" , prec,"RTR" );for_each_online_cpu (j) -seq_printf (p,"%10u " ,irq_stats (j)->icr_read_retry_count); +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" ); +seq_printf (p,"%*s:" , prec,"PLT" );for_each_online_cpu (j) -seq_printf (p,"%10u " ,irq_stats (j)->x86_platform_ipis); +put_decimal (p,irq_stats (j)->x86_platform_ipis);seq_puts (p," Platform interrupts\n" ); }#endif #ifdef CONFIG_SMP -seq_printf (p,"%*s: " , prec,"RES" ); +seq_printf (p,"%*s:" , prec,"RES" );for_each_online_cpu (j) -seq_printf (p,"%10u " ,irq_stats (j)->irq_resched_count); +put_decimal (p,irq_stats (j)->irq_resched_count);seq_puts (p," Rescheduling interrupts\n" ); -seq_printf (p,"%*s: " , prec,"CAL" ); +seq_printf (p,"%*s:" , prec,"CAL" );for_each_online_cpu (j) -seq_printf (p,"%10u " ,irq_stats (j)->irq_call_count); +put_decimal (p,irq_stats (j)->irq_call_count);seq_puts (p," Function call interrupts\n" ); -seq_printf (p,"%*s: " , prec,"TLB" ); +seq_printf (p,"%*s:" , prec,"TLB" );for_each_online_cpu (j) -seq_printf (p,"%10u " ,irq_stats (j)->irq_tlb_count); +put_decimal (p,irq_stats (j)->irq_tlb_count);seq_puts (p," TLB shootdowns\n" );#endif #ifdef CONFIG_X86_THERMAL_VECTOR -seq_printf (p,"%*s: " , prec,"TRM" ); +seq_printf (p,"%*s:" , prec,"TRM" );for_each_online_cpu (j) -seq_printf (p,"%10u " ,irq_stats (j)->irq_thermal_count); +put_decimal (p,irq_stats (j)->irq_thermal_count);seq_puts (p," Thermal event interrupts\n" );#endif #ifdef CONFIG_X86_MCE_THRESHOLD -seq_printf (p,"%*s: " , prec,"THR" ); +seq_printf (p,"%*s:" , prec,"THR" );for_each_online_cpu (j) -seq_printf (p,"%10u " ,irq_stats (j)->irq_threshold_count); +put_decimal (p,irq_stats (j)->irq_threshold_count);seq_puts (p," Threshold APIC interrupts\n" );#endif #ifdef CONFIG_X86_MCE_AMD -seq_printf (p,"%*s: " , prec,"DFR" ); +seq_printf (p,"%*s:" , prec,"DFR" );for_each_online_cpu (j) -seq_printf (p,"%10u " ,irq_stats (j)->irq_deferred_error_count); +put_decimal (p,irq_stats (j)->irq_deferred_error_count);seq_puts (p," Deferred Error APIC interrupts\n" );#endif #ifdef CONFIG_X86_MCE -seq_printf (p,"%*s: " , prec,"MCE" ); +seq_printf (p,"%*s:" , prec,"MCE" );for_each_online_cpu (j) -seq_printf (p,"%10u " ,per_cpu (mce_exception_count, j)); +put_decimal (p,per_cpu (mce_exception_count, j));seq_puts (p," Machine check exceptions\n" ); -seq_printf (p,"%*s: " , prec,"MCP" ); +seq_printf (p,"%*s:" , prec,"MCP" );for_each_online_cpu (j) -seq_printf (p,"%10u " ,per_cpu (mce_poll_count, j)); +put_decimal (p,per_cpu (mce_poll_count, j));seq_puts (p," 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" ); +seq_printf (p,"%*s:" , prec,"HYP" );for_each_online_cpu (j) -seq_printf (p,"%10u " , -irq_stats (j)->irq_hv_callback_count); +put_decimal (p,irq_stats (j)->irq_hv_callback_count);seq_puts (p," Hypervisor callback interrupts\n" ); }#endif #if IS_ENABLED (CONFIG_HYPERV )if (test_bit (HYPERV_REENLIGHTENMENT_VECTOR , system_vectors)) { -seq_printf (p,"%*s: " , prec,"HRE" ); +seq_printf (p,"%*s:" , prec,"HRE" );for_each_online_cpu (j) -seq_printf (p,"%10u " , -irq_stats (j)->irq_hv_reenlightenment_count); +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" ); +seq_printf (p,"%*s:" , prec,"HVS" );for_each_online_cpu (j) -seq_printf (p,"%10u " , -irq_stats (j)->hyperv_stimer0_count); +put_decimal (p,irq_stats (j)->hyperv_stimer0_count);seq_puts (p," Hyper-V stimer0 interrupts\n" ); }#endif
@@ -175,35 +185,31 @@ int arch_show_interrupts(struct seq_fileseq_printf (p,"%*s: %10u\n" , prec,"MIS" ,atomic_read (&irq_mis_count));#endif #if IS_ENABLED (CONFIG_KVM ) -seq_printf (p,"%*s: " , prec,"PIN" ); +seq_printf (p,"%*s:" , prec,"PIN" );for_each_online_cpu (j) -seq_printf (p,"%10u " ,irq_stats (j)->kvm_posted_intr_ipis); +put_decimal (p,irq_stats (j)->kvm_posted_intr_ipis);seq_puts (p," Posted-interrupt notification event\n" ); -seq_printf (p,"%*s: " , prec,"NPI" ); +seq_printf (p,"%*s:" , prec,"NPI" );for_each_online_cpu (j) -seq_printf (p,"%10u " , -irq_stats (j)->kvm_posted_intr_nested_ipis); +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" ); +seq_printf (p,"%*s:" , prec,"PIW" );for_each_online_cpu (j) -seq_printf (p,"%10u " , -irq_stats (j)->kvm_posted_intr_wakeup_ipis); +put_decimal (p,irq_stats (j)->kvm_posted_intr_wakeup_ipis);seq_puts (p," Posted-interrupt wakeup event\n" );#endif #ifdef CONFIG_GUEST_PERF_EVENTS -seq_printf (p,"%*s: " , prec,"VPMI" ); +seq_printf (p,"%*s:" , prec,"VPMI" );for_each_online_cpu (j) -seq_printf (p,"%10u " , -irq_stats (j)->perf_guest_mediated_pmis); +put_decimal (p,irq_stats (j)->perf_guest_mediated_pmis);seq_puts (p," Perf Guest Mediated PMI\n" );#endif #ifdef CONFIG_X86_POSTED_MSI -seq_printf (p,"%*s: " , prec,"PMN" ); +seq_printf (p,"%*s:" , prec,"PMN" );for_each_online_cpu (j) -seq_printf (p,"%10u " , -irq_stats (j)->posted_msi_notification_count); +put_decimal (p,irq_stats (j)->posted_msi_notification_count);seq_puts (p," Posted MSI notification event\n" );#endif return 0 ;
Thomas Gleixner
maintainerPATCH v5 2/15 genirq/proc: Avoid formatting zero counts in /proc/interrupts
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
A large portion of interrupt count entries are zero. There is no point in formatting the zero value as it is way cheeper to just emit a constant string.
Collect the number of consecutive zero counts and emit them in one go before a non-zero count and at the end of the line.
---
V2: Use sizeof() for ZSTR1_LEN - Dmitry
--- include/linux/interrupt.h | 1 + kernel/irq/proc.c | 42 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 5 deletions(-)
include/linux/interrupt.h +1 -0
@@ -864,6 +864,7 @@ static inline void init_irq_proc(void)struct seq_file ;int show_interrupts (struct seq_file *p,void *v);int arch_show_interrupts (struct seq_file *p,int prec); +void irq_proc_emit_counts (struct seq_file *p,unsigned int __percpu *cnts);extern int early_irq_init (void );extern int arch_probe_nr_irqs (void );
kernel/irq/proc.c +37 -5
54 lines not on this page · raw message
Thomas Gleixner
maintainerPATCH v5 3/15 genirq/proc: Utilize irq_desc::tot_count to avoid evaluation
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
Interrupts which are not marked per CPU increment not only the per CPU statistics, but also the accumulation counter irq_desc::tot_count.
Change the counter to type unsigned long so it does not produce sporadic zeros due to wrap arounds on 64-bit machines and do a quick check for non per CPU interrupts. If the counter is zero, then simply emit a full set of zero strings. That spares the evaluation of the per CPU counters completely for interrupts with zero events.
--- include/linux/irqdesc.h | 6 +++--- kernel/irq/proc.c | 11 ++++++++++- 2 files changed, 13 insertions(+), 4 deletions(-)
include/linux/irqdesc.h +3 -3
@@ -52,8 +52,8 @@ struct irq_redirect {* @depth: disable-depth, for nested irq_disable() calls * @wake_depth: enable depth, for multiple irq_set_irq_wake() callers * @tot_count: stats field for non-percpu irqs -* @irq_count: stats field to detect stalled irqs * @last_unhandled: aging timer for unhandled count +* @irq_count: stats field to detect stalled irqs * @irqs_unhandled: stats field for spurious unhandled interrupts * @threads_handled: stats field for deferred spurious detection of threaded handlers * @threads_handled_last: comparator field for deferred spurious detection of threaded handlers
@@ -87,9 +87,9 @@ struct irq_desc {unsigned int core_internal_state__do_not_mess_with_it;unsigned int depth;/* nested irq disables */ unsigned int wake_depth;/* nested wake enables */ -unsigned int tot_count; -unsigned int irq_count;/* For detecting broken IRQs */ +unsigned long tot_count;unsigned long last_unhandled;/* Aging timer for unhandled count */ +unsigned int irq_count;/* For detecting broken IRQs */ unsigned int irqs_unhandled;atomic_t threads_handled;int threads_handled_last;
kernel/irq/proc.c +10 -1
@@ -521,7 +521,16 @@ int show_interrupts(struct seq_file *p,return 0 ;seq_printf (p,"%*d:" , prec, i); -irq_proc_emit_counts (p, &desc->kstat_irqs->cnt); + +/* +* Always output per CPU interrupts. Output device interrupts 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)) +irq_proc_emit_counts (p, &desc->kstat_irqs->cnt); +else +irq_proc_emit_zero_counts (p,num_online_cpus ());seq_putc (p,' ' );guard (raw_spinlock_irq)(&desc->lock);
Thomas Gleixner
maintainerPATCH v5 4/15 x86/irq: Make irqstats array based
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
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.
---
V5: Move the inc and enable function to the next patch
Use COUNT and not VECTOR for the AMD deferred part
V4: Make irq_stats_init() a late initcall so that platform IPI and posted
vector initialization has been done
Supress AMD/HYGON specific vectors when CPU is from a different vendor
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/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 | 259 +++++++++++++----------------------- arch/x86/kernel/irq_work.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 27 files changed, 174 insertions(+), 233 deletions(-)
arch/x86/events/amd/core.c +1 -1
@@ -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;
arch/x86/events/amd/ibs.c +1 -1
@@ -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);
arch/x86/events/core.c +1 -1
@@ -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; }
arch/x86/events/intel/core.c +1 -1
@@ -3504,7 +3504,7 @@ static int handle_pmi_common(struct pt_rint 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
arch/x86/events/intel/knc.c +1 -1
@@ -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];
arch/x86/events/intel/p4.c +1 -1
@@ -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
arch/x86/events/zhaoxin/core.c +1 -1
@@ -373,7 +373,7 @@ static int zhaoxin_pmu_handle_irq(structelse 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
arch/x86/hyperv/hv_init.c +1 -1
@@ -219,7 +219,7 @@ static inline bool hv_reenlightenment_avDEFINE_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 ); }
arch/x86/include/asm/hardirq.h +47 -30
113 lines not on this page · raw message
arch/x86/include/asm/mce.h +0 -3
@@ -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 );
arch/x86/kernel/apic/apic.c +2 -2
@@ -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_inttrace_spurious_apic_entry (vector); -inc_irq_stat (irq_spurious_count); +inc_irq_stat (SPURIOUS );/* * If this is a spurious interrupt then do not acknowledge
arch/x86/kernel/apic/ipi.c +1 -1
@@ -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 ;
arch/x86/kernel/cpu/acrn.c +1 -1
@@ -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 ();
arch/x86/kernel/cpu/mce/amd.c +1 -1
@@ -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 ();
arch/x86/kernel/cpu/mce/core.c +2 -6
@@ -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 fstruct 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;
arch/x86/kernel/cpu/mce/threshold.c +1 -1
@@ -37,7 +37,7 @@ void (*mce_threshold_vector)(void) = defDEFINE_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 ();
arch/x86/kernel/cpu/mshyperv.c +2 -2
@@ -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 );
arch/x86/kernel/irq.c +95 -164
353 lines not on this page · raw message
arch/x86/kernel/irq_work.c +1 -1
@@ -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 ); }
arch/x86/kernel/kvm.c +1 -1
@@ -310,7 +310,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_kvm_asyncpapic_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);
arch/x86/kernel/nmi.c +2 -2
@@ -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);
arch/x86/kernel/smp.c +3 -3
@@ -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 ); }
arch/x86/mm/tlb.c +1 -1
@@ -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 ); }
arch/x86/xen/enlighten_hvm.c +1 -1
@@ -125,7 +125,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_xen_hvm_caif (xen_percpu_upcall)apic_eoi (); -inc_irq_stat (irq_hv_callback_count); +inc_irq_stat (HYPERVISOR_CALLBACK );xen_evtchn_do_upcall ();
arch/x86/xen/enlighten_pv.c +1 -1
@@ -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 ();
arch/x86/xen/smp.c +3 -3
@@ -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_intstatic 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 ; }
arch/x86/xen/smp_pv.c +1 -1
@@ -400,7 +400,7 @@ static void xen_pv_stop_other_cpus(int wstatic 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 ; }
Thomas Gleixner
maintainerPATCH v5 5/15 x86/irq: Suppress unlikely interrupt stats by default
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
Unlikely interrupt counters like the spurious vector and the synthetic APIC ICR read retry show up in /proc/interrupts with all counts 0 most of the time.
As these are events which should never happen, suppress them by default and enable them for output when they actually happen.
This requires a seperate bitmap as the description array is marked __ro_after_init. With that bitmap in place it becomes RO data.
---
V5: Move irq_stat_inc_and_enable() here
V4: Fix the bad idea of writing to __ro_after_init marked data
V3: New patch
--- arch/x86/include/asm/hardirq.h | 1 + arch/x86/kernel/apic/apic.c | 2 +- arch/x86/kernel/apic/ipi.c | 2 +- arch/x86/kernel/irq.c | 38 ++++++++++++++++++++++++++++---------- 4 files changed, 31 insertions(+), 12 deletions(-)
arch/x86/include/asm/hardirq.h +1 -0
@@ -68,6 +68,7 @@ DECLARE_PER_CPU_ALIGNED(struct pi_desc,#define __ARCH_IRQ_STAT#define inc_irq_stat (index) this_cpu_inc(irq_stat.counts[IRQ_COUNT_##index]) +void irq_stat_inc_and_enable (enum irq_stat_counts which);#ifdef CONFIG_X86_LOCAL_APIC #define inc_perf_irq_stat () inc_irq_stat(APIC_PERF)
arch/x86/kernel/apic/apic.c +1 -1
@@ -2108,7 +2108,7 @@ static noinline void handle_spurious_inttrace_spurious_apic_entry (vector); -inc_irq_stat (SPURIOUS ); +irq_stat_inc_and_enable (IRQ_COUNT_SPURIOUS );/* * If this is a spurious interrupt then do not acknowledge
arch/x86/kernel/apic/ipi.c +1 -1
@@ -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 ); +irq_stat_inc_and_enable (IRQ_COUNT_ICR_READ_RETRY );udelay (100 ); }return APIC_ICR_BUSY ;
arch/x86/kernel/irq.c +28 -10
@@ -69,19 +69,24 @@ struct irq_stat_info {const char *text; }; +#define DEFAULT_SUPPRESSED_VECTOR UINT_MAX +#define ISS (idx, sym, txt) [IRQ_COUNT_##idx] = { .symbol = sym, .text = txt }#define ITS (idx, sym, txt) [IRQ_COUNT_##idx] = \ { .skip_vector = idx## _VECTOR, .symbol = sym, .text = txt } -static struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX ]__ro_after_init = { +#define IDS (idx, sym, txt) [IRQ_COUNT_##idx] = \ + { .skip_vector = DEFAULT_SUPPRESSED_VECTOR, .symbol = sym, .text = txt } + +static const struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX ] = {ISS (NMI ,"NMI" ," Non-maskable interrupts\n" ), #ifdef CONFIG_X86_LOCAL_APICISS (APIC_TIMER ,"LOC" ," Local timer interrupts\n" ), -ISS (SPURIOUS ,"SPU" ," Spurious interrupts\n" ), +IDS (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" ), +IDS (ICR_READ_RETRY ,"RTR" ," APIC ICR read retries\n" ),ISS (X86_PLATFORM_IPI ,"PLT" ," Platform interrupts\n" ), #endif #ifdef CONFIG_SMP
@@ -122,34 +127,47 @@ static struct irq_stat_info irq_stat_inf#endif }; +static DECLARE_BITMAP(irq_stat_count_show, IRQ_COUNT_MAX) __read_mostly; +static int __init irq_init_stats (void ) { -struct irq_stat_info *info = irq_stat_info; +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 &&test_bit (info->skip_vector, system_vectors)) - info->skip_vector =0 ; +if (!info->skip_vector || (info->skip_vector !=DEFAULT_SUPPRESSED_VECTOR && +test_bit (info->skip_vector, system_vectors))) +set_bit (i, irq_stat_count_show); }#ifdef CONFIG_X86_LOCAL_APIC if (!x86_platform_ipi_callback) - irq_stat_info[IRQ_COUNT_X86_PLATFORM_IPI ].skip_vector =1 ; +clear_bit (IRQ_COUNT_X86_PLATFORM_IPI , irq_stat_count_show);#endif #ifdef CONFIG_X86_POSTED_MSI if (!posted_msi_enabled ()) - irq_stat_info[IRQ_COUNT_POSTED_MSI_NOTIFICATION ].skip_vector =1 ; +clear_bit (IRQ_COUNT_POSTED_MSI_NOTIFICATION , irq_stat_count_show);#endif #ifdef CONFIG_X86_MCE_AMD if (boot_cpu_data.x86_vendor !=X86_VENDOR_AMD && boot_cpu_data.x86_vendor !=X86_VENDOR_HYGON ) - irq_stat_info[IRQ_COUNT_DEFERRED_ERROR ].skip_vector =1 ; +clear_bit (IRQ_COUNT_DEFERRED_ERROR , irq_stat_count_show);#endif return 0 ; }late_initcall (irq_init_stats); +/* +* Used for default enabled counters to increment the stats and to enable the +* entry for /proc/interrupts output. +*/ +void irq_stat_inc_and_enable (enum irq_stat_counts which) +{ +this_cpu_inc (irq_stat.counts[which]); +set_bit (which, irq_stat_count_show); +} +#ifdef CONFIG_PROC_FS /* * /proc/interrupts printing for arch specific interrupts
@@ -159,7 +177,7 @@ int arch_show_interrupts(struct seq_fileconst 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) +if (!test_bit (i, irq_stat_count_show))continue ;seq_printf (p,"%*s:" , prec, info->symbol);
Re: [patch V5 05/15] x86/irq: Suppress unlikely interrupt stats by default
It looks like we had a "race condition", and you haven't picked up my Reviewed-by tag on this patch on v4. There was a minor comment too. I'm re-including them here.
Quoted 39 lines · Thomas Gleixner
From: Thomas Gleixner <[email protected]>
Unlikely interrupt counters like the spurious vector and the synthetic APIC ICR read retry show up in
/proc/interruptswith all counts 0 most of the time.As these are events which should never happen, suppress them by default and enable them for output when they actually happen.
This requires a seperate bitmap as the description array is marked
__ro_after_init. With that bitmap in place it becomes RO data.Signed-off-by Thomas Gleixner <[email protected]>---
V5: Moveirq_stat_inc_and_enable()here
V4: Fix the bad idea of writing to__ro_after_initmarked data
V3: New patch
---
arch/x86/include/asm/hardirq.h | 1 + arch/x86/kernel/apic/apic.c | 2 +- arch/x86/kernel/apic/ipi.c | 2 +-
arch/x86/kernel/irq.c | 38 ++++++++++++++++++++++++++++----------4 files changed, 31 insertions(+), 12 deletions(-)arch/x86/include/asm/hardirq.h +0
-0@@ -68,6 +68,7 @@ DECLARE_PER_CPU_ALIGNED(struct pi_desc, #define __ARCH_IRQ_STAT
#defineinc_irq_stat(index)this_cpu_inc(irq_stat.counts[IRQ_COUNT_##index])
+voidirq_stat_inc_and_enable(enum irq_stat_counts which);
#ifdefCONFIG_X86_LOCAL_APIC
#defineinc_perf_irq_stat()inc_irq_stat(APIC_PERF)arch/x86/kernel/apic/apic.c +0
-0@@ -2108,7 +2108,7 @@ static noinline void handle_spurious_int
trace_spurious_apic_entry(vector);
arch/x86/kernel/apic/apic.c static noinline void handle_spurious_int · line 2111
Quoted 2 lines
arch/x86/kernel/apic/apic.c +1
-1-inc_irq_stat (SPURIOUS ); +irq_stat_inc_and_enable (IRQ_COUNT_SPURIOUS );
This is just a matter of style but for symmetry with inc_irq_stat(), I would prepend '__' to the function name and create a wrapper macro that adds the 'IRQ_COUNT_' prefix.
And for consistency (you also have inc_perf_irq_stat()) I would call it inc_and_enable_irq_stat().
And yes, my comments above are totally bike shedding :)
Quoted 104 lines
/*
* If this is a spurious interrupt then do not acknowledgearch/x86/kernel/apic/ipi.c +1
-1@@ -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 ); +irq_stat_inc_and_enable (IRQ_COUNT_ICR_READ_RETRY ); udelay(100); } return APIC_ICR_BUSY;arch/x86/kernel/irq.c +0
-0@@ -69,19 +69,24 @@ struct irq_stat_info { const char *text; };arch/x86/kernel/irq.c +2
-0+#define DEFAULT_SUPPRESSED_VECTOR UINT_MAX +#define
ISS(idx, sym, txt)[IRQ_COUNT_##idx] = { .symbol = sym, .text = txt }
#defineITS(idx, sym, txt)[IRQ_COUNT_##idx] = \
{ .skip_vector = idx##_VECTOR, .symbol = sym, .text = txt }arch/x86/kernel/irq.c +4
-1-static struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX ]__ro_after_init = { +#define IDS (idx, sym, txt) [IRQ_COUNT_##idx] = \ + { .skip_vector = DEFAULT_SUPPRESSED_VECTOR, .symbol = sym, .text = txt } + +static const struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX ] = {
ISS(NMI, "NMI", " Non-maskable interrupts\n"),
#ifdefCONFIG_X86_LOCAL_APIC
ISS(APIC_TIMER, "LOC", " Local timer interrupts\n"),arch/x86/kernel/irq.c +1
-1- ISS(SPURIOUS, "SPU", " Spurious interrupts\n"), + IDS(SPURIOUS, "SPU", " Spurious interrupts\n"),
ISS(APIC_PERF, "PMI", " Performance monitoring interrupts\n"),
ISS(IRQ_WORK, "IWI", " IRQ work interrupts\n"),arch/x86/kernel/irq.c +1
-1- ISS(ICR_READ_RETRY, "RTR", " APIC ICR read retries\n"), + IDS(ICR_READ_RETRY, "RTR", " APIC ICR read retries\n"),
ISS(X86_PLATFORM_IPI, "PLT", " Platform interrupts\n"),
#endif
#ifdefCONFIG_SMParch/x86/kernel/irq.c +0
-0@@ -122,34 +127,47 @@ static struct irq_stat_info irq_stat_inf #endif };arch/x86/kernel/irq.c +2
-0+static DECLARE_BITMAP(irq_stat_count_show, IRQ_COUNT_MAX) __read_mostly; +static int
__initirq_init_stats(void)
{arch/x86/kernel/irq.c +1
-1-struct irq_stat_info *info = irq_stat_info; +const struct irq_stat_info *info = irq_stat_info;
for (unsigned int i = 0; i <ARRAY_SIZE(irq_stat_info); i++, info++) {arch/x86/kernel/irq.c +3
-2-if (info->skip_vector &&test_bit (info->skip_vector, system_vectors)) - info->skip_vector =0 ; +if (!info->skip_vector || (info->skip_vector !=DEFAULT_SUPPRESSED_VECTOR && + test_bit(info->skip_vector, system_vectors))) +set_bit (i, irq_stat_count_show);}
#ifdefCONFIG_X86_LOCAL_APIC
if (!x86_platform_ipi_callback)arch/x86/kernel/irq.c +1
-1- irq_stat_info[IRQ_COUNT_X86_PLATFORM_IPI ].skip_vector =1 ; +clear_bit (IRQ_COUNT_X86_PLATFORM_IPI , irq_stat_count_show);#endif
#ifdefCONFIG_X86_POSTED_MSI
if (!posted_msi_enabled())arch/x86/kernel/irq.c +1
-1- irq_stat_info[IRQ_COUNT_POSTED_MSI_NOTIFICATION ].skip_vector =1 ; +clear_bit (IRQ_COUNT_POSTED_MSI_NOTIFICATION , irq_stat_count_show);#endif
#ifdefCONFIG_X86_MCE_AMD
if (boot_cpu_data.x86_vendor !=X86_VENDOR_AMD&& boot_cpu_data.x86_vendor !=X86_VENDOR_HYGON)arch/x86/kernel/irq.c +1
-1- irq_stat_info[IRQ_COUNT_DEFERRED_ERROR ].skip_vector =1 ; +clear_bit (IRQ_COUNT_DEFERRED_ERROR , irq_stat_count_show);#endif
return 0;
}
late_initcall(irq_init_stats);arch/x86/kernel/irq.c +10
-0+/* +* Used for default enabled counters to increment the stats and to enable the +* entry for /proc/interrupts output. +*/ +void irq_stat_inc_and_enable (enum irq_stat_counts which) +{ +this_cpu_inc (irq_stat.counts[which]); +set_bit (which, irq_stat_count_show); +} +#ifdef
CONFIG_PROC_FS
/*
*/proc/interruptsprinting for arch specific interruptsarch/x86/kernel/irq.c +0
-0@@ -159,7 +177,7 @@ int arch_show_interrupts(struct seq_file const struct irq_stat_info *info = irq_stat_info;
for (unsigned int i = 0; i <ARRAY_SIZE(irq_stat_info); i++, info++) {arch/x86/kernel/irq.c +1
-1-if (info->skip_vector) +if (!test_bit (i, irq_stat_count_show))
Quoted 3 lines
continue;
seq_printf(p, "%*s:", prec, info->symbol);
Thomas Gleixner
maintainerRe: [patch V5 05/15] x86/irq: Suppress unlikely interrupt stats by default
Quoted 9 lines · Radu Rendec
arch/x86/kernel/apic/apic.c static noinline void handle_spurious_int · line 2111
Quoted 2 lines
arch/x86/kernel/apic/apic.c +1
-1-inc_irq_stat (SPURIOUS ); +irq_stat_inc_and_enable (IRQ_COUNT_SPURIOUS );This is just a matter of style but for symmetry with
inc_irq_stat(), I would prepend '__' to the function name and create a wrapper macro that adds the 'IRQ_COUNT_' prefix.And for consistency (you also have
inc_perf_irq_stat()) I would call itinc_and_enable_irq_stat().
I fundamentaly despise these constructs which do not have a proper name space prefix. They make it just hard to grep for. Yes, inc_irq_stat() exists already, but that does not mean we need to proliferate it.
Thanks,
tglx
Thomas Gleixner
maintainerPATCH v5 6/15 x86/irq: Move IOAPIC misrouted and PIC/APIC error counts into irq_stats
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
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.
---
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(-)
arch/x86/include/asm/hardirq.h +4 -3
@@ -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);
arch/x86/include/asm/hw_irq.h +0 -4
@@ -110,10 +110,6 @@ static inline void lock_vector_lock(voidstatic 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[];
arch/x86/kernel/apic/apic.c +1 -1
@@ -2180,7 +2180,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_error_inteapic_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);
arch/x86/kernel/apic/io_apic.c +1 -3
@@ -1575,8 +1575,6 @@ static unsigned int startup_ioapic_irq(sreturn 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); }
arch/x86/kernel/i8259.c +1 -1
@@ -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
arch/x86/kernel/irq.c +4 -12
@@ -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_fileirq_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,
fs/proc/stat.c +0 -4
@@ -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));
Re: [patch V5 06/15] x86/irq: Move IOAPIC misrouted and PIC/APIC error counts into irq_stats
Quoted 171 lines · Thomas Gleixner
From: Thomas Gleixner <[email protected]>
The special treatment of these counts is just adding extra code for no real value. The
irq_statsmechanism 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 <[email protected]>---
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(-)arch/x86/include/asm/hardirq.h +4
-0@@ -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, };arch/x86/include/asm/hardirq.h +0
-3@@ -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);arch/x86/include/asm/hw_irq.h +0
-0@@ -110,10 +110,6 @@ static inline void lock_vector_lock(void static inline void unlock_vector_lock(void) {} #endifarch/x86/include/asm/hw_irq.h +0
-4-/* 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[];arch/x86/kernel/apic/apic.c +1
-1@@ -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);arch/x86/kernel/apic/io_apic.c +0
-0@@ -1575,8 +1575,6 @@ static unsigned int startup_ioapic_irq(s return was_pending; }arch/x86/kernel/apic/io_apic.c +0
-2-atomic_t irq_mis_count; -#ifdef
CONFIG_GENERIC_PENDING_IRQ
static boolio_apic_level_ack_pending(struct mp_chip_data *data){arch/x86/kernel/apic/io_apic.c +1
-1@@ -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); }arch/x86/kernel/i8259.c +1
-1@@ -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 arch/x86/kernel/irq.c +0
-0@@ -39,8 +39,6 @@ EXPORT_PER_CPU_SYMBOL(__softirq_pending)
DEFINE_PER_CPU_CACHE_HOT(structirq_stack*,hardirq_stack_ptr);arch/x86/kernel/irq.c +0
-2-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.arch/x86/kernel/irq.c +4
-0@@ -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 };
staticDECLARE_BITMAP(irq_stat_count_show, IRQ_COUNT_MAX)__read_mostly;arch/x86/kernel/irq.c +0
-4@@ -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; }arch/x86/kernel/irq.c +0
-6@@ -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_inlinevoid handle_irq(structirq_desc*desc,fs/proc/stat.c +0
-3@@ -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
u64get_idle_time(struct kernel_cpustat *kcs, int cpu){fs/proc/stat.c +0
-1@@ -122,7 +119,6 @@ static int show_stat(struct seq_file *p, sum_softirq += softirq_stat; } } - sum +=arch_irq_stat ();
Quoted 3 lines
seq_put_decimal_ull(p, "cpu ", nsec_to_clock_t(user));seq_put_decimal_ull(p, " ", nsec_to_clock_t(nice));
Thomas Gleixner
maintainerPATCH v5 7/15 scripts/gdb: Update x86 interrupts to the array based storage
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
x86 changed the interrupt statistics from a struct with individual members to an counter array. It also provides a corresponding info array with the strings for prefix and description and an indicator to skip the entry.
Update the already out of sync GDB script to use the counter and the info array, which keeps the GDB script in sync automatically.
---
V5: Bring the show_err_irq() back as it is used on mips
Fix the index calculation for the show bitmap so it works with more than one unsigned long.
V4: Adopted to irq_stat_count_show bitmap and removed the duplicate ERR/MIS output
V3: New patch - Radu
--- scripts/gdb/linux/interrupts.py | 81 +++++++++++----------------------------- 1 file changed, 23 insertions(+), 58 deletions(-)
scripts/gdb/linux/interrupts.py +23 -58
@@ -90,71 +90,29 @@ irq_desc_type = utils.CachedType("struct return text -def show_irq_err_count(prec): - cnt = utils.gdb_eval_or_none("irq_err_count") - text = "" - if cnt is not None: - text += "%*s: %10u\n" % (prec, "ERR", cnt['counter']) - return text - -def x86_show_irqstat(prec, pfx, field, desc): - irq_stat = gdb.parse_and_eval("&irq_stat") +def x86_show_irqstat(prec, pfx, idx, desc): + irq_stat = gdb.parse_and_eval("&irq_stat.counts[%d]" %idx) text = "%*s: " % (prec, pfx) for cpu in cpus.each_online_cpu(): stat = cpus.per_cpu(irq_stat, cpu) - text += "%10u " % (stat[field]) - text += " %s\n" % (desc) - return text - -def x86_show_mce(prec, var, pfx, desc): - pvar = gdb.parse_and_eval(var) - text = "%*s: " % (prec, pfx) - for cpu in cpus.each_online_cpu(): - text += "%10u " % (cpus.per_cpu(pvar, cpu).dereference()) - text += " %s\n" % (desc) + text += "%10u " % (stat.dereference()) + text += desc return text def x86_show_interupts(prec): - text = x86_show_irqstat(prec, "NMI", '__nmi_count', 'Non-maskable interrupts') + info_type = gdb.lookup_type('struct irq_stat_info') + info = gdb.parse_and_eval('irq_stat_info') + bitmap = gdb.parse_and_eval('irq_stat_count_show') + bitsperlong = 8 * int(bitmap.type.target().sizeof) - if constants.LX_CONFIG_X86_LOCAL_APIC: - text += x86_show_irqstat(prec, "LOC", 'apic_timer_irqs', "Local timer interrupts") - text += x86_show_irqstat(prec, "SPU", 'irq_spurious_count', "Spurious interrupts") - text += x86_show_irqstat(prec, "PMI", 'apic_perf_irqs', "Performance monitoring interrupts") - text += x86_show_irqstat(prec, "IWI", 'apic_irq_work_irqs', "IRQ work interrupts") - text += x86_show_irqstat(prec, "RTR", 'icr_read_retry_count', "APIC ICR read retries") - if utils.gdb_eval_or_none("x86_platform_ipi_callback") is not None: - text += x86_show_irqstat(prec, "PLT", 'x86_platform_ipis', "Platform interrupts") - - if constants.LX_CONFIG_SMP: - text += x86_show_irqstat(prec, "RES", 'irq_resched_count', "Rescheduling interrupts") - text += x86_show_irqstat(prec, "CAL", 'irq_call_count', "Function call interrupts") - text += x86_show_irqstat(prec, "TLB", 'irq_tlb_count', "TLB shootdowns") - - if constants.LX_CONFIG_X86_THERMAL_VECTOR: - text += x86_show_irqstat(prec, "TRM", 'irq_thermal_count', "Thermal events interrupts") - - if constants.LX_CONFIG_X86_MCE_THRESHOLD: - text += x86_show_irqstat(prec, "THR", 'irq_threshold_count', "Threshold APIC interrupts") - - if constants.LX_CONFIG_X86_MCE_AMD: - text += x86_show_irqstat(prec, "DFR", 'irq_deferred_error_count', "Deferred Error APIC interrupts") - - if constants.LX_CONFIG_X86_MCE: - text += x86_show_mce(prec, "&mce_exception_count", "MCE", "Machine check exceptions") - text += x86_show_mce(prec, "&mce_poll_count", "MCP", "Machine check polls") - - text += show_irq_err_count(prec) - - if constants.LX_CONFIG_X86_IO_APIC: - cnt = utils.gdb_eval_or_none("irq_mis_count") - if cnt is not None: - text += "%*s: %10u\n" % (prec, "MIS", cnt['counter']) - - if constants.LX_CONFIG_KVM: - text += x86_show_irqstat(prec, "PIN", 'kvm_posted_intr_ipis', 'Posted-interrupt notification event') - text += x86_show_irqstat(prec, "NPI", 'kvm_posted_intr_nested_ipis', 'Nested posted-interrupt event') - text += x86_show_irqstat(prec, "PIW", 'kvm_posted_intr_wakeup_ipis', 'Posted-interrupt wakeup event') + text = "" + for idx in range(int(info.type.sizeof / info_type.sizeof)): + show = bitmap[int(idx / bitsperlong)] + if not show & 1 << int(idx % bitsperlong): + continue + pfx = info[idx]['symbol'].string() + desc = info[idx]['text'].string() + text += x86_show_irqstat(prec, pfx, idx, desc) return text
@@ -187,6 +145,13 @@ irq_desc_type = utils.CachedType("struct text += "%*s: %10lu\n" % (prec, "ERR", gdb.parse_and_eval("irq_err_count")) return text +def show_irq_err_count(prec): + cnt = utils.gdb_eval_or_none("irq_err_count") + text = "" + if cnt is not None: + text += "%*s: %10u\n" % (prec, "ERR", cnt['counter']) + return text + def arch_show_interrupts(prec): text = "" if utils.is_target_arch("x86"):
Radu Rendec
reviewerRe: [patch V5 07/15] scripts/gdb: Update x86 interrupts to the array based storage
Quoted 27 lines · Thomas Gleixner
From: Thomas Gleixner <[email protected]>
x86 changed the interrupt statistics from a struct with individual members to an counter array. It also provides a corresponding info array with the strings for prefix and description and an indicator to skip the entry.
Update the already out of sync GDB script to use the counter and the info array, which keeps the GDB script in sync automatically.
Signed-off-by Thomas Gleixner <[email protected]>Tested-by Florian Fainelli <[email protected]>Reviewed-by Florian Fainelli <[email protected]>---
V5: Bring theshow_err_irq()back as it is used on mips
Fix the index calculation for the show bitmap so it works with more than one unsigned long.
V4: Adopted toirq_stat_count_showbitmap and removed the duplicate ERR/MIS output
V3: New patch - Radu
---
scripts/gdb/linux/interrupts.py | 81 +++++++++++-----------------------------1 file changed, 23 insertions(+), 58 deletions(-)scripts/gdb/linux/interrupts.py +0
-0@@ -90,71 +90,29 @@ irq_desc_type = utils.CachedType("struct
return text
scripts/gdb/linux/interrupts.py irq_desc_type = utils.CachedType("struct · line 99
Quoted 7 lines
scripts/gdb/linux/interrupts.py +0
-7-def show_irq_err_count(prec): - cnt = utils.gdb_eval_or_none("irq_err_count") - text = "" - if cnt is not None: - text += "%*s: %10u\n" % (prec, "ERR", cnt['counter']) - return text -
Nit: show_irq_err_count() is unchanged, and I see no obvious reason to move it down below. Keeping it here would exclude it from the patch altogether, making the patch shorter and easier to read/review.
Quoted 10 lines
scripts/gdb/linux/interrupts.py +2
-2-def x86_show_irqstat(prec, pfx, field, desc): - irq_stat = gdb.parse_and_eval("&irq_stat") +def x86_show_irqstat(prec, pfx, idx, desc): + irq_stat = gdb.parse_and_eval("&irq_stat.counts[%d]" %idx)text = "%*s: " % (prec, pfx)
for cpu incpus.each_online_cpu():
stat =cpus.per_cpu(irq_stat, cpu)
- text += "%10u " % (stat[field])
- text += " %s\n" % (desc)
- return text
scripts/gdb/linux/interrupts.py irq_desc_type = utils.CachedType("struct · line 98
Quoted 8 lines
scripts/gdb/linux/interrupts.py +1
-7- -def x86_show_mce(prec, var, pfx, desc): - pvar = gdb.parse_and_eval(var) - text = "%*s: " % (prec, pfx) - for cpu in cpus.each_online_cpu(): - text += "%10u " % (cpus.per_cpu(pvar, cpu).dereference()) - text += " %s\n" % (desc) + text += "%10u " % (stat.dereference())
Something is wrong here. The counters for the arch specific interrupts are not aligned with the generic ones. Sample from my VM setup:
(gdb) lx-interruptlist
CPU0 CPU1 CPU2 CPU3
1: 0 0 9 0 IO-APIC 1-edge i8042 4: 0 0 0 267 IO-APIC 4-edge ttyS0 9: 0 0 0 0 IO-APIC 9-fasteoi acpi 12: 0 15 0 0 IO-APIC 12-edge i8042 [...] NMI: 0 0 0 0 Non-maskable interrupts LOC: 3925 3997 4281 3972 Local timer interrupts PMI: 0 0 0 0 Performance monitoring interrupts
[...]
If I remove the space in "%10u " it becomes aligned but then the description is no longer aligned.
Quoted 73 lines
+ text += desc
return text
defx86_show_interupts(prec):
- text =x86_show_irqstat(prec, "NMI", '__nmi_count', 'Non-maskable interrupts')
+info_type=gdb.lookup_type('struct irq_stat_info')
+ info =gdb.parse_and_eval('irq_stat_info')
+ bitmap =gdb.parse_and_eval('irq_stat_count_show')
+ bitsperlong = 8 * int(bitmap.type.target().sizeof)
- if constants.LX_CONFIG_X86_LOCAL_APIC:
- text +=x86_show_irqstat(prec, "LOC", 'apic_timer_irqs', "Local timer interrupts")- text +=x86_show_irqstat(prec, "SPU", 'irq_spurious_count', "Spurious interrupts")- text +=x86_show_irqstat(prec, "PMI", 'apic_perf_irqs', "Performance monitoring interrupts")- text +=x86_show_irqstat(prec, "IWI", 'apic_irq_work_irqs', "IRQ work interrupts")- text +=x86_show_irqstat(prec, "RTR", 'icr_read_retry_count', "APIC ICR read retries")- ifutils.gdb_eval_or_none("x86_platform_ipi_callback")is not None:
- text +=x86_show_irqstat(prec, "PLT", 'x86_platform_ipis', "Platform interrupts")scripts/gdb/linux/interrupts.py +8
-30- - if constants.LX_CONFIG_SMP: - text += x86_show_irqstat(prec, "RES", 'irq_resched_count', "Rescheduling interrupts") - text += x86_show_irqstat(prec, "CAL", 'irq_call_count', "Function call interrupts") - text += x86_show_irqstat(prec, "TLB", 'irq_tlb_count', "TLB shootdowns") - - if constants.LX_CONFIG_X86_THERMAL_VECTOR: - text += x86_show_irqstat(prec, "TRM", 'irq_thermal_count', "Thermal events interrupts") - - if constants.LX_CONFIG_X86_MCE_THRESHOLD: - text += x86_show_irqstat(prec, "THR", 'irq_threshold_count', "Threshold APIC interrupts") - - if constants.LX_CONFIG_X86_MCE_AMD: - text += x86_show_irqstat(prec, "DFR", 'irq_deferred_error_count', "Deferred Error APIC interrupts") - - if constants.LX_CONFIG_X86_MCE: - text += x86_show_mce(prec, "&mce_exception_count", "MCE", "Machine check exceptions") - text += x86_show_mce(prec, "&mce_poll_count", "MCP", "Machine check polls") - - text += show_irq_err_count(prec) - - if constants.LX_CONFIG_X86_IO_APIC: - cnt = utils.gdb_eval_or_none("irq_mis_count") - if cnt is not None: - text += "%*s: %10u\n" % (prec, "MIS", cnt['counter']) - - if constants.LX_CONFIG_KVM: - text += x86_show_irqstat(prec, "PIN", 'kvm_posted_intr_ipis', 'Posted-interrupt notification event') - text += x86_show_irqstat(prec, "NPI", 'kvm_posted_intr_nested_ipis', 'Nested posted-interrupt event') - text += x86_show_irqstat(prec, "PIW", 'kvm_posted_intr_wakeup_ipis', 'Posted-interrupt wakeup event') + text = "" + for idx in range(int(info.type.sizeof / info_type.sizeof)): + show = bitmap[int(idx / bitsperlong)] + if not show & 1 << int(idx % bitsperlong): + continue + pfx = info[idx]['symbol'].string() + desc = info[idx]['text'].string() + text += x86_show_irqstat(prec, pfx, idx, desc)
return textscripts/gdb/linux/interrupts.py +0
-0@@ -187,6 +145,13 @@ irq_desc_type = utils.CachedType("struct text += "%*s: %10lu\n" % (prec, "ERR", gdb.parse_and_eval("irq_err_count"))return text
scripts/gdb/linux/interrupts.py +7
-0+def show_irq_err_count(prec): + cnt = utils.gdb_eval_or_none("irq_err_count") + text = "" + if cnt is not None: + text += "%*s: %10u\n" % (prec, "ERR", cnt['counter']) + return text +def
arch_show_interrupts(prec):
text = ""
ifutils.is_target_arch("x86"):
Thomas Gleixner
maintainerPATCH v5 8/15 genirq: Expose nr_irqs in core code
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
... to avoid function calls in the core code to retrieve the maximum number of interrupts.
Rename it to 'total_nr_irqs' as 'nr_irqs' is too generic and fix up the 'nr_irqs' reference in the related GDB script as well.
---
V3: Fix up the missing usage sites and gdb script - Radu
--- kernel/irq/internals.h | 1 + kernel/irq/irqdesc.c | 28 ++++++++++++++-------------- kernel/irq/proc.c | 4 ++-- scripts/gdb/linux/interrupts.py | 2 +- 4 files changed, 18 insertions(+), 17 deletions(-)
kernel/irq/internals.h +1 -0
@@ -21,6 +21,7 @@extern bool noirqdebug;extern int irq_poll_cpu; +extern unsigned int total_nr_irqs;extern struct irqaction chained_action;
kernel/irq/irqdesc.c +14 -14
@@ -140,14 +140,14 @@ static void desc_set_defaults(unsigned idesc_smp_init (desc, node, affinity); } -static unsigned int nr_irqs =NR_IRQS ; +unsigned int total_nr_irqs__read_mostly =NR_IRQS ;/** * irq_get_nr_irqs() - Number of interrupts supported by the system. */ unsigned int irq_get_nr_irqs (void ) { -return nr_irqs; +return total_nr_irqs; }EXPORT_SYMBOL_GPL (irq_get_nr_irqs);
@@ -159,7 +159,7 @@ EXPORT_SYMBOL_GPL(irq_get_nr_irqs);*/ unsigned int irq_set_nr_irqs (unsigned int nr) { - nr_irqs = nr; + total_nr_irqs = nr;return nr; }
@@ -187,9 +187,9 @@ static unsigned int irq_find_at_or_afterstruct irq_desc *desc;guard (rcu)(); - desc =mt_find (&sparse_irqs, &index, nr_irqs); + desc =mt_find (&sparse_irqs, &index, total_nr_irqs); -return desc ?irq_desc_get_irq (desc) : nr_irqs; +return desc ?irq_desc_get_irq (desc) : total_nr_irqs; }static void irq_insert_desc (unsigned int irq,struct irq_desc *desc)
@@ -543,7 +543,7 @@ static bool irq_expand_nr_irqs(unsigned {if (nr >MAX_SPARSE_IRQS )return false; - nr_irqs = nr; + total_nr_irqs = nr;return true; }
@@ -557,16 +557,16 @@ int __init early_irq_init(void)/* Let arch update nr_irqs and return the nr of preallocated irqs */ initcnt =arch_probe_nr_irqs ();printk (KERN_INFO "NR_IRQS: %d, nr_irqs: %d, preallocated irqs: %d\n" , -NR_IRQS , nr_irqs, initcnt); +NR_IRQS , total_nr_irqs, initcnt); -if (WARN_ON (nr_irqs >MAX_SPARSE_IRQS )) - nr_irqs =MAX_SPARSE_IRQS ; +if (WARN_ON (total_nr_irqs >MAX_SPARSE_IRQS )) + total_nr_irqs =MAX_SPARSE_IRQS ;if (WARN_ON (initcnt >MAX_SPARSE_IRQS )) initcnt =MAX_SPARSE_IRQS ; -if (initcnt > nr_irqs) - nr_irqs = initcnt; +if (initcnt > total_nr_irqs) + total_nr_irqs = initcnt;for (i =0 ; i < initcnt; i++) { desc =alloc_desc (i, node,0 ,NULL ,NULL );
@@ -862,7 +862,7 @@ void irq_free_descs(unsigned int from, u {int i; -if (from >= nr_irqs || (from + cnt) > nr_irqs) +if (from >= total_nr_irqs || (from + cnt) > total_nr_irqs)return ;guard (mutex)(&sparse_irq_lock);
@@ -911,7 +911,7 @@ int __ref __irq_alloc_descs(int irq, unsif (irq >=0 && start != irq)return -EEXIST ; -if (start + cnt > nr_irqs) { +if (start + cnt > total_nr_irqs) {if (!irq_expand_nr_irqs (start + cnt))return -ENOMEM ; }
@@ -923,7 +923,7 @@ EXPORT_SYMBOL_GPL(__irq_alloc_descs);* irq_get_next_irq - get next allocated irq number * @offset: where to start the search * -* Returns next irq number after offset or nr_irqs if none is found. +* Returns next irq number after offset or total_nr_irqs if none is found. */ unsigned int irq_get_next_irq (unsigned int offset) {
kernel/irq/proc.c +2 -2
@@ -447,7 +447,7 @@ int __weak arch_show_interrupts(struct s }#ifndef ACTUAL_NR_IRQS -# define ACTUAL_NR_IRQS irq_get_nr_irqs() +# define ACTUAL_NR_IRQS total_nr_irqs#endif #define ZSTR1 " 0"
@@ -488,7 +488,7 @@ void irq_proc_emit_counts(struct seq_filint show_interrupts (struct seq_file *p,void *v) { -const unsigned int nr_irqs =irq_get_nr_irqs (); +const unsigned int nr_irqs = total_nr_irqs;static int prec;int i = *(loff_t *) v, j;
scripts/gdb/linux/interrupts.py +1 -1
@@ -174,7 +174,7 @@ irq_desc_type = utils.CachedType("struct super(LxInterruptList, self).__init__("lx-interruptlist", gdb.COMMAND_DATA) def invoke(self, arg, from_tty): - nr_irqs = gdb.parse_and_eval("nr_irqs") + nr_irqs = gdb.parse_and_eval("total_nr_irqs") prec = 3 j = 1000 while prec < 10 and j <= nr_irqs:
Thomas Gleixner
maintainerPATCH v5 9/15 genirq/manage: Make NMI cleanup RT safe
In reply to Thomas GleixnerEventually blocking functions cannot be invoked with interrupts disabled and a raw spin lock held. Restructure the code so this happens outside of the descriptor lock held region.
---
V4: New patch. Found when adding the validation update to it
--- kernel/irq/manage.c | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-)
kernel/irq/manage.c +21 -16
@@ -2026,24 +2026,30 @@ const void *free_irq(unsigned int irq, v }EXPORT_SYMBOL (free_irq); -/* This function must be called with desc->lock held */ static const void *__cleanup_nmi (unsigned int irq,struct irq_desc *desc) { +struct irqaction *action =NULL ;const char *devname =NULL ; - desc->istate &= ~IRQS_NMI ; + scoped_guard(raw_spinlock_irqsave, &desc->lock) { +irq_nmi_teardown (desc); -if (!WARN_ON (desc->action ==NULL )) { -irq_pm_remove_action (desc, desc->action); - devname = desc->action->name; -unregister_handler_proc (irq, desc->action); + desc->istate &= ~IRQS_NMI ; -kfree (desc->action); +if (!WARN_ON (desc->action ==NULL )) { + action = desc->action; +irq_pm_remove_action (desc, action); + devname = action->name; + } desc->action =NULL ; + +irq_settings_clr_disable_unlazy (desc); +irq_shutdown_and_deactivate (desc); } -irq_settings_clr_disable_unlazy (desc); -irq_shutdown_and_deactivate (desc); +if (action) +unregister_handler_proc (irq, action); +kfree (action);irq_release_resources (desc);
@@ -2067,8 +2073,6 @@ const void *free_nmi(unsigned int irq, vif (WARN_ON (desc->depth ==0 ))disable_nmi_nosync (irq); -guard (raw_spinlock_irqsave)(&desc->lock); -irq_nmi_teardown (desc);return __cleanup_nmi (irq, desc); }
@@ -2318,13 +2322,14 @@ int request_nmi(unsigned int irq, irq_ha/* Setup NMI state */ desc->istate |=IRQS_NMI ; retval =irq_nmi_setup (desc); -if (retval) { -__cleanup_nmi (irq, desc); -return -EINVAL ; - } -return 0 ; } +if (retval) { +__cleanup_nmi (irq, desc); +return -EINVAL ; + } +return 0 ; + err_irq_setup:irq_chip_pm_put (&desc->irq_data); err_out:
Thomas Gleixner
maintainerPATCH v5 10/15 genirq: Cache the condition for /proc/interrupts exposure
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
show_interrupts() evaluates a boatload of conditions to establish whether it should expose an interrupt in /proc/interrupts or not.
That can be simplified by caching the condition in an internal status flag, which is updated when one of the relevant inputs changes.
The irq_desc::kstat_irq check is dropped because visible interrupt descriptors always have a valid pointer.
As a result the number of instructions and branches for reading /proc/interrupts is reduced significantly.
---
V4: Cover freeing of per CPU and NMI type interrupts
V3: Remove the historical kstat_irq check - Radu
V2: s/IRQF_/IRQ/ and fixup the enum treatment - Dmitry
--- include/linux/irq.h | 1 + kernel/irq/chip.c | 2 ++ kernel/irq/internals.h | 2 ++ kernel/irq/manage.c | 8 +++++++- kernel/irq/proc.c | 15 +++++++++++---- kernel/irq/settings.h | 13 +++++++++++++ 6 files changed, 36 insertions(+), 5 deletions(-)
include/linux/irq.h +1 -0
@@ -99,6 +99,7 @@ enum {IRQ_DISABLE_UNLAZY = (1 <<19 ),IRQ_HIDDEN = (1 <<20 ),IRQ_NO_DEBUG = (1 <<21 ), + IRQ_RESERVED = (1 << 22), };#define IRQF_MODIFY_MASK \
kernel/irq/chip.c +2 -0
@@ -1004,6 +1004,7 @@ static voidWARN_ON (irq_chip_pm_get (irq_desc_get_irq_data (desc)));irq_activate_and_startup (desc,IRQ_RESEND ); } +irq_proc_update_valid (desc); } void __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
@@ -1064,6 +1065,7 @@ void irq_modify_status(unsigned int irq, trigger = tmp;irqd_set (&desc->irq_data, trigger); +irq_proc_update_valid (desc); } }EXPORT_SYMBOL_GPL (irq_modify_status);
kernel/irq/internals.h +2 -0
@@ -123,6 +123,7 @@ extern void register_irq_proc(unsigned iextern void unregister_irq_proc (unsigned int irq,struct irq_desc *desc);extern void register_handler_proc (unsigned int irq,struct irqaction *action);extern void unregister_handler_proc (unsigned int irq,struct irqaction *action); +void irq_proc_update_valid (struct irq_desc *desc);#else static inline void register_irq_proc (unsigned int irq,struct irq_desc *desc) { }static inline void unregister_irq_proc (unsigned int irq,struct irq_desc *desc) { }
@@ -130,6 +131,7 @@ static inline void register_handler_proc struct irqaction *action) { }static inline void unregister_handler_proc (unsigned int irq,struct irqaction *action) { } +static inline void irq_proc_update_valid (struct irq_desc *desc) { }#endif extern bool irq_can_set_affinity_usr (unsigned int irq);
kernel/irq/manage.c +7 -1
@@ -1802,6 +1802,7 @@ static int__enable_irq (desc); } +irq_proc_update_valid (desc);raw_spin_unlock_irqrestore (&desc->lock, flags);chip_bus_sync_unlock (desc);mutex_unlock (&desc->request_mutex);
@@ -1906,6 +1907,7 @@ static struct irqaction *__free_irq(stru desc->affinity_hint =NULL ;#endif +irq_proc_update_valid (desc);raw_spin_unlock_irqrestore (&desc->lock, flags);/* * Drop bus_lock here so the changes which were done in the chip
@@ -2047,6 +2049,8 @@ static const void *__cleanup_nmi(unsigneirq_shutdown_and_deactivate (desc); } +irq_proc_update_valid (desc); +if (action)unregister_handler_proc (irq, action);kfree (action);
@@ -2433,8 +2437,10 @@ static struct irqaction *__free_percpu_i *action_ptr = action->next;/* Demote from NMI if we killed the last action */ -if (!desc->action) +if (!desc->action) { desc->istate &= ~IRQS_NMI ; +irq_proc_update_valid (desc); + } }unregister_handler_proc (irq, action);
kernel/irq/proc.c +11 -4
@@ -439,6 +439,16 @@ void init_irq_proc(void)register_irq_proc (irq, desc); } +void irq_proc_update_valid (struct irq_desc *desc) +{ +u32 set = _IRQ_PROC_VALID; + +if (irq_settings_is_hidden (desc) ||irq_desc_is_chained (desc) || !desc->action) + set =0 ; + +irq_settings_update_proc_valid (desc, set); +} +#ifdef CONFIG_GENERIC_IRQ_SHOW int __weak arch_show_interrupts (struct seq_file *p,int prec)
@@ -514,10 +524,7 @@ int show_interrupts(struct seq_file *p,guard (rcu)(); desc =irq_to_desc (i); -if (!desc ||irq_settings_is_hidden (desc)) -return 0 ; - -if (!desc->action ||irq_desc_is_chained (desc) || !desc->kstat_irqs) +if (!desc || !irq_settings_proc_valid (desc))return 0 ;seq_printf (p,"%*d:" , prec, i);
kernel/irq/settings.h +13 -0
@@ -18,6 +18,7 @@ enum { _IRQ_DISABLE_UNLAZY =IRQ_DISABLE_UNLAZY , _IRQ_HIDDEN =IRQ_HIDDEN , _IRQ_NO_DEBUG =IRQ_NO_DEBUG , + _IRQ_PROC_VALID =IRQ_RESERVED , _IRQF_MODIFY_MASK = IRQF_MODIFY_MASK, };
@@ -34,6 +35,7 @@ enum {#define IRQ_DISABLE_UNLAZY GOT_YOU_MORON#define IRQ_HIDDEN GOT_YOU_MORON#define IRQ_NO_DEBUG GOT_YOU_MORON +#define IRQ_RESERVED GOT_YOU_MORON#undef IRQF_MODIFY_MASK#define IRQF_MODIFY_MASK GOT_YOU_MORON
@@ -180,3 +182,14 @@ static inline bool irq_settings_no_debug {return desc->status_use_accessors & _IRQ_NO_DEBUG; } + +static inline bool irq_settings_proc_valid (struct irq_desc *desc) +{ +return desc->status_use_accessors & _IRQ_PROC_VALID; +} + +static inline void irq_settings_update_proc_valid (struct irq_desc *desc,u32 set) +{ + desc->status_use_accessors &= ~_IRQ_PROC_VALID; + desc->status_use_accessors |= (set & _IRQ_PROC_VALID); +}
Re: [patch V5 10/15] genirq: Cache the condition for /proc/interrupts exposure
Quoted 167 lines · Thomas Gleixner
From: Thomas Gleixner <[email protected]>
show_interrupts()evaluates a boatload of conditions to establish whether it should expose an interrupt in/proc/interruptsor not.That can be simplified by caching the condition in an internal status flag, which is updated when one of the relevant inputs changes.
The
irq_desc::kstat_irqcheck is dropped because visible interrupt descriptors always have a valid pointer.As a result the number of instructions and branches for reading
/proc/interruptsis reduced significantly.Signed-off-by Thomas Gleixner <[email protected]>Reviewed-by Dmitry Ilvokhin <[email protected]>---
V4: Cover freeing of per CPU and NMI type interrupts
V3: Remove the historicalkstat_irqcheck - Radu
V2: s/IRQF_/IRQ/ and fixup the enum treatment - Dmitry
---
include/linux/irq.h | 1 + kernel/irq/chip.c | 2 ++kernel/irq/internals.h| 2 ++ kernel/irq/manage.c | 8 +++++++- kernel/irq/proc.c | 15 +++++++++++---- kernel/irq/settings.h | 13 +++++++++++++6 files changed, 36 insertions(+), 5 deletions(-)include/linux/irq.h +1
-0@@ -99,6 +99,7 @@ enum { IRQ_DISABLE_UNLAZY = (1 << 19), IRQ_HIDDEN = (1 << 20), IRQ_NO_DEBUG = (1 << 21), + IRQ_RESERVED = (1 << 22), };
#defineIRQF_MODIFY_MASK\kernel/irq/chip.c +1
-0@@ -1004,6 +1004,7 @@ static void WARN_ON(irq_chip_pm_get(irq_desc_get_irq_data(desc))); irq_activate_and_startup(desc, IRQ_RESEND); } +irq_proc_update_valid (desc); }
void __irq_set_handler(unsigned int irq,irq_flow_handler_thandle, intis_chained,kernel/irq/chip.c +0
-0@@ -1064,6 +1065,7 @@ void irq_modify_status(unsigned int irq, trigger = tmp;
irqd_set(&desc->irq_data, trigger);kernel/irq/chip.c +1
-0+irq_proc_update_valid (desc);}
}
EXPORT_SYMBOL_GPL(irq_modify_status);kernel/irq/internals.h +2
-0@@ -123,6 +123,7 @@ extern void register_irq_proc(unsigned i extern void unregister_irq_proc(unsigned int irq, struct irq_desc *desc); extern void register_handler_proc(unsigned int irq, struct irqaction *action); extern void unregister_handler_proc(unsigned int irq, struct irqaction *action); +void irq_proc_update_valid (struct irq_desc *desc); #else static inline void register_irq_proc(unsigned int irq, struct irq_desc *desc) { } static inline void unregister_irq_proc(unsigned int irq, struct irq_desc *desc) { }@@ -130,6 +131,7 @@ static inline void register_handler_proc struct irqaction *action) { } static inline void unregister_handler_proc(unsigned int irq, struct irqaction *action) { } +static inline void irq_proc_update_valid (struct irq_desc *desc) { } #endif
extern boolirq_can_set_affinity_usr(unsigned int irq);kernel/irq/manage.c +0
-0@@ -1802,6 +1802,7 @@ static int __enable_irq(desc); }kernel/irq/manage.c +1
-0+irq_proc_update_valid (desc);
raw_spin_unlock_irqrestore(&desc->lock, flags);chip_bus_sync_unlock(desc);mutex_unlock(&desc->request_mutex);kernel/irq/manage.c +0
-0@@ -1906,6 +1907,7 @@ static struct irqaction *__free_irq(stru desc->affinity_hint = NULL; #endifkernel/irq/manage.c +1
-0+irq_proc_update_valid (desc);
raw_spin_unlock_irqrestore(&desc->lock, flags);
/*
* Dropbus_lockhere so the changes which were done in the chipkernel/irq/manage.c +0
-0@@ -2047,6 +2049,8 @@ static const void *__cleanup_nmi(unsigne irq_shutdown_and_deactivate(desc); }kernel/irq/manage.c +2
-0+irq_proc_update_valid (desc); +if (action)
unregister_handler_proc(irq, action); kfree(action);kernel/irq/manage.c +0
-0@@ -2433,8 +2437,10 @@ static struct irqaction *__free_percpu_i *action_ptr = action->next;
/* Demote from NMI if we killed the last action */kernel/irq/manage.c +1
-1-if (!desc->action) +if (!desc->action) {
desc->istate&= ~IRQS_NMI;kernel/irq/manage.c +2
-0+irq_proc_update_valid (desc); + }}
unregister_handler_proc(irq, action);kernel/irq/proc.c +0
-0@@ -439,6 +439,16 @@ void init_irq_proc(void) register_irq_proc(irq, desc); }kernel/irq/proc.c +10
-0+void irq_proc_update_valid (struct irq_desc *desc) +{ +u32 set = _IRQ_PROC_VALID; + +if (irq_settings_is_hidden (desc) ||irq_desc_is_chained (desc) || !desc->action) + set =0 ; + +irq_settings_update_proc_valid (desc, set); +} +#ifdef
CONFIG_GENERIC_IRQ_SHOW
int__weakarch_show_interrupts(struct seq_file *p, int prec)kernel/irq/proc.c +0
-0@@ -514,10 +524,7 @@ int show_interrupts(struct seq_file *p,
guard(rcu)();
desc =irq_to_desc(i);kernel/irq/proc.c +1
-4-if (!desc ||irq_settings_is_hidden (desc)) -return 0 ; - -if (!desc->action ||irq_desc_is_chained (desc) || !desc->kstat_irqs) +if (!desc || !irq_settings_proc_valid (desc))return 0;
seq_printf(p, "%*d:", prec, i);kernel/irq/settings.h +1
-0@@ -18,6 +18,7 @@ enum { _IRQ_DISABLE_UNLAZY = IRQ_DISABLE_UNLAZY, _IRQ_HIDDEN = IRQ_HIDDEN, _IRQ_NO_DEBUG = IRQ_NO_DEBUG, + _IRQ_PROC_VALID =IRQ_RESERVED , _IRQF_MODIFY_MASK = IRQF_MODIFY_MASK, };kernel/irq/settings.h +1
-0@@ -34,6 +35,7 @@ enum { #define IRQ_DISABLE_UNLAZY GOT_YOU_MORON #define IRQ_HIDDEN GOT_YOU_MORON #define IRQ_NO_DEBUG GOT_YOU_MORON +#define IRQ_RESERVED GOT_YOU_MORON #undef IRQF_MODIFY_MASK #define IRQF_MODIFY_MASK GOT_YOU_MORONkernel/irq/settings.h +1
-0@@ -180,3 +182,14 @@ static inline bool irq_settings_no_debug { return desc->status_use_accessors & _IRQ_NO_DEBUG; } +
kernel/irq/settings.h static inline bool irq_settings_no_debug · line 195
Quoted 10 lines
kernel/irq/settings.h +10
-0+static inline bool irq_settings_proc_valid (struct irq_desc *desc) +{ +return desc->status_use_accessors & _IRQ_PROC_VALID; +} + +static inline void irq_settings_update_proc_valid (struct irq_desc *desc,u32 set) +{ + desc->status_use_accessors &= ~_IRQ_PROC_VALID; + desc->status_use_accessors |= (set & _IRQ_PROC_VALID); +}
Thomas Gleixner
maintainerPATCH v5 11/15 genirq: Calculate precision only when required
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
Calculating the precision of the interrupt number column on every initial show_interrupt() invocation is a pointless exercise as the underlying maximum number of interrupts rarely changes.
Calculate it only when that number is modified and let show_interrupts() use the cached value.
--- kernel/irq/internals.h | 6 ++++++ kernel/irq/irqdesc.c | 10 ++++++---- kernel/irq/proc.c | 33 +++++++++++++++++++++++---------- 3 files changed, 35 insertions(+), 14 deletions(-)
kernel/irq/internals.h +6 -0
@@ -134,6 +134,12 @@ static inline void unregister_handler_prstatic inline void irq_proc_update_valid (struct irq_desc *desc) { }#endif +#if defined(CONFIG_PROC_FS ) && defined(CONFIG_GENERIC_IRQ_SHOW ) +void irq_proc_calc_prec (void ); +#else +static inline void irq_proc_calc_prec (void ) { } +#endif +extern bool irq_can_set_affinity_usr (unsigned int irq); extern int irq_do_set_affinity(struct irq_data *data,
kernel/irq/irqdesc.c +6 -4
@@ -157,13 +157,12 @@ EXPORT_SYMBOL_GPL(irq_get_nr_irqs);* * Return: @nr. */ -unsigned int irq_set_nr_irqs (unsigned int nr) +unsigned int __init irq_set_nr_irqs (unsigned int nr) { total_nr_irqs = nr; - +irq_proc_calc_prec ();return nr; } -EXPORT_SYMBOL_GPL (irq_set_nr_irqs);static DEFINE_MUTEX (sparse_irq_lock); static struct maple_tree sparse_irqs = MTREE_INIT_EXT(sparse_irqs,
@@ -544,6 +543,7 @@ static bool irq_expand_nr_irqs(unsignedif (nr >MAX_SPARSE_IRQS )return false; total_nr_irqs = nr; +irq_proc_calc_prec ();return true; }
@@ -572,6 +572,7 @@ int __init early_irq_init(void) desc =alloc_desc (i, node,0 ,NULL ,NULL );irq_insert_desc (i, desc); } +irq_proc_calc_prec ();return arch_early_irq_init (); }
@@ -592,7 +593,7 @@ int __init early_irq_init(void)init_irq_default_affinity (); -printk (KERN_INFO "NR_IRQS: %d\n" ,NR_IRQS ); +pr_info ("NR_IRQS: %d\n" ,NR_IRQS ); count =ARRAY_SIZE (irq_desc);
@@ -602,6 +603,7 @@ int __init early_irq_init(void) goto __free_desc_res; } +irq_proc_calc_prec ();return arch_early_irq_init (); __free_desc_res:
kernel/irq/proc.c +23 -10
@@ -456,10 +456,25 @@ int __weak arch_show_interrupts(struct sreturn 0 ; } +static struct irq_proc_constraints { +unsigned int num_prec; +} irq_proc_constraints__read_mostly = { + .num_prec =3 , +}; +#ifndef ACTUAL_NR_IRQS # define ACTUAL_NR_IRQS total_nr_irqs#endif +void irq_proc_calc_prec (void ) +{ +unsigned int prec, n; + +for (prec =3 , n =1000 ; prec <10 && n <= total_nr_irqs; ++prec) + n *=10 ; +WRITE_ONCE (irq_proc_constraints.num_prec, prec); +} +#define ZSTR1 " 0"#define ZSTR1_LEN (sizeof(ZSTR1) - 1)#define ZSTR16 ZSTR1 ZSTR1 ZSTR1 ZSTR1 ZSTR1 ZSTR1 ZSTR1 ZSTR1 \
@@ -498,9 +513,7 @@ void irq_proc_emit_counts(struct seq_filint show_interrupts (struct seq_file *p,void *v) { -const unsigned int nr_irqs = total_nr_irqs; -static int prec; - +unsigned int prec =READ_ONCE (irq_proc_constraints.num_prec);int i = *(loff_t *) v, j;struct irqaction *action;struct irq_desc *desc;
@@ -513,9 +526,6 @@ int show_interrupts(struct seq_file *p,/* print header and calculate the width of the first column */ if (i ==0 ) { -for (prec =3 , j =1000 ; prec <10 && j <= nr_irqs; ++prec) - j *=10 ; -seq_printf (p,"%*s" , prec +8 ,"" );for_each_online_cpu (j)seq_printf (p,"CPU%-8d" , j);
@@ -551,13 +561,16 @@ int show_interrupts(struct seq_file *p, }else {seq_printf (p,"%8s" ,"None" ); } + +seq_putc (p,' ' );if (desc->irq_data.domain) -seq_printf (p," %*lu" , prec, desc->irq_data.hwirq); +seq_put_decimal_ull_width (p,"" , desc->irq_data.hwirq, prec);else seq_printf (p," %*s" , prec,"" ); -#ifdef CONFIG_GENERIC_IRQ_SHOW_LEVEL -seq_printf (p," %-8s" ,irqd_is_level_type (&desc->irq_data) ?"Level" :"Edge" ); -#endif + +if (IS_ENABLED (CONFIG_GENERIC_IRQ_SHOW_LEVEL )) +seq_printf (p," %-8s" ,irqd_is_level_type (&desc->irq_data) ?"Level" :"Edge" ); +if (desc->name)seq_printf (p,"-%-8s" , desc->name);
Thomas Gleixner
maintainerPATCH v5 12/15 genirq: Add rcuref count to struct irq_desc
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
Prepare for a smarter iterator for /proc/interrupts so that the next interrupt descriptor can be cached after lookup.
--- include/linux/irqdesc.h | 2 ++ kernel/irq/internals.h | 17 ++++++++++++++++- kernel/irq/irqdesc.c | 21 +++++++++++++-------- 3 files changed, 31 insertions(+), 9 deletions(-)
include/linux/irqdesc.h +2 -0
@@ -70,6 +70,7 @@ struct irq_redirect {* IRQF_NO_SUSPEND set * @force_resume_depth: number of irqactions on a irq descriptor with * IRQF_FORCE_RESUME set +* @refcnt: Reference count mainly for /proc/interrupts * @rcu: rcu head for delayed free * @kobj: kobject used to represent this struct in sysfs * @request_mutex: mutex to protect request/free before locking desc->lock
@@ -119,6 +120,7 @@ struct irq_desc {struct dentry *debugfs_file;const char *dev_name;#endif +rcuref_t refcnt;#ifdef CONFIG_SPARSE_IRQ struct rcu_head rcu;struct kobject kobj;
kernel/irq/internals.h +16 -1
@@ -9,6 +9,7 @@#include <linux/irqdesc.h> #include <linux/kernel_stat.h> #include <linux/pm_runtime.h> +#include <linux/rcuref.h> #include <linux/sched/clock.h> #ifdef CONFIG_SPARSE_IRQ
@@ -101,9 +102,23 @@ extern void unmask_irq(struct irq_desc *extern void unmask_threaded_irq (struct irq_desc *desc);#ifdef CONFIG_SPARSE_IRQ -static inline void irq_mark_irq (unsigned int irq) { } +static __always_inline void irq_mark_irq(unsigned int irq) { } +void irq_desc_free_rcu (struct irq_desc *desc); + +static __always_inline bool irq_desc_get_ref(struct irq_desc *desc) +{ +return rcuref_get (&desc->refcnt); +} + +static __always_inline void irq_desc_put_ref(struct irq_desc *desc) +{ +if (rcuref_put (&desc->refcnt)) +irq_desc_free_rcu (desc); +}#else extern void irq_mark_irq (unsigned int irq); +static __always_inline bool irq_desc_get_ref(struct irq_desc *desc) { return true; } +static __always_inline void irq_desc_put_ref(struct irq_desc *desc) { }#endif irqreturn_t __handle_irq_event_percpu (struct irq_desc *desc);
kernel/irq/irqdesc.c +13 -8
@@ -137,6 +137,7 @@ static void desc_set_defaults(unsigned i desc->tot_count =0 ; desc->name =NULL ; desc->owner = owner; +rcuref_init (&desc->refcnt,1 );desc_smp_init (desc, node, affinity); }
@@ -465,6 +466,17 @@ static void delayed_free_desc(struct rcukobject_put (&desc->kobj); } +void irq_desc_free_rcu (struct irq_desc *desc) +{ +/* +* We free the descriptor, masks and stat fields via RCU. That +* allows demultiplex interrupts to do rcu based management of +* the child interrupts. +* This also allows us to use rcu in kstat_irqs_usr(). +*/ +call_rcu (&desc->rcu, delayed_free_desc); +} +static void free_desc (unsigned int irq) {struct irq_desc *desc =irq_to_desc (irq);
@@ -483,14 +495,7 @@ static void free_desc(unsigned int irq)*/ irq_sysfs_del (desc);delete_irq_desc (irq); - -/* -* We free the descriptor, masks and stat fields via RCU. That -* allows demultiplex interrupts to do rcu based management of -* the child interrupts. -* This also allows us to use rcu in kstat_irqs_usr(). -*/ -call_rcu (&desc->rcu, delayed_free_desc); +irq_desc_put_ref (desc); } static int alloc_descs(unsigned int start, unsigned int cnt, int node,
Thomas Gleixner
maintainerPATCH v5 13/15 genirq: Expose irq_find_desc_at_or_after() in core code
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
... in preparation for a smarter iterator for /proc/interrupts.
---
V3: Add missing RCU guard and a lockdep assert - Dmitry
--- kernel/irq/internals.h | 2 ++ kernel/irq/irqdesc.c | 15 ++++++++------- 2 files changed, 10 insertions(+), 7 deletions(-)
kernel/irq/internals.h +2 -0
@@ -155,6 +155,8 @@ void irq_proc_calc_prec(void);static inline void irq_proc_calc_prec (void ) { }#endif +struct irq_desc *irq_find_desc_at_or_after (unsigned int offset); +extern bool irq_can_set_affinity_usr (unsigned int irq); extern int irq_do_set_affinity(struct irq_data *data,
kernel/irq/irqdesc.c +8 -7
@@ -181,15 +181,12 @@ static int irq_find_free_area(unsigned ireturn mas.index; } -static unsigned int irq_find_at_or_after (unsigned int offset) +struct irq_desc *irq_find_desc_at_or_after (unsigned int offset) {unsigned long index = offset; -struct irq_desc *desc; - -guard (rcu)(); - desc =mt_find (&sparse_irqs, &index, total_nr_irqs); -return desc ?irq_desc_get_irq (desc) : total_nr_irqs; +lockdep_assert_in_rcu_read_lock (); +return mt_find (&sparse_irqs, &index, total_nr_irqs); }static void irq_insert_desc (unsigned int irq,struct irq_desc *desc)
@@ -934,7 +931,11 @@ EXPORT_SYMBOL_GPL(__irq_alloc_descs);*/ unsigned int irq_get_next_irq (unsigned int offset) { -return irq_find_at_or_after (offset); +struct irq_desc *desc; + +guard (rcu)(); + desc =irq_find_desc_at_or_after (offset); +return desc ?irq_desc_get_irq (desc) : total_nr_irqs; } struct irq_desc *__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus,
Thomas Gleixner
maintainerPATCH v5 14/15 genirq/proc: Runtime size the chip name
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
The chip name column in the /proc/interrupt output is 8 characters and right aligned, which causes visual clutter due to the fixed length and the alignment. Many interrupt chips, e.g. PCI/MSI[X] have way longer names.
Update the length when a chip is assigned to an interrupt and utilize this information for the output. Align it left so all chip names start at the begin of the column.
Update the GDB script as well.
---
V3: New patch
--- kernel/irq/chip.c | 6 ++++-- kernel/irq/internals.h | 8 ++------ kernel/irq/irqdomain.c | 3 +++ kernel/irq/proc.c | 33 ++++++++++++++++++++++++++++----- kernel/irq/proc.h | 9 +++++++++ scripts/gdb/linux/interrupts.py | 25 ++++++++++++++++--------- 6 files changed, 62 insertions(+), 22 deletions(-)
kernel/irq/chip.c +4 -2
@@ -46,9 +46,11 @@ int irq_set_chip(unsigned int irq, const scoped_irqdesc->irq_data.chip = (struct irq_chip *)(chip ?: &no_irq_chip); ret =0 ; } -/* For !CONFIG_SPARSE_IRQ make the irq show up in allocated_irqs. */ -if (!ret) +if (!ret) { +/* For !CONFIG_SPARSE_IRQ make the irq show up in allocated_irqs. */ irq_mark_irq (irq); +irq_proc_update_chip (chip); + }return ret; }EXPORT_SYMBOL (irq_set_chip);
kernel/irq/internals.h +2 -6
@@ -12,6 +12,8 @@#include <linux/rcuref.h> #include <linux/sched/clock.h> +#include "proc.h" +#ifdef CONFIG_SPARSE_IRQ # define MAX_SPARSE_IRQS INT_MAX#else
@@ -149,12 +151,6 @@ static inline void unregister_handler_prstatic inline void irq_proc_update_valid (struct irq_desc *desc) { }#endif -#if defined(CONFIG_PROC_FS ) && defined(CONFIG_GENERIC_IRQ_SHOW ) -void irq_proc_calc_prec (void ); -#else -static inline void irq_proc_calc_prec (void ) { } -#endif -struct irq_desc *irq_find_desc_at_or_after (unsigned int offset);extern bool irq_can_set_affinity_usr (unsigned int irq);
kernel/irq/irqdomain.c +3 -0
@@ -20,6 +20,8 @@#include <linux/smp.h> #include <linux/fs.h> +#include "proc.h" +static LIST_HEAD (irq_domain_list);static DEFINE_MUTEX (irq_domain_mutex);
@@ -1532,6 +1534,7 @@ int irq_domain_set_hwirq_and_chip(struct irq_data->chip = (struct irq_chip *)(chip ? chip : &no_irq_chip); irq_data->chip_data = chip_data; +irq_proc_update_chip (chip);return 0 ; }EXPORT_SYMBOL_GPL (irq_domain_set_hwirq_and_chip);
kernel/irq/proc.c +28 -5
@@ -456,10 +456,14 @@ int __weak arch_show_interrupts(struct sreturn 0 ; } +static DEFINE_RAW_SPINLOCK (irq_proc_constraints_lock); +static struct irq_proc_constraints {unsigned int num_prec; +unsigned int chip_width; } irq_proc_constraints__read_mostly = { .num_prec =3 , + .chip_width =8 , };#ifndef ACTUAL_NR_IRQS
@@ -472,7 +476,23 @@ void irq_proc_calc_prec(void)for (prec =3 , n =1000 ; prec <10 && n <= total_nr_irqs; ++prec) n *=10 ; -WRITE_ONCE (irq_proc_constraints.num_prec, prec); + +guard (raw_spinlock_irqsave)(&irq_proc_constraints_lock); +if (prec > irq_proc_constraints.num_prec) +WRITE_ONCE (irq_proc_constraints.num_prec, prec); +} + +void irq_proc_update_chip (const struct irq_chip *chip) +{ +unsigned int len = chip && chip->name ?strlen (chip->name) :0 ; + +if (!len || len <=READ_ONCE (irq_proc_constraints.chip_width)) +return ; + +/* Can be invoked from interrupt disabled contexts */ +guard (raw_spinlock_irqsave)(&irq_proc_constraints_lock); +if (len > irq_proc_constraints.chip_width) +WRITE_ONCE (irq_proc_constraints.chip_width, len); }#define ZSTR1 " 0"
@@ -513,6 +533,7 @@ void irq_proc_emit_counts(struct seq_filint show_interrupts (struct seq_file *p,void *v) { +unsigned int chip_width =READ_ONCE (irq_proc_constraints.chip_width);unsigned int prec =READ_ONCE (irq_proc_constraints.num_prec);int i = *(loff_t *) v, j;struct irqaction *action;
@@ -548,18 +569,20 @@ int show_interrupts(struct seq_file *p,irq_proc_emit_counts (p, &desc->kstat_irqs->cnt); elseirq_proc_emit_zero_counts (p ,num_online_cpus ()); -seq_putc (p,' ' ); + +/* Enforce a visual gap */ +seq_write (p," " ,2 );guard (raw_spinlock_irq)(&desc->lock);if (desc->irq_data.chip) {if (desc->irq_data.chip->irq_print_chip) desc->irq_data.chip->irq_print_chip (&desc->irq_data, p);else if (desc->irq_data.chip->name) -seq_printf (p,"%8s" , desc->irq_data.chip->name); +seq_printf (p,"%-*s" , chip_width, desc->irq_data.chip->name);else -seq_printf (p,"%8s" ,"-" ); +seq_printf (p,"%-*s" , chip_width,"-" ); }else { -seq_printf (p,"%8s" ,"None" ); +seq_printf (p,"%-*s" , chip_width,"None" ); }seq_putc (p,' ' );
kernel/irq/proc.h new file +9 -0
@@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#if defined(CONFIG_PROC_FS ) && defined(CONFIG_GENERIC_IRQ_SHOW ) +void irq_proc_calc_prec (void ); +void irq_proc_update_chip (const struct irq_chip *chip); +#else +static inline void irq_proc_calc_prec (void ) { } +static inline void irq_proc_update_chip (const struct irq_chip *chip) { } +#endif
scripts/gdb/linux/interrupts.py +16 -9
@@ -20,7 +20,7 @@ irq_desc_type = utils.CachedType("struct def irqd_is_level(desc): return desc['irq_data']['common']['state_use_accessors'] & constants.LX_IRQD_LEVEL -def show_irq_desc(prec, irq): +def show_irq_desc(prec, chip_width, irq): text = "" desc = mapletree.mtree_load(gdb.parse_and_eval("&sparse_irqs"), irq)
@@ -48,7 +48,7 @@ irq_desc_type = utils.CachedType("struct count = cpus.per_cpu(desc['kstat_irqs'], cpu)['cnt'] else: count = 0 - text += "%10u" % (count) + text += "%10u " % (count) name = "None" if desc['irq_data']['chip']:
@@ -58,7 +58,7 @@ irq_desc_type = utils.CachedType("struct else: name = "-" - text += " %8s" % (name) + text += " %-*s" % (chip_width, name) if desc['irq_data']['domain']: text += " %*lu" % (prec, desc['irq_data']['hwirq'])
@@ -175,11 +175,18 @@ irq_desc_type = utils.CachedType("struct def invoke(self, arg, from_tty): nr_irqs = gdb.parse_and_eval("total_nr_irqs") - prec = 3 - j = 1000 - while prec < 10 and j <= nr_irqs: - prec += 1 - j *= 10 + constr = utils.gdb_eval_or_none('irq_proc_constraints') + + if constr: + prec = int(constr['num_prec']) + chip_width = int(constr['chip_width']) + else: + prec = 3 + j = 1000 + while prec < 10 and j <= nr_irqs: + prec += 1 + j *= 10 + chip_width = 8 gdb.write("%*s" % (prec + 8, "")) for cpu in cpus.each_online_cpu():
@@ -190,7 +197,7 @@ irq_desc_type = utils.CachedType("struct raise gdb.GdbError("Unable to find the sparse IRQ tree, is CONFIG_SPARSE_IRQ enabled?") for irq in range(nr_irqs): - gdb.write(show_irq_desc(prec, irq)) + gdb.write(show_irq_desc(prec, chip_width, irq)) gdb.write(arch_show_interrupts(prec))
Thomas Gleixner
maintainerPATCH v5 15/15 genirq/proc: Speed up /proc/interrupts iteration
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
Reading /proc/interrupts iterates over the interrupt number space one by one and looks up the descriptors one by one. That's just a waste of time.
When CONFIG_GENERIC_IRQ_SHOW is enabled this can utilize the maple tree and cache the descriptor pointer efficiently for the sequence file operations.
Implement a CONFIG_GENERIC_IRQ_SHOW specific version in the core code and leave the fs/proc/ variant for the legacy architectures which ignore generic code.
This reduces the time wasted for looking up the next record significantly.
---
V5: Reorder the header print - Dmitry
--- fs/proc/Makefile | 4 + kernel/irq/proc.c | 118 ++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 95 insertions(+), 27 deletions(-)
fs/proc/Makefile +3 -1
@@ -16,7 +16,9 @@ proc-y += cmdline.o proc-y += consoles.o proc-y += cpuinfo.o proc-y += devices.o -proc-y += interrupts.o +ifneq ($(CONFIG_GENERIC_IRQ_SHOW),y) +proc-y += interrupts.o +endif proc-y += loadavg.o proc-y += meminfo.o proc-y += stat.o
kernel/irq/proc.c +92 -26
160 lines not on this page · raw message
From: Thomas Gleixner <[email protected]> Sent: Wednesday, April 1, 2026 2:51 PM
Quoted 7 lines
This is a follow up to v4 which can be found here:
https://lore.kernel.org/[email protected]
The v1 cover letter contains a full analysis, explanation and numbers:
Quoted 8 lines
https://lore.kernel.org/[email protected]
TLDR:
- The performance of reading of
/proc/interruptshas been improved piecewise over the years, but most of the low hanging fruit has been left on the table.
As I did with v2 of the patch series, I've tested in Hyper-V guests on x86/x64 and arm64. Did basic smoke tests of taking a CPU offline, and removing a PCI device along with its IRQs, then adding them back again. I did not do anything with the new binary interface or the gdb Python script.
The improved alignment looks good. But if you want to be picky about the alignment, I noticed these things:
On x86, the leftmost column now correctly aligns the "VPMI:" label. But the rightmost column does not correctly align the text "Perf Guest Mediated PMI". It needs one additional space. Here's my output (with some CPU columns removed so it's not so wide):
root@mhkubun:~# cat /proc/interrupts | cut -b 1-30,64-
CPU0 CPU1 CPU5 CPU6 CPU7
8: 0 0 0 0 0 IO-APIC 8-edge rtc0
9: 0 0 0 0 0 IO-APIC 9-fasteoi acpi
NMI: 0 0 0 0 0 Non-maskable interrupts
LOC: 0 0 0 0 0 Local timer interrupts
PMI: 0 0 0 0 0 Performance monitoring interrupts
IWI: 1 0 0 0 0 IRQ work interrupts
RES: 325 231 235 341 231 Rescheduling interrupts
CAL: 11547 7178 9033 8798 6738 Function call interrupts
TLB: 0 0 0 0 0 TLB shootdowns
TRM: 0 0 0 0 0 Thermal event interrupt
THR: 0 0 0 0 0 Threshold APIC interrupts
MCE: 0 0 0 0 0 Machine check exceptions
MCP: 3 3 3 3 3 Machine check polls
HYP: 7300 5185 278 6960 1213 Hypervisor callback interrupts
HRE: 0 0 0 0 0 Hyper-V reenlightenment interrupts
HVS: 8963 4359 9784 8374 61709 Hyper-V stimer0 interrupts
PIN: 0 0 0 0 0 Posted-interrupt notification event
NPI: 0 0 0 0 0 Nested posted-interrupt event
PIW: 0 0 0 0 0 Posted-interrupt wakeup event
VPMI: 0 0 0 0 0 Perf Guest Mediated PMIOn arm64, the leftmost column doesn't align the IPI<n> entries. Neither does the rightmost column for the IPI<n> entries. Here's some of my output:
45: 0 0 80 0 0 HV-PCI-MSIX-0817:00:02.0 14 Edge mlx5_comp13@pci:0817:00:02.0 46: 0 0 0 10 0 HV-PCI-MSIX-0817:00:02.0 15 Edge mlx5_comp14@pci:0817:00:02.0 47: 0 0 0 0 45 HV-PCI-MSIX-0817:00:02.0 16 Edge mlx5_comp15@pci:0817:00:02.0 IPI0: 906 536 649 495 606 Rescheduling interrupts IPI1: 28844 12457 14770 55242 39175 Function call interrupts IPI2: 0 0 0 0 0 CPU stop interrupts IPI3: 0 0 0 0 0 CPU stop NMIs IPI4: 0 0 0 0 0 Timer broadcast interrupts IPI5: 11 1 5 0 2 IRQ work interrupts IPI6: 0 0 0 0 0 CPU backtrace interrupts IPI7: 0 0 0 0 0 KGDB roundup interrupts Err: 0
The alignment notwithstanding, for the series (excluding the binary interface and gdb Python),
Thomas Gleixner
maintainerQuoted 23 lines · Michael Kelley
From: Thomas Gleixner <[email protected]> Sent: Wednesday, April 1, 2026 2:51 PM The improved alignment looks good. But if you want to be picky about the alignment, I noticed these things:
On x86, the leftmost column now correctly aligns the "VPMI:" label. But the rightmost column does not correctly align the text "Perf Guest Mediated PMI". It needs one additional space. Here's my output (with some CPU columns removed so it's not so wide):
root@mhkubun:~# cat /proc/interrupts | cut -b 1-30,64- CPU0 CPU1 CPU5 CPU6 CPU7 8: 0 0 0 0 0 IO-APIC 8-edge rtc0 9: 0 0 0 0 0 IO-APIC 9-fasteoi acpi NMI: 0 0 0 0 0 Non-maskable interrupts LOC: 0 0 0 0 0 Local timer interrupts PMI: 0 0 0 0 0 Performance monitoring interrupts IWI: 1 0 0 0 0 IRQ work interrupts RES: 325 231 235 341 231 Rescheduling interrupts CAL: 11547 7178 9033 8798 6738 Function call interrupts TLB: 0 0 0 0 0 TLB shootdowns TRM: 0 0 0 0 0 Thermal event interrupt THR: 0 0 0 0 0 Threshold APIC interrupts MCE: 0 0 0 0 0 Machine check exceptions
Quoted 8 lines
MCP: 3 3 3 3 3 Machine check polls HYP: 7300 5185 278 6960 1213 Hypervisor callback interrupts HRE: 0 0 0 0 0 Hyper-V reenlightenment interrupts HVS: 8963 4359 9784 8374 61709 Hyper-V stimer0 interrupts PIN: 0 0 0 0 0 Posted-interrupt notification event NPI: 0 0 0 0 0 Nested posted-interrupt event PIW: 0 0 0 0 0 Posted-interrupt wakeup event VPMI: 0 0 0 0 0 Perf Guest Mediated PMI
That's weird. Let me have a look.
Quoted 4 lines
On arm64, the leftmost column doesn't align the IPI<n> entries. Neither does the rightmost column for the IPI<n> entries. Here's some of my output:
Quoted 12 lines
45: 0 0 80 0 0 HV-PCI-MSIX-0817:00:02.0 14 Edge mlx5_comp13@pci:0817:00:02.0 46: 0 0 0 10 0 HV-PCI-MSIX-0817:00:02.0 15 Edge mlx5_comp14@pci:0817:00:02.0 47: 0 0 0 0 45 HV-PCI-MSIX-0817:00:02.0 16 Edge mlx5_comp15@pci:0817:00:02.0 IPI0: 906 536 649 495 606 Rescheduling interrupts IPI1: 28844 12457 14770 55242 39175 Function call interrupts IPI2: 0 0 0 0 0 CPU stop interrupts IPI3: 0 0 0 0 0 CPU stop NMIs IPI4: 0 0 0 0 0 Timer broadcast interrupts IPI5: 11 1 5 0 2 IRQ work interrupts IPI6: 0 0 0 0 0 CPU backtrace interrupts IPI7: 0 0 0 0 0 KGDB roundup interrupts Err: 0
So the problem is that the default width for the interupt number is 3, while the IPI entries need 4. I can I make that default to 4.
The text of the IPIs is prepadded in arm64's arch_show_interrupts().
Thomas Gleixner
maintainerQuoted 9 lines · Michael Kelley
On x86, the leftmost column now correctly aligns the "VPMI:" label. But the rightmost column does not correctly align the text "Perf Guest Mediated PMI". It needs one additional space. Here's my output (with some CPU columns removed so it's not so wide):
root@mhkubun:~# cat /proc/interrupts | cut -b 1-30,64- CPU0 CPU1 CPU5 CPU6 CPU7 PIW: 0 0 0 0 0 Posted-interrupt wakeup event VPMI: 0 0 0 0 0 Perf Guest Mediated PMI
Right. There is a missing space in the VPMI text.
Quoted 4 lines
On arm64, the leftmost column doesn't align the IPI<n> entries. Neither does the rightmost column for the IPI<n> entries. Here's some of my output:
Quoted 11 lines
45: 0 0 80 0 0 HV-PCI-MSIX-0817:00:02.0 14 Edge mlx5_comp13@pci:0817:00:02.0 46: 0 0 0 10 0 HV-PCI-MSIX-0817:00:02.0 15 Edge mlx5_comp14@pci:0817:00:02.0 47: 0 0 0 0 45 HV-PCI-MSIX-0817:00:02.0 16 Edge mlx5_comp15@pci:0817:00:02.0 IPI0: 906 536 649 495 606 Rescheduling interrupts IPI1: 28844 12457 14770 55242 39175 Function call interrupts IPI2: 0 0 0 0 0 CPU stop interrupts IPI3: 0 0 0 0 0 CPU stop NMIs IPI4: 0 0 0 0 0 Timer broadcast interrupts IPI5: 11 1 5 0 2 IRQ work interrupts IPI6: 0 0 0 0 0 CPU backtrace interrupts IPI7: 0 0 0 0 0 KGDB roundup interrupts
That means total_nr_irqs is < 1000, which makes it use precision of
3. That obiously is not enough for the IPIn output and already an issue today. The IPI text is misaligned on ARM64 already today in pretty much the same way as with the patches applied. That's trivial to fix.
As a bonus ARM(64) has quite some interrupt chips which have their own print routine to show the interupt chip name. That makes it even more randomly aligned. That's also an existing problem and a larger effort to mop up and as it's already a mess. I leave it so.
x86 suffers from the leftmost column issue as well when you make NR_CPUS small enough in case that VPMI is emitted because the default precision in the core is 3 digits.
Thanks,
tglx