]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/char/ipmi/ipmi_watchdog.c
Merge branches 'release' and 'ppc-workaround' into release
[linux-2.6-omap-h63xx.git] / drivers / char / ipmi / ipmi_watchdog.c
index 147c12047cf3d83b6b345c4b8348f43e4ffc6a5d..8f45ca9235ad10fb41ece3d803d776843bb3ea53 100644 (file)
@@ -61,7 +61,8 @@
    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
+#include <asm/kdebug.h>
+#define HAVE_DIE_NMI
 #endif
 
 #define        PFX "IPMI Watchdog: "
@@ -174,8 +175,6 @@ static char expect_close;
 
 static int ifnum_to_use = -1;
 
-static DECLARE_RWSEM(register_sem);
-
 /* Parameters to ipmi_set_timeout */
 #define IPMI_SET_TIMEOUT_NO_HB                 0
 #define IPMI_SET_TIMEOUT_HB_IF_NECESSARY       1
@@ -201,11 +200,9 @@ static int set_param_int(const char *val, struct kernel_param *kp)
        if (endp == val)
                return -EINVAL;
 
-       down_read(&register_sem);
        *((int *)kp->arg) = l;
        if (watchdog_user)
                rv = ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY);
-       up_read(&register_sem);
 
        return rv;
 }
@@ -234,17 +231,15 @@ static int set_param_str(const char *val, struct kernel_param *kp)
 
        s = strstrip(valcp);
 
-       down_read(&register_sem);
        rv = fn(s, NULL);
        if (rv)
-               goto out_unlock;
+               goto out;
 
        check_parms();
        if (watchdog_user)
                rv = ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY);
 
- out_unlock:
-       up_read(&register_sem);
+ out:
        return rv;
 }
 
@@ -327,14 +322,12 @@ 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
+#ifdef HAVE_DIE_NMI
 static int testing_nmi;
 static int nmi_handler_registered;
 #endif
 
 static int ipmi_heartbeat(void);
-static void panic_halt_ipmi_heartbeat(void);
-
 
 /* We use a mutex to make sure that only one thing can send a set
    timeout at one time, because we only have one copy of the data.
@@ -374,7 +367,6 @@ static int i_ipmi_set_timeout(struct ipmi_smi_msg  *smi_msg,
 
 
        /* These can be cleared as we are setting the timeout. */
-       ipmi_start_timer_on_heartbeat = 0;
        pretimeout_since_last_heartbeat = 0;
 
        data[0] = 0;
@@ -462,19 +454,64 @@ out:
        return rv;
 }
 
-static void dummy_smi_free(struct ipmi_smi_msg *msg)
+static atomic_t panic_done_count = ATOMIC_INIT(0);
+
+static void panic_smi_free(struct ipmi_smi_msg *msg)
 {
+       atomic_dec(&panic_done_count);
 }
-static void dummy_recv_free(struct ipmi_recv_msg *msg)
+static void panic_recv_free(struct ipmi_recv_msg *msg)
 {
+       atomic_dec(&panic_done_count);
 }
+
+static struct ipmi_smi_msg panic_halt_heartbeat_smi_msg =
+{
+       .done = panic_smi_free
+};
+static struct ipmi_recv_msg panic_halt_heartbeat_recv_msg =
+{
+       .done = panic_recv_free
+};
+
+static void panic_halt_ipmi_heartbeat(void)
+{
+       struct kernel_ipmi_msg             msg;
+       struct ipmi_system_interface_addr addr;
+       int rv;
+
+       /* Don't reset the timer if we have the timer turned off, that
+           re-enables the watchdog. */
+       if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE)
+               return;
+
+       addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+       addr.channel = IPMI_BMC_CHANNEL;
+       addr.lun = 0;
+
+       msg.netfn = 0x06;
+       msg.cmd = IPMI_WDOG_RESET_TIMER;
+       msg.data = NULL;
+       msg.data_len = 0;
+       rv = ipmi_request_supply_msgs(watchdog_user,
+                                     (struct ipmi_addr *) &addr,
+                                     0,
+                                     &msg,
+                                     NULL,
+                                     &panic_halt_heartbeat_smi_msg,
+                                     &panic_halt_heartbeat_recv_msg,
+                                     1);
+       if (!rv)
+               atomic_add(2, &panic_done_count);
+}
+
 static struct ipmi_smi_msg panic_halt_smi_msg =
 {
-       .done = dummy_smi_free
+       .done = panic_smi_free
 };
 static struct ipmi_recv_msg panic_halt_recv_msg =
 {
-       .done = dummy_recv_free
+       .done = panic_recv_free
 };
 
 /* Special call, doesn't claim any locks.  This is only to be called
@@ -486,13 +523,21 @@ static void panic_halt_ipmi_set_timeout(void)
        int send_heartbeat_now;
        int rv;
 
+       /* Wait for the messages to be free. */
+       while (atomic_read(&panic_done_count) != 0)
+               ipmi_poll_interface(watchdog_user);
        rv = i_ipmi_set_timeout(&panic_halt_smi_msg,
                                &panic_halt_recv_msg,
                                &send_heartbeat_now);
        if (!rv) {
+               atomic_add(2, &panic_done_count);
                if (send_heartbeat_now)
                        panic_halt_ipmi_heartbeat();
-       }
+       } else
+               printk(KERN_WARNING PFX
+                      "Unable to extend the watchdog timeout.");
+       while (atomic_read(&panic_done_count) != 0)
+               ipmi_poll_interface(watchdog_user);
 }
 
 /* We use a semaphore to make sure that only one thing can send a
@@ -521,15 +566,6 @@ static struct ipmi_recv_msg heartbeat_recv_msg =
        .done = heartbeat_free_recv
 };
  
-static struct ipmi_smi_msg panic_halt_heartbeat_smi_msg =
-{
-       .done = dummy_smi_free
-};
-static struct ipmi_recv_msg panic_halt_heartbeat_recv_msg =
-{
-       .done = dummy_recv_free
-};
 static int ipmi_heartbeat(void)
 {
        struct kernel_ipmi_msg            msg;
@@ -540,6 +576,7 @@ static int ipmi_heartbeat(void)
                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) {
@@ -599,35 +636,6 @@ static int ipmi_heartbeat(void)
        return rv;
 }
 
-static void panic_halt_ipmi_heartbeat(void)
-{
-       struct kernel_ipmi_msg             msg;
-       struct ipmi_system_interface_addr addr;
-
-
-       /* Don't reset the timer if we have the timer turned off, that
-           re-enables the watchdog. */
-       if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE)
-               return;
-
-       addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
-       addr.channel = IPMI_BMC_CHANNEL;
-       addr.lun = 0;
-
-       msg.netfn = 0x06;
-       msg.cmd = IPMI_WDOG_RESET_TIMER;
-       msg.data = NULL;
-       msg.data_len = 0;
-       ipmi_request_supply_msgs(watchdog_user,
-                                (struct ipmi_addr *) &addr,
-                                0,
-                                &msg,
-                                NULL,
-                                &panic_halt_heartbeat_smi_msg,
-                                &panic_halt_heartbeat_recv_msg,
-                                1);
-}
-
 static struct watchdog_info ident =
 {
        .options        = 0,    /* WDIOF_SETTIMEOUT, */
@@ -661,6 +669,7 @@ static int ipmi_ioctl(struct inode *inode, struct file *file,
                return 0;
 
        case WDIOC_SET_PRETIMEOUT:
+       case WDIOC_SETPRETIMEOUT:
                i = copy_from_user(&val, argp, sizeof(int));
                if (i)
                        return -EFAULT;
@@ -668,6 +677,7 @@ static int ipmi_ioctl(struct inode *inode, struct file *file,
                return ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY);
 
        case WDIOC_GET_PRETIMEOUT:
+       case WDIOC_GETPRETIMEOUT:
                i = copy_to_user(argp, &pretimeout, sizeof(pretimeout));
                if (i)
                        return -EFAULT;
@@ -908,7 +918,6 @@ static void ipmi_register_watchdog(int ipmi_intf)
 {
        int rv = -EBUSY;
 
-       down_write(&register_sem);
        if (watchdog_user)
                goto out;
 
@@ -934,7 +943,7 @@ static void ipmi_register_watchdog(int ipmi_intf)
                printk(KERN_CRIT PFX "Unable to register misc device\n");
        }
 
-#ifdef HAVE_DIE_NMI_POST
+#ifdef HAVE_DIE_NMI
        if (nmi_handler_registered) {
                int old_pretimeout = pretimeout;
                int old_timeout = timeout;
@@ -974,8 +983,6 @@ static void ipmi_register_watchdog(int ipmi_intf)
 #endif
 
  out:
-       up_write(&register_sem);
-
        if ((start_now) && (rv == 0)) {
                /* Run from startup, so start the timer now. */
                start_now = 0; /* Disable this function after first startup. */
@@ -993,8 +1000,6 @@ static void ipmi_unregister_watchdog(int ipmi_intf)
 {
        int rv;
 
-       down_write(&register_sem);
-
        if (!watchdog_user)
                goto out;
 
@@ -1019,16 +1024,29 @@ static void ipmi_unregister_watchdog(int ipmi_intf)
        watchdog_user = NULL;
 
  out:
-       up_write(&register_sem);
+       return;
 }
 
-#ifdef HAVE_DIE_NMI_POST
+#ifdef HAVE_DIE_NMI
 static int
 ipmi_nmi(struct notifier_block *self, unsigned long val, void *data)
 {
-       if (val != DIE_NMI_POST)
+       struct die_args *args = data;
+
+       if (val != DIE_NMI)
+               return NOTIFY_OK;
+
+       /* Hack, if it's a memory or I/O error, ignore it. */
+       if (args->err & 0xc0)
                return NOTIFY_OK;
 
+       /*
+        * If we get here, it's an NMI that's not a memory or I/O
+        * error.  We can't truly tell if it's from IPMI or not
+        * without sending a message, and sending a message is almost
+        * impossible because of locking.
+        */
+
        if (testing_nmi) {
                testing_nmi = 2;
                return NOTIFY_STOP;
@@ -1070,7 +1088,7 @@ static int wdog_reboot_handler(struct notifier_block *this,
                /* Make sure we only do this once. */
                reboot_event_handled = 1;
 
-               if (code == SYS_DOWN || code == SYS_HALT) {
+               if (code == SYS_POWER_OFF || code == SYS_HALT) {
                        /* Disable the WDT if we are shutting down. */
                        ipmi_watchdog_state = WDOG_TIMEOUT_NONE;
                        panic_halt_ipmi_set_timeout();
@@ -1174,7 +1192,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_DIE_NMI
        else if (strcmp(inval, "pre_nmi") == 0)
                preaction_val = WDOG_PRETIMEOUT_NMI;
 #endif
@@ -1208,7 +1226,7 @@ static int preop_op(const char *inval, char *outval)
 
 static void check_parms(void)
 {
-#ifdef HAVE_DIE_NMI_POST
+#ifdef HAVE_DIE_NMI
        int do_nmi = 0;
        int rv;
 
@@ -1267,7 +1285,7 @@ static int __init ipmi_wdog_init(void)
 
        rv = ipmi_smi_watcher_register(&smi_watcher);
        if (rv) {
-#ifdef HAVE_DIE_NMI_POST
+#ifdef HAVE_DIE_NMI
                if (nmi_handler_registered)
                        unregister_die_notifier(&ipmi_nmi_handler);
 #endif
@@ -1288,7 +1306,7 @@ 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_DIE_NMI
        if (nmi_handler_registered)
                unregister_die_notifier(&ipmi_nmi_handler);
 #endif