|
|
|
@ -50,18 +50,10 @@
|
|
|
|
|
#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(®ister_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(®ister_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,
|
|
|
|
|