x86: expand /proc/interrupts to include missing vectors, v2
authorJoe Korty <joe.korty@ccur.com>
Wed, 17 Oct 2007 16:04:40 +0000 (18:04 +0200)
committerThomas Gleixner <tglx@inhelltoy.tec.linutronix.de>
Wed, 17 Oct 2007 18:16:53 +0000 (20:16 +0200)
Add missing IRQs and IRQ descriptions to /proc/interrupts.

/proc/interrupts is most useful when it displays every IRQ vector in use by
the system, not just those somebody thought would be interesting.

This patch inserts the following vector displays to the i386 and x86_64
platforms, as appropriate:

rescheduling interrupts
TLB flush interrupts
function call interrupts
thermal event interrupts
threshold interrupts
spurious interrupts

A threshold interrupt occurs when ECC memory correction is occuring at too
high a frequency.  Thresholds are used by the ECC hardware as occasional
ECC failures are part of normal operation, but long sequences of ECC
failures usually indicate a memory chip that is about to fail.

Thermal event interrupts occur when a temperature threshold has been
exceeded for some CPU chip.  IIRC, a thermal interrupt is also generated
when the temperature drops back to a normal level.

A spurious interrupt is an interrupt that was raised then lowered by the
device before it could be fully processed by the APIC.  Hence the apic sees
the interrupt but does not know what device it came from.  For this case
the APIC hardware will assume a vector of 0xff.

Rescheduling, call, and TLB flush interrupts are sent from one CPU to
another per the needs of the OS.  Typically, their statistics would be used
to discover if an interrupt flood of the given type has been occuring.

AK: merged v2 and v4 which had some more tweaks
AK: replace Local interrupts with Local timer interrupts
AK: Fixed description of interrupt types.

[ tglx: arch/x86 adaptation ]
[ mingo: small cleanup ]

Signed-off-by: Joe Korty <joe.korty@ccur.com>
Signed-off-by: Andi Kleen <ak@suse.de>
Cc: Tim Hockin <thockin@hockin.org>
Cc: Andi Kleen <ak@suse.de>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
14 files changed:
Documentation/filesystems/proc.txt
arch/x86/kernel/apic_32.c
arch/x86/kernel/apic_64.c
arch/x86/kernel/cpu/mcheck/p4.c
arch/x86/kernel/irq_32.c
arch/x86/kernel/irq_64.c
arch/x86/kernel/mce_amd_64.c
arch/x86/kernel/mce_intel_64.c
arch/x86/kernel/smp_32.c
arch/x86/kernel/smp_64.c
arch/x86/mach-voyager/voyager_smp.c
arch/x86/xen/smp.c
include/asm-x86/hardirq_32.h
include/asm-x86/pda.h

index 4a37e25e694cbca0aa50d08f783bce606ca54d12..e5c1df52a876fe9ab83173966772960ce0b25a77 100644 (file)
@@ -347,7 +347,35 @@ connects the CPUs in a SMP system. This means that an error has been detected,
 the IO-APIC automatically retry the transmission, so it should not be a big
 problem, but you should read the SMP-FAQ.
 
-In this context it could be interesting to note the new irq directory in 2.4.
+In 2.6.2* /proc/interrupts was expanded again.  This time the goal was for
+/proc/interrupts to display every IRQ vector in use by the system, not
+just those considered 'most important'.  The new vectors are:
+
+  THR -- interrupt raised when a machine check threshold counter
+  (typically counting ECC corrected errors of memory or cache) exceeds
+  a configurable threshold.  Only available on some systems.
+
+  TRM -- a thermal event interrupt occurs when a temperature threshold
+  has been exceeded for the CPU.  This interrupt may also be generated
+  when the temperature drops back to normal.
+
+  SPU -- a spurious interrupt is some interrupt that was raised then lowered
+  by some IO device before it could be fully processed by the APIC.  Hence
+  the APIC sees the interrupt but does not know what device it came from.
+  For this case the APIC will generate the interrupt with a IRQ vector
+  of 0xff. This might also be generated by chipset bugs.
+
+  RES, CAL, TLB -- rescheduling, call and TLB flush interrupts are
+  sent from one CPU to another per the needs of the OS.  Typically,
+  their statistics are used by kernel developers and interested users to
+  determine the occurance of interrupt of the given type.
+
+The above IRQ vectors are displayed only when relevent.  For example,
+the threshold vector does not exist on x86_64 platforms.  Others are
+suppressed when the system is a uniprocessor.  As of this writing, only
+i386 and x86_64 platforms support the new IRQ vector displays.
+
+Of some interest is the introduction of the /proc/irq directory to 2.4.
 It could be used to set IRQ to CPU affinity, this means that you can "hook" an
 IRQ to only one CPU, or to exclude a CPU of handling IRQs. The contents of the
 irq subdir is one subdir for each IRQ, and one file; prof_cpu_mask
index 3d67ae18d762048a145c4211a0fa5f2ca174bf02..793341fffc81af62eb9049fbafd535d0c216eea9 100644 (file)
@@ -1277,6 +1277,7 @@ void smp_spurious_interrupt(struct pt_regs *regs)
        /* see sw-dev-man vol 3, chapter 7.4.13.5 */
        printk(KERN_INFO "spurious APIC interrupt on CPU#%d, "
               "should never happen.\n", smp_processor_id());
+       __get_cpu_var(irq_stat).irq_spurious_count++;
        irq_exit();
 }
 
index 2250c654eacc5d341a6c22b20f2a6a3a5d5c9081..f47bc493dba94fbbb4dd8b8ee68bbf3f445109b0 100644 (file)
@@ -1140,6 +1140,7 @@ asmlinkage void smp_spurious_interrupt(void)
        if (v & (1 << (SPURIOUS_APIC_VECTOR & 0x1f)))
                ack_APIC_irq();
 
+       add_pda(irq_spurious_count, 1);
        irq_exit();
 }
 
index 1509edfb2313010be2856f3639985d04abf6804b..be4dabfee1f5c9af40b1b61362f6e80ec1cc6ff1 100644 (file)
@@ -61,6 +61,7 @@ fastcall void smp_thermal_interrupt(struct pt_regs *regs)
 {
        irq_enter();
        vendor_thermal_interrupt(regs);
+       __get_cpu_var(irq_stat).irq_thermal_count++;
        irq_exit();
 }
 
index e173b763f148ad6ae032e68b590f87f519ecb159..10f359021aaeebec9e19e808ae5db2eb51ed7cff 100644 (file)
@@ -280,14 +280,41 @@ skip:
                seq_printf(p, "NMI: ");
                for_each_online_cpu(j)
                        seq_printf(p, "%10u ", nmi_count(j));
-               seq_putc(p, '\n');
+               seq_printf(p, "  Non-maskable interrupts\n");
 #ifdef CONFIG_X86_LOCAL_APIC
                seq_printf(p, "LOC: ");
                for_each_online_cpu(j)
                        seq_printf(p, "%10u ",
                                per_cpu(irq_stat,j).apic_timer_irqs);
-               seq_putc(p, '\n');
+               seq_printf(p, "  Local timer interrupts\n");
 #endif
+#ifdef CONFIG_SMP
+               seq_printf(p, "RES: ");
+               for_each_online_cpu(j)
+                       seq_printf(p, "%10u ",
+                               per_cpu(irq_stat,j).irq_resched_count);
+               seq_printf(p, "  Rescheduling interrupts\n");
+               seq_printf(p, "CAL: ");
+               for_each_online_cpu(j)
+                       seq_printf(p, "%10u ",
+                               per_cpu(irq_stat,j).irq_call_count);
+               seq_printf(p, "  function call interrupts\n");
+               seq_printf(p, "TLB: ");
+               for_each_online_cpu(j)
+                       seq_printf(p, "%10u ",
+                               per_cpu(irq_stat,j).irq_tlb_count);
+               seq_printf(p, "  TLB shootdowns\n");
+#endif
+               seq_printf(p, "TRM: ");
+               for_each_online_cpu(j)
+                       seq_printf(p, "%10u ",
+                               per_cpu(irq_stat,j).irq_thermal_count);
+               seq_printf(p, "  Thermal event interrupts\n");
+               seq_printf(p, "SPU: ");
+               for_each_online_cpu(j)
+                       seq_printf(p, "%10u ",
+                               per_cpu(irq_stat,j).irq_spurious_count);
+               seq_printf(p, "  Spurious interrupts\n");
                seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count));
 #if defined(CONFIG_X86_IO_APIC)
                seq_printf(p, "MIS: %10u\n", atomic_read(&irq_mis_count));
index 865669efc540fe0904201792115531b3d31924d2..3881189df8ee5658410f18a7a01d445d241eaaa1 100644 (file)
@@ -86,11 +86,37 @@ skip:
                seq_printf(p, "NMI: ");
                for_each_online_cpu(j)
                        seq_printf(p, "%10u ", cpu_pda(j)->__nmi_count);
-               seq_putc(p, '\n');
+               seq_printf(p, "  Non-maskable interrupts\n");
                seq_printf(p, "LOC: ");
                for_each_online_cpu(j)
                        seq_printf(p, "%10u ", cpu_pda(j)->apic_timer_irqs);
-               seq_putc(p, '\n');
+               seq_printf(p, "  Local timer interrupts\n");
+#ifdef CONFIG_SMP
+               seq_printf(p, "RES: ");
+               for_each_online_cpu(j)
+                       seq_printf(p, "%10u ", cpu_pda(j)->irq_resched_count);
+               seq_printf(p, "  Rescheduling interrupts\n");
+               seq_printf(p, "CAL: ");
+               for_each_online_cpu(j)
+                       seq_printf(p, "%10u ", cpu_pda(j)->irq_call_count);
+               seq_printf(p, "  function call interrupts\n");
+               seq_printf(p, "TLB: ");
+               for_each_online_cpu(j)
+                       seq_printf(p, "%10u ", cpu_pda(j)->irq_tlb_count);
+               seq_printf(p, "  TLB shootdowns\n");
+#endif
+               seq_printf(p, "TRM: ");
+               for_each_online_cpu(j)
+                       seq_printf(p, "%10u ", cpu_pda(j)->irq_thermal_count);
+               seq_printf(p, "  Thermal event interrupts\n");
+               seq_printf(p, "THR: ");
+               for_each_online_cpu(j)
+                       seq_printf(p, "%10u ", cpu_pda(j)->irq_threshold_count);
+               seq_printf(p, "  Threshold APIC interrupts\n");
+               seq_printf(p, "SPU: ");
+               for_each_online_cpu(j)
+                       seq_printf(p, "%10u ", cpu_pda(j)->irq_spurious_count);
+               seq_printf(p, "  Spurious interrupts\n");
                seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count));
        }
        return 0;
index 805b62b1e0dfa3c77adf0c68fb1cab083297379f..0d2afd96aca40b7ff5a458f1890b9422ba32873e 100644 (file)
@@ -237,6 +237,7 @@ asmlinkage void mce_threshold_interrupt(void)
                }
        }
 out:
+       add_pda(irq_threshold_count, 1);
        irq_exit();
 }
 
index 6551505d8a2cd8d0c7276d32334bef0b1aed0809..c17eaf5dd6dd4ea820f5057e14f2beb861821ec1 100644 (file)
@@ -26,6 +26,7 @@ asmlinkage void smp_thermal_interrupt(void)
        if (therm_throt_process(msr_val & 1))
                mce_log_therm_throt_event(smp_processor_id(), msr_val);
 
+       add_pda(irq_thermal_count, 1);
        irq_exit();
 }
 
index eebc6e82576c2dce8926b22505d1bc418b38412d..791d9f8036ae94019b2def24ed94a267414baf34 100644 (file)
@@ -342,6 +342,7 @@ fastcall void smp_invalidate_interrupt(struct pt_regs *regs)
        smp_mb__after_clear_bit();
 out:
        put_cpu_no_resched();
+       __get_cpu_var(irq_stat).irq_tlb_count++;
 }
 
 void native_flush_tlb_others(const cpumask_t *cpumaskp, struct mm_struct *mm,
@@ -640,6 +641,7 @@ static void native_smp_send_stop(void)
 fastcall void smp_reschedule_interrupt(struct pt_regs *regs)
 {
        ack_APIC_irq();
+       __get_cpu_var(irq_stat).irq_resched_count++;
 }
 
 fastcall void smp_call_function_interrupt(struct pt_regs *regs)
@@ -660,6 +662,7 @@ fastcall void smp_call_function_interrupt(struct pt_regs *regs)
         */
        irq_enter();
        (*func)(info);
+       __get_cpu_var(irq_stat).irq_call_count++;
        irq_exit();
 
        if (wait) {
index df4a82812adbbad89fe7d491c3ac7347553cb281..5c2964727d197faf6bc135e50c7c95399781bcae 100644 (file)
@@ -163,6 +163,7 @@ asmlinkage void smp_invalidate_interrupt(struct pt_regs *regs)
 out:
        ack_APIC_irq();
        cpu_clear(cpu, f->flush_cpumask);
+       add_pda(irq_tlb_count, 1);
 }
 
 static void flush_tlb_others(cpumask_t cpumask, struct mm_struct *mm,
@@ -493,6 +494,7 @@ void smp_send_stop(void)
 asmlinkage void smp_reschedule_interrupt(void)
 {
        ack_APIC_irq();
+       add_pda(irq_resched_count, 1);
 }
 
 asmlinkage void smp_call_function_interrupt(void)
@@ -514,6 +516,7 @@ asmlinkage void smp_call_function_interrupt(void)
        exit_idle();
        irq_enter();
        (*func)(info);
+       add_pda(irq_call_count, 1);
        irq_exit();
        if (wait) {
                mb();
index 1f86b529dbbbb63fe286037fac33aad58f21fe14..e4928aa6bdfb783a57363364c159b5223ad5262a 100644 (file)
@@ -1037,6 +1037,7 @@ smp_call_function_interrupt(void)
         */
        irq_enter();
        (*func)(info);
+       __get_cpu_var(irq_stat).irq_call_count++;
        irq_exit();
        if (wait) {
                mb();
index 4fa33c27ccb6d4d4e6a7619f7bd8c46c25f36a2c..6c058585459cd8fa9f1e7fec655601a38aae759a 100644 (file)
@@ -356,6 +356,7 @@ static irqreturn_t xen_call_function_interrupt(int irq, void *dev_id)
         */
        irq_enter();
        (*func)(info);
+       __get_cpu_var(irq_stat).irq_call_count++;
        irq_exit();
 
        if (wait) {
index 918863530b9279f7d269325ece6b0e2b365994d1..4f85f0f4b5631699e21f36016bb88ebe9b51a911 100644 (file)
@@ -10,6 +10,11 @@ typedef struct {
        unsigned int __nmi_count;       /* arch dependent */
        unsigned int apic_timer_irqs;   /* arch dependent */
        unsigned int irq0_irqs;
+       unsigned int irq_resched_count;
+       unsigned int irq_call_count;
+       unsigned int irq_tlb_count;
+       unsigned int irq_thermal_count;
+       unsigned int irq_spurious_count;
 } ____cacheline_aligned irq_cpustat_t;
 
 DECLARE_PER_CPU(irq_cpustat_t, irq_stat);
index fb49f80eb94f9f03929392f058e4669ca5eb6968..35962bbe5e72ad1219e576c911cf9af1f6aaecfe 100644 (file)
@@ -30,6 +30,12 @@ struct x8664_pda {
        struct mm_struct *active_mm;
        unsigned apic_timer_irqs;
        unsigned irq0_irqs;
+       unsigned irq_resched_count;
+       unsigned irq_call_count;
+       unsigned irq_tlb_count;
+       unsigned irq_thermal_count;
+       unsigned irq_threshold_count;
+       unsigned irq_spurious_count;
 } ____cacheline_aligned_in_smp;
 
 extern struct x8664_pda *_cpu_pda[];