114 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			114 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * 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"
 | 
						|
#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();
 | 
						|
}
 |