minix3/kernel/watchdog.c

114 lines
2.6 KiB
C

/*
* This is arch independent NMI watchdog implementation 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"
#include "arch/i386/glo.h"
#include "profile.h"
unsigned watchdog_local_timer_ticks = 0U;
struct arch_watchdog *watchdog;
int watchdog_enabled;
static void lockup_check(struct nmi_frame * frame)
{
/* 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)
return;
if (last_tick_count != watchdog_local_timer_ticks) {
if (no_ticks == 1) {
printf("watchdog : kernel unlocked\n");
no_ticks = 0;
}
/* we are still ticking, everything seems good */
last_tick_count = watchdog_local_timer_ticks;
return;
}
/*
* 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)
printf("WARNING watchdog : possible kernel lockup\n");
return;
}
/* if we get this far, the kernel is locked up */
arch_watchdog_lockup(frame);
}
void nmi_watchdog_handler(struct nmi_frame * frame)
{
#if SPROFILE
/*
* 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)
watchdog->reinit(cpuid);
#else
if (watchdog_enabled) {
lockup_check(frame);
if (watchdog->reinit)
watchdog->reinit(cpuid);
}
#endif
}
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();
}