]> err.no Git - linux-2.6/blobdiff - drivers/char/ipmi/ipmi_watchdog.c
Merge master.kernel.org:/home/rmk/linux-2.6-arm
[linux-2.6] / drivers / char / ipmi / ipmi_watchdog.c
index 147c12047cf3d83b6b345c4b8348f43e4ffc6a5d..41f78e2c158f453543ba8d315df7d0b580efa341 100644 (file)
 #include <linux/poll.h>
 #include <linux/string.h>
 #include <linux/ctype.h>
-#include <linux/delay.h>
 #include <asm/atomic.h>
 
-#ifdef CONFIG_X86
-/* This is ugly, but I've determined that x86 is the only architecture
-   that can reasonably support the IPMI NMI watchdog timeout at this
-   time.  If another architecture adds this capability somehow, it
-   will have to be a somewhat different mechanism and I have no idea
-   how it will work.  So in the unlikely event that another
-   architecture supports this, we can figure out a good generic
-   mechanism for it at that time. */
-#define HAVE_DIE_NMI_POST
+#ifdef CONFIG_X86_LOCAL_APIC
+#include <asm/apic.h>
 #endif
 
 #define        PFX "IPMI Watchdog: "
@@ -327,11 +319,6 @@ static unsigned char ipmi_version_minor;
 /* If a pretimeout occurs, this is used to allow only one panic to happen. */
 static atomic_t preop_panic_excl = ATOMIC_INIT(-1);
 
-#ifdef HAVE_DIE_NMI_POST
-static int testing_nmi;
-static int nmi_handler_registered;
-#endif
-
 static int ipmi_heartbeat(void);
 static void panic_halt_ipmi_heartbeat(void);
 
@@ -373,10 +360,6 @@ static int i_ipmi_set_timeout(struct ipmi_smi_msg  *smi_msg,
        int                               hbnow = 0;
 
 
-       /* These can be cleared as we are setting the timeout. */
-       ipmi_start_timer_on_heartbeat = 0;
-       pretimeout_since_last_heartbeat = 0;
-
        data[0] = 0;
        WDOG_SET_TIMER_USE(data[0], WDOG_TIMER_USE_SMS_OS);
 
@@ -451,12 +434,13 @@ static int ipmi_set_timeout(int do_heartbeat)
 
        wait_for_completion(&set_timeout_wait);
 
-       mutex_unlock(&set_timeout_lock);
-
        if ((do_heartbeat == IPMI_SET_TIMEOUT_FORCE_HB)
            || ((send_heartbeat_now)
                && (do_heartbeat == IPMI_SET_TIMEOUT_HB_IF_NECESSARY)))
+       {
                rv = ipmi_heartbeat();
+       }
+       mutex_unlock(&set_timeout_lock);
 
 out:
        return rv;
@@ -536,10 +520,12 @@ static int ipmi_heartbeat(void)
        int                               rv;
        struct ipmi_system_interface_addr addr;
 
-       if (ipmi_ignore_heartbeat)
+       if (ipmi_ignore_heartbeat) {
                return 0;
+       }
 
        if (ipmi_start_timer_on_heartbeat) {
+               ipmi_start_timer_on_heartbeat = 0;
                ipmi_watchdog_state = action_val;
                return ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB);
        } else if (pretimeout_since_last_heartbeat) {
@@ -547,6 +533,7 @@ static int ipmi_heartbeat(void)
                   We don't want to set the action, though, we want to
                   leave that alone (thus it can't be combined with the
                   above operation. */
+               pretimeout_since_last_heartbeat = 0;
                return ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY);
        }
 
@@ -934,45 +921,6 @@ static void ipmi_register_watchdog(int ipmi_intf)
                printk(KERN_CRIT PFX "Unable to register misc device\n");
        }
 
-#ifdef HAVE_DIE_NMI_POST
-       if (nmi_handler_registered) {
-               int old_pretimeout = pretimeout;
-               int old_timeout = timeout;
-               int old_preop_val = preop_val;
-
-               /* Set the pretimeout to go off in a second and give
-                  ourselves plenty of time to stop the timer. */
-               ipmi_watchdog_state = WDOG_TIMEOUT_RESET;
-               preop_val = WDOG_PREOP_NONE; /* Make sure nothing happens */
-               pretimeout = 99;
-               timeout = 100;
-
-               testing_nmi = 1;
-
-               rv = ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB);
-               if (rv) {
-                       printk(KERN_WARNING PFX "Error starting timer to"
-                              " test NMI: 0x%x.  The NMI pretimeout will"
-                              " likely not work\n", rv);
-                       rv = 0;
-                       goto out_restore;
-               }
-
-               msleep(1500);
-
-               if (testing_nmi != 2) {
-                       printk(KERN_WARNING PFX "IPMI NMI didn't seem to"
-                              " occur.  The NMI pretimeout will"
-                              " likely not work\n");
-               }
-       out_restore:
-               testing_nmi = 0;
-               preop_val = old_preop_val;
-               pretimeout = old_pretimeout;
-               timeout = old_timeout;
-       }
-#endif
-
  out:
        up_write(&register_sem);
 
@@ -982,10 +930,6 @@ static void ipmi_register_watchdog(int ipmi_intf)
                ipmi_watchdog_state = action_val;
                ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB);
                printk(KERN_INFO PFX "Starting now!\n");
-       } else {
-               /* Stop the timer now. */
-               ipmi_watchdog_state = WDOG_TIMEOUT_NONE;
-               ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB);
        }
 }
 
@@ -1022,28 +966,17 @@ static void ipmi_unregister_watchdog(int ipmi_intf)
        up_write(&register_sem);
 }
 
-#ifdef HAVE_DIE_NMI_POST
+#ifdef HAVE_NMI_HANDLER
 static int
-ipmi_nmi(struct notifier_block *self, unsigned long val, void *data)
+ipmi_nmi(void *dev_id, int cpu, int handled)
 {
-       if (val != DIE_NMI_POST)
-               return NOTIFY_OK;
-
-       if (testing_nmi) {
-               testing_nmi = 2;
-               return NOTIFY_STOP;
-       }
-
         /* If we are not expecting a timeout, ignore it. */
        if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE)
-               return NOTIFY_OK;
-
-       if (preaction_val != WDOG_PRETIMEOUT_NMI)
-               return NOTIFY_OK;
+               return NOTIFY_DONE;
 
        /* If no one else handled the NMI, we assume it was the IPMI
            watchdog. */
-       if (preop_val == WDOG_PREOP_PANIC) {
+       if ((!handled) && (preop_val == WDOG_PREOP_PANIC)) {
                /* On some machines, the heartbeat will give
                   an error and not work unless we re-enable
                   the timer.   So do so. */
@@ -1052,12 +985,18 @@ ipmi_nmi(struct notifier_block *self, unsigned long val, void *data)
                        panic(PFX "pre-timeout");
        }
 
-       return NOTIFY_STOP;
+       return NOTIFY_DONE;
 }
 
-static struct notifier_block ipmi_nmi_handler = {
-       .notifier_call = ipmi_nmi
+static struct nmi_handler ipmi_nmi_handler =
+{
+       .link     = LIST_HEAD_INIT(ipmi_nmi_handler.link),
+       .dev_name = "ipmi_watchdog",
+       .dev_id   = NULL,
+       .handler  = ipmi_nmi,
+       .priority = 0, /* Call us last. */
 };
+int nmi_handler_registered;
 #endif
 
 static int wdog_reboot_handler(struct notifier_block *this,
@@ -1174,7 +1113,7 @@ static int preaction_op(const char *inval, char *outval)
                preaction_val = WDOG_PRETIMEOUT_NONE;
        else if (strcmp(inval, "pre_smi") == 0)
                preaction_val = WDOG_PRETIMEOUT_SMI;
-#ifdef HAVE_DIE_NMI_POST
+#ifdef HAVE_NMI_HANDLER
        else if (strcmp(inval, "pre_nmi") == 0)
                preaction_val = WDOG_PRETIMEOUT_NMI;
 #endif
@@ -1208,7 +1147,7 @@ static int preop_op(const char *inval, char *outval)
 
 static void check_parms(void)
 {
-#ifdef HAVE_DIE_NMI_POST
+#ifdef HAVE_NMI_HANDLER
        int do_nmi = 0;
        int rv;
 
@@ -1221,9 +1160,20 @@ static void check_parms(void)
                        preop_op("preop_none", NULL);
                        do_nmi = 0;
                }
+#ifdef CONFIG_X86_LOCAL_APIC
+               if (nmi_watchdog == NMI_IO_APIC) {
+                       printk(KERN_WARNING PFX "nmi_watchdog is set to IO APIC"
+                              " mode (value is %d), that is incompatible"
+                              " with using NMI in the IPMI watchdog."
+                              " Disabling IPMI nmi pretimeout.\n",
+                              nmi_watchdog);
+                       preaction_val = WDOG_PRETIMEOUT_NONE;
+                       do_nmi = 0;
+               }
+#endif
        }
        if (do_nmi && !nmi_handler_registered) {
-               rv = register_die_notifier(&ipmi_nmi_handler);
+               rv = request_nmi(&ipmi_nmi_handler);
                if (rv) {
                        printk(KERN_WARNING PFX
                               "Can't register nmi handler\n");
@@ -1231,7 +1181,7 @@ static void check_parms(void)
                } else
                        nmi_handler_registered = 1;
        } else if (!do_nmi && nmi_handler_registered) {
-               unregister_die_notifier(&ipmi_nmi_handler);
+               release_nmi(&ipmi_nmi_handler);
                nmi_handler_registered = 0;
        }
 #endif
@@ -1267,9 +1217,9 @@ static int __init ipmi_wdog_init(void)
 
        rv = ipmi_smi_watcher_register(&smi_watcher);
        if (rv) {
-#ifdef HAVE_DIE_NMI_POST
-               if (nmi_handler_registered)
-                       unregister_die_notifier(&ipmi_nmi_handler);
+#ifdef HAVE_NMI_HANDLER
+               if (preaction_val == WDOG_PRETIMEOUT_NMI)
+                       release_nmi(&ipmi_nmi_handler);
 #endif
                atomic_notifier_chain_unregister(&panic_notifier_list,
                                                 &wdog_panic_notifier);
@@ -1288,9 +1238,9 @@ static void __exit ipmi_wdog_exit(void)
        ipmi_smi_watcher_unregister(&smi_watcher);
        ipmi_unregister_watchdog(watchdog_ifnum);
 
-#ifdef HAVE_DIE_NMI_POST
+#ifdef HAVE_NMI_HANDLER
        if (nmi_handler_registered)
-               unregister_die_notifier(&ipmi_nmi_handler);
+               release_nmi(&ipmi_nmi_handler);
 #endif
 
        atomic_notifier_chain_unregister(&panic_notifier_list,