]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
x86/nmi: Add new NMI queues to deal with IO_CHK and SERR
authorMaxim Uvarov <maxim.uvarov@oracle.com>
Thu, 9 Aug 2012 15:14:24 +0000 (08:14 -0700)
committerMaxim Uvarov <maxim.uvarov@oracle.com>
Mon, 13 Aug 2012 13:00:30 +0000 (06:00 -0700)
In discussions with Thomas Mingarelli about hpwdt, he explained
to me some issues they were some when using their virtual NMI
button to test the hpwdt driver.

It turns out the virtual NMI button used on HP's machines do no
send unknown NMIs but instead send IO_CHK NMIs.  The way the
kernel code is written, the hpwdt driver can not register itself
against that type of NMI and therefore can not successfully
capture system information before panic'ing.

To solve this I created two new NMI queues to allow driver to
register against the IO_CHK and SERR NMIs.  Or in the hpwdt all
three (if you include unknown NMIs too).

The change is straightforward and just mimics what the unknown
NMI does.

Reported-and-tested-by: Thomas Mingarelli <thomas.mingarelli@hp.com>
Signed-off-by: Don Zickus <dzickus@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Link: http://lkml.kernel.org/r/1333051877-15755-3-git-send-email-dzickus@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Conflicts:

arch/x86/kernel/nmi.c
drivers/watchdog/hpwdt.c

arch/x86/include/asm/nmi.h
arch/x86/kernel/nmi.c
drivers/watchdog/hpwdt.c

index 480b69b75756595f1db3340c08f0acad7f233221..897fd775b62961a9b44f4455253ac6a277c11db9 100644 (file)
@@ -47,6 +47,8 @@ void arch_trigger_all_cpu_backtrace(void);
 enum {
        NMI_LOCAL=0,
        NMI_UNKNOWN,
+       NMI_SERR,
+       NMI_IO_CHECK,
        NMI_MAX
 };
 
index 3d795f077b15b65373e7ed96a32c03cf344d868e..fc9a4ea091fea3b07447a0b241b9902db3460d7e 100644 (file)
@@ -49,6 +49,14 @@ static struct nmi_desc nmi_desc[NMI_MAX] =
                .lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[1].lock),
                .head = LIST_HEAD_INIT(nmi_desc[1].head),
        },
+       {
+               .lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[2].lock),
+               .head = LIST_HEAD_INIT(nmi_desc[2].head),
+       },
+       {
+               .lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[3].lock),
+               .head = LIST_HEAD_INIT(nmi_desc[3].head),
+       },
 
 };
 
@@ -103,6 +111,15 @@ static int __setup_nmi(unsigned int type, struct nmiaction *action)
 
        spin_lock_irqsave(&desc->lock, flags);
 
+       /*
+        * most handlers of type NMI_UNKNOWN never return because
+        * they just assume the NMI is theirs.  Just a sanity check
+        * to manage expectations
+        */
+       WARN_ON_ONCE(type == NMI_UNKNOWN && !list_empty(&desc->head));
+       WARN_ON_ONCE(type == NMI_SERR && !list_empty(&desc->head));
+       WARN_ON_ONCE(type == NMI_IO_CHECK && !list_empty(&desc->head));
+
        /*
         * some handlers need to be executed first otherwise a fake
         * event confuses some handlers (kdump uses this flag)
@@ -194,6 +211,10 @@ EXPORT_SYMBOL_GPL(unregister_nmi_handler);
 static notrace __kprobes void
 pci_serr_error(unsigned char reason, struct pt_regs *regs)
 {
+       /* check to see if anyone registered against these types of errors */
+       if (nmi_handle(NMI_SERR, regs, false))
+               return;
+
        pr_emerg("NMI: PCI system error (SERR) for reason %02x on CPU %d.\n",
                 reason, smp_processor_id());
 
@@ -223,6 +244,10 @@ io_check_error(unsigned char reason, struct pt_regs *regs)
 {
        unsigned long i;
 
+       /* check to see if anyone registered against these types of errors */
+       if (nmi_handle(NMI_IO_CHECK, regs, false))
+               return;
+
        pr_emerg(
        "NMI: IOCK error (debug interrupt?) for reason %02x on CPU %d.\n",
                 reason, smp_processor_id());
index 57a848bc070e8db3df5a3d3eea6b3fc6a9060e85..40fece9f4afd4d94cd7f52d2aca48613c2654588 100644 (file)
@@ -744,17 +744,15 @@ static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev)
         * die notify list to handle a critical NMI. The default is to
         * be last so other users of the NMI signal can function.
         */
-       if (priority)
-               die_notifier.priority = 0x7FFFFFFF;
-
-       retval = register_die_notifier(&die_notifier);
-       if (retval != 0) {
-               dev_warn(&dev->dev,
-                       "Unable to register a die notifier (err=%d).\n",
-                       retval);
-               if (cru_rom_addr)
-                       iounmap(cru_rom_addr);
-       }
+       retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 0, "hpwdt");
+       if (retval)
+               goto error;
+       retval = register_nmi_handler(NMI_SERR, hpwdt_pretimeout, 0, "hpwdt");
+       if (retval)
+               goto error1;
+       retval = register_nmi_handler(NMI_IO_CHECK, hpwdt_pretimeout, 0, "hpwdt");
+       if (retval)
+               goto error2;
 
        dev_info(&dev->dev,
                        "HP Watchdog Timer Driver: NMI decoding initialized"
@@ -763,6 +761,18 @@ static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev)
                        (allow_kdump == 0) ? "OFF" : "ON",
                        (priority == 0) ? "LAST" : "FIRST");
        return 0;
+
+error2:
+       unregister_nmi_handler(NMI_SERR, "hpwdt");
+error1:
+       unregister_nmi_handler(NMI_UNKNOWN, "hpwdt");
+error:
+       dev_warn(&dev->dev,
+               "Unable to register a die notifier (err=%d).\n",
+               retval);
+       if (cru_rom_addr)
+               iounmap(cru_rom_addr);
+       return retval;
 }
 
 static void hpwdt_exit_nmi_decoding(void)