x86: add IRQ context simulation in module mce-inject
authorChen Gong <gong.chen@linux.intel.com>
Wed, 7 Dec 2011 17:21:37 +0000 (09:21 -0800)
committerTony Luck <tony.luck@intel.com>
Fri, 16 Dec 2011 19:20:02 +0000 (11:20 -0800)
mce-inject provides a mechanism to simulate errors so that test
scripts can check for correct operation of the kernel without
requiring any specialized hardware to create rare events.

The existing code can simulate events in normal process context
and also in NMI context - but not in IRQ context. This patch
fills that gap.

Link: https://lkml.org/lkml/2011/12/7/537
Signed-off-by: Chen Gong <gong.chen@linux.intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
arch/x86/include/asm/mce.h
arch/x86/kernel/cpu/mcheck/mce-inject.c

index 0e8ae57..9816583 100644 (file)
 #define MCJ_CTX_MASK           3
 #define MCJ_CTX(flags)         ((flags) & MCJ_CTX_MASK)
 #define MCJ_CTX_RANDOM         0    /* inject context: random */
-#define MCJ_CTX_PROCESS                1    /* inject context: process */
-#define MCJ_CTX_IRQ            2    /* inject context: IRQ */
-#define MCJ_NMI_BROADCAST      4    /* do NMI broadcasting */
-#define MCJ_EXCEPTION          8    /* raise as exception */
+#define MCJ_CTX_PROCESS                0x1  /* inject context: process */
+#define MCJ_CTX_IRQ            0x2  /* inject context: IRQ */
+#define MCJ_NMI_BROADCAST      0x4  /* do NMI broadcasting */
+#define MCJ_EXCEPTION          0x8  /* raise as exception */
+#define MCJ_IRQ_BRAODCAST      0x10 /* do IRQ broadcasting */
 
 /* Fields are zero when not available */
 struct mce {
index 319882e..fc4beb3 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/fs.h>
+#include <linux/preempt.h>
 #include <linux/smp.h>
 #include <linux/notifier.h>
 #include <linux/kdebug.h>
@@ -92,6 +93,18 @@ static int mce_raise_notify(unsigned int cmd, struct pt_regs *regs)
        return NMI_HANDLED;
 }
 
+static void mce_irq_ipi(void *info)
+{
+       int cpu = smp_processor_id();
+       struct mce *m = &__get_cpu_var(injectm);
+
+       if (cpumask_test_cpu(cpu, mce_inject_cpumask) &&
+                       m->inject_flags & MCJ_EXCEPTION) {
+               cpumask_clear_cpu(cpu, mce_inject_cpumask);
+               raise_exception(m, NULL);
+       }
+}
+
 /* Inject mce on current CPU */
 static int raise_local(void)
 {
@@ -139,9 +152,10 @@ static void raise_mce(struct mce *m)
                return;
 
 #ifdef CONFIG_X86_LOCAL_APIC
-       if (m->inject_flags & MCJ_NMI_BROADCAST) {
+       if (m->inject_flags & (MCJ_IRQ_BRAODCAST | MCJ_NMI_BROADCAST)) {
                unsigned long start;
                int cpu;
+
                get_online_cpus();
                cpumask_copy(mce_inject_cpumask, cpu_online_mask);
                cpumask_clear_cpu(get_cpu(), mce_inject_cpumask);
@@ -151,13 +165,25 @@ static void raise_mce(struct mce *m)
                            MCJ_CTX(mcpu->inject_flags) != MCJ_CTX_RANDOM)
                                cpumask_clear_cpu(cpu, mce_inject_cpumask);
                }
-               if (!cpumask_empty(mce_inject_cpumask))
-                       apic->send_IPI_mask(mce_inject_cpumask, NMI_VECTOR);
+               if (!cpumask_empty(mce_inject_cpumask)) {
+                       if (m->inject_flags & MCJ_IRQ_BRAODCAST) {
+                               /*
+                                * don't wait because mce_irq_ipi is necessary
+                                * to be sync with following raise_local
+                                */
+                               preempt_disable();
+                               smp_call_function_many(mce_inject_cpumask,
+                                       mce_irq_ipi, NULL, 0);
+                               preempt_enable();
+                       } else if (m->inject_flags & MCJ_NMI_BROADCAST)
+                               apic->send_IPI_mask(mce_inject_cpumask,
+                                               NMI_VECTOR);
+               }
                start = jiffies;
                while (!cpumask_empty(mce_inject_cpumask)) {
                        if (!time_before(jiffies, start + 2*HZ)) {
                                printk(KERN_ERR
-                               "Timeout waiting for mce inject NMI %lx\n",
+                               "Timeout waiting for mce inject %lx\n",
                                        *cpumask_bits(mce_inject_cpumask));
                                break;
                        }