2010-01-16 21:53:55 +01:00
|
|
|
/*
|
|
|
|
* This is arch independent NMI watchdog implementaion part. It is used to
|
|
|
|
* detect kernel lockups and help debugging. each architecture must add its own
|
|
|
|
* low level code that triggers periodic checks
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "watchdog.h"
|
2010-09-15 16:09:52 +02:00
|
|
|
#include "arch/i386/glo.h"
|
2010-09-23 12:49:45 +02:00
|
|
|
#include "profile.h"
|
2010-01-16 21:53:55 +01:00
|
|
|
|
2010-04-22 09:49:40 +02:00
|
|
|
unsigned watchdog_local_timer_ticks = 0U;
|
2010-01-16 21:53:55 +01:00
|
|
|
struct arch_watchdog *watchdog;
|
|
|
|
int watchdog_enabled;
|
|
|
|
|
2010-09-23 12:49:45 +02:00
|
|
|
PRIVATE void lockup_check(struct nmi_frame * frame)
|
2010-01-16 21:53:55 +01:00
|
|
|
{
|
|
|
|
/* FIXME this should be CPU local */
|
|
|
|
static unsigned no_ticks;
|
|
|
|
static unsigned last_tick_count = (unsigned) -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* when debugging on serial console, printing takes a lot of time some
|
|
|
|
* times while the kernel is certainly not locked up. We don't want to
|
|
|
|
* report a lockup in such situation
|
|
|
|
*/
|
|
|
|
if (serial_debug_active)
|
2010-09-23 12:49:45 +02:00
|
|
|
return;
|
2010-01-16 21:53:55 +01:00
|
|
|
|
|
|
|
if (last_tick_count != watchdog_local_timer_ticks) {
|
|
|
|
if (no_ticks == 1) {
|
2010-03-03 16:45:01 +01:00
|
|
|
printf("watchdog : kernel unlocked\n");
|
2010-01-16 21:53:55 +01:00
|
|
|
no_ticks = 0;
|
|
|
|
}
|
|
|
|
/* we are still ticking, everything seems good */
|
|
|
|
last_tick_count = watchdog_local_timer_ticks;
|
2010-09-23 12:49:45 +02:00
|
|
|
return;
|
2010-01-16 21:53:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if watchdog_local_timer_ticks didn't changed since last time, give it
|
|
|
|
* some more time and only if it still dead, trigger the watchdog alarm
|
|
|
|
*/
|
|
|
|
if (++no_ticks < 10) {
|
|
|
|
if (no_ticks == 1)
|
2010-03-03 16:45:01 +01:00
|
|
|
printf("WARNING watchdog : possible kernel lockup\n");
|
2010-09-23 12:49:45 +02:00
|
|
|
return;
|
2010-01-16 21:53:55 +01:00
|
|
|
}
|
|
|
|
|
2010-09-23 12:49:45 +02:00
|
|
|
/* if we get this far, the kernel is locked up */
|
2010-01-16 21:53:55 +01:00
|
|
|
arch_watchdog_lockup(frame);
|
2010-09-23 12:49:45 +02:00
|
|
|
}
|
2010-01-16 21:53:55 +01:00
|
|
|
|
2010-09-23 12:49:45 +02:00
|
|
|
PUBLIC void nmi_watchdog_handler(struct nmi_frame * frame)
|
|
|
|
{
|
2011-05-25 09:42:11 +02:00
|
|
|
#if SPROFILE
|
2010-09-23 12:49:45 +02:00
|
|
|
/*
|
|
|
|
* Do not check for lockups while profiling, it is extremely likely that
|
|
|
|
* a false positive is detected if the frequency is high
|
|
|
|
*/
|
|
|
|
if (watchdog_enabled && !sprofiling)
|
|
|
|
lockup_check(frame);
|
|
|
|
if (sprofiling)
|
|
|
|
nmi_sprofile_handler(frame);
|
|
|
|
|
|
|
|
if ((watchdog_enabled || sprofiling) && watchdog->reinit)
|
2010-01-16 21:53:55 +01:00
|
|
|
watchdog->reinit(cpuid);
|
2011-05-25 09:42:11 +02:00
|
|
|
#else
|
|
|
|
if (watchdog_enabled) {
|
|
|
|
lockup_check(frame);
|
|
|
|
if (watchdog->reinit)
|
|
|
|
watchdog->reinit(cpuid);
|
|
|
|
}
|
|
|
|
#endif
|
2010-01-16 21:53:55 +01:00
|
|
|
}
|
2010-09-23 12:49:45 +02:00
|
|
|
|
|
|
|
int nmi_watchdog_start_profiling(const unsigned freq)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
/* if watchdog hasn't been enabled, we must enable it now */
|
|
|
|
if (!watchdog_enabled) {
|
|
|
|
if (arch_watchdog_init())
|
|
|
|
return ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!watchdog->profile_init) {
|
|
|
|
printf("WARNING NMI watchdog profiling not supported\n");
|
|
|
|
nmi_watchdog_stop_profiling();
|
|
|
|
return ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = watchdog->profile_init(freq);
|
|
|
|
if (err != OK)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
watchdog->resetval = watchdog->profile_resetval;
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nmi_watchdog_stop_profiling(void)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* if we do not rearm the NMI source, we are done, if we want to keep
|
|
|
|
* the watchdog running, we reset is to its normal value
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (watchdog)
|
|
|
|
watchdog->resetval = watchdog->watchdog_resetval;
|
|
|
|
|
|
|
|
if (!watchdog_enabled)
|
|
|
|
arch_watchdog_stop();
|
|
|
|
}
|