diff --git a/testing/ostest/wdog.c b/testing/ostest/wdog.c index 1d5cf4ccd..49478ae49 100644 --- a/testing/ostest/wdog.c +++ b/testing/ostest/wdog.c @@ -26,9 +26,10 @@ #include #include +#include #include #include -#include +#include #include /**************************************************************************** @@ -36,12 +37,12 @@ ****************************************************************************/ #define WDOGTEST_RAND_ITER 1024 -#define WDOGTEST_THREAD_NR 8 -#define WDOGTEST_TOLERENT_LATENCY_US 5000 +#define WDOGTEST_THREAD_NR (CONFIG_SMP_NCPUS * 4) +#define WDOGTEST_TOLERENT_TICK 10 #define wdtest_assert(x) _ASSERT(x, __FILE__, __LINE__) -#define wdtest_printf(...) printf(__VA_ARGS__) +#define wdtest_printf(...) syslog(LOG_WARNING, __VA_ARGS__) #define wdtest_delay(delay_ns) usleep(delay_ns / 1000 + 1) @@ -64,63 +65,131 @@ typedef struct wdtest_param_s #ifdef CONFIG_BUILD_FLAT static void wdtest_callback(wdparm_t param) { - struct timespec tp; FAR wdtest_param_t *wdtest_param = (FAR wdtest_param_t *)param; - clock_gettime(CLOCK_MONOTONIC, &tp); + /* Increment the callback count */ wdtest_param->callback_cnt += 1; - wdtest_param->triggered_tick = clock_time2ticks(&tp); + + /* Record the system tick at which the callback was triggered */ + + wdtest_param->triggered_tick = clock_systime_ticks(); +} + +static void wdtest_checkdelay(sclock_t diff, sclock_t delay_tick) +{ + /* Ensure the watchdog trigger time is not earlier than expected. */ + + wdtest_assert(diff - delay_tick >= 0); + + /* If the timer latency exceeds the tolerance, print a warning. */ + + if (diff - delay_tick > WDOGTEST_TOLERENT_TICK) + { + wdtest_printf("WARNING: wdog latency ticks %lld " + "(> %u may indicate timing error)\n", + (long long)diff - delay_tick, + WDOGTEST_TOLERENT_TICK); + } } static void wdtest_once(FAR struct wdog_s *wdog, FAR wdtest_param_t *param, sclock_t delay_ns) { - uint64_t cnt; - long long diff; - clock_t wdset_tick; - struct timespec tp; - clock_t delay_ticks = (clock_t)NSEC2TICK((clock_t)delay_ns); + uint64_t cnt; + sclock_t diff; + clock_t wdset_tick; + irqstate_t flags; + sclock_t delay_tick = (sclock_t)NSEC2TICK((clock_t)delay_ns); wdtest_printf("wdtest_once %lld ns\n", (long long)delay_ns); - clock_gettime(CLOCK_MONOTONIC, &tp); + /* Save the current callback count. */ - wdset_tick = clock_time2ticks(&tp); cnt = param->callback_cnt; - wdtest_assert(wd_start(wdog, delay_ticks, wdtest_callback, + /* Enter a critical section to prevent interruptions. */ + + flags = enter_critical_section(); + + /* Record the current system tick before setting the watchdog. */ + + wdset_tick = clock_systime_ticks(); + + wdtest_assert(wd_start(wdog, delay_tick, wdtest_callback, (wdparm_t)param) == OK); - wdtest_delay(delay_ns); + leave_critical_section(flags); - diff = (long long)(param->triggered_tick - wdset_tick); + /* Wait until the callback is triggered exactly once. */ - wdtest_assert(cnt + 1 == param->callback_cnt); + while (cnt + 1 != param->callback_cnt) + { + wdtest_delay(delay_ns); + } - /* Ensure diff - delay_ticks >= 0. */ + /* Check if the delay is within the acceptable tolerance. */ - wdtest_assert(diff - (long long)delay_ticks >= 0); - wdtest_printf("wdtest_once latency ticks %lld\n", diff - delay_ticks); + diff = (sclock_t)(param->triggered_tick - wdset_tick); + + wdtest_checkdelay(diff, delay_tick); } static void wdtest_rand(FAR struct wdog_s *wdog, FAR wdtest_param_t *param, sclock_t rand_ns) { - int idx; - sclock_t delay_ns; + uint64_t cnt; + int idx; + sclock_t delay_ns; + clock_t wdset_tick; + sclock_t delay_tick; + sclock_t diff; + irqstate_t flags; + + /* Perform multiple iterations with random delays. */ for (idx = 0; idx < WDOGTEST_RAND_ITER; idx++) { - delay_ns = rand() % rand_ns; - wdtest_assert(wd_start(wdog, NSEC2TICK(delay_ns), wdtest_callback, - (wdparm_t)param) == 0); + cnt = param->callback_cnt; - /* Wait or Cancel 50/50 */ + /* Generate a random delay within the specified range. */ + + delay_ns = rand() % rand_ns; + delay_tick = NSEC2TICK(delay_ns); + + /* Enter critical section if the callback count is odd. */ + + if (cnt % 2) + { + flags = enter_critical_section(); + } + + wdset_tick = clock_systime_ticks(); + wdtest_assert(wd_start(wdog, delay_tick, wdtest_callback, + (wdparm_t)param) == 0); + if (cnt % 2) + { + leave_critical_section(flags); + } + + /* Decide to wait for the callback or cancel the watchdog. */ if (delay_ns % 2) { - wdtest_delay(delay_ns); + /* Wait for the callback. */ + + while (cnt + 1 != param->callback_cnt) + { + wdtest_delay(delay_ns); + } + + /* Check the delay if the callback count is odd. */ + + if (cnt % 2) + { + diff = (sclock_t)(param->triggered_tick - wdset_tick); + wdtest_checkdelay(diff, delay_tick); + } } else { @@ -131,14 +200,11 @@ static void wdtest_rand(FAR struct wdog_s *wdog, FAR wdtest_param_t *param, static void wdtest_callback_recursive(wdparm_t param) { - struct timespec tp; FAR wdtest_param_t *wdtest_param = (FAR wdtest_param_t *)param; sclock_t interval = wdtest_param->interval; - clock_gettime(CLOCK_MONOTONIC, &tp); - wdtest_param->callback_cnt += 1; - wdtest_param->triggered_tick = clock_time2ticks(&tp); + wdtest_param->triggered_tick = clock_systime_ticks(); wd_start(wdtest_param->wdog, interval, wdtest_callback_recursive, param); @@ -149,24 +215,29 @@ static void wdtest_recursive(FAR struct wdog_s *wdog, sclock_t delay_ns, unsigned int times) { - uint64_t cnt; - struct timespec tp; - clock_t wdset_tick; + uint64_t cnt; + clock_t wdset_tick; + irqstate_t flags; wdtest_printf("wdtest_recursive %lldus\n", (long long)delay_ns); + cnt = param->callback_cnt; + param->wdog = wdog; param->interval = (sclock_t)NSEC2TICK((clock_t)delay_ns); wdtest_assert(param->interval >= 0); - clock_gettime(CLOCK_MONOTONIC, &tp); - wdset_tick = clock_time2ticks(&tp); + flags = enter_critical_section(); + + wdset_tick = clock_systime_ticks(); wdtest_assert(wd_start(param->wdog, param->interval, wdtest_callback_recursive, (wdparm_t)param) == OK); + leave_critical_section(flags); + wdtest_delay(times * delay_ns); wdtest_assert(wd_cancel(param->wdog) == 0); @@ -208,7 +279,6 @@ static void wdog_test_run(FAR wdtest_param_t *param) wdtest_once(&test_wdog, param, 100); wdtest_once(&test_wdog, param, 1000); wdtest_once(&test_wdog, param, 10000); - wdtest_delay(10); /* Delay > 0, middle 100us */ @@ -242,11 +312,11 @@ static void wdog_test_run(FAR wdtest_param_t *param) wdtest_assert(rest < delay && rest > (delay >> 1)); - wdtest_printf("wd_start with maximum delay, cancel %lld\n", - (long long)rest); - wdtest_assert(wd_cancel(&test_wdog) == 0); + wdtest_printf("wd_start with maximum delay, cancel OK, rest %lld\n", + (long long)rest); + /* Delay wraparound (delay < 0) */ delay = (sclock_t)((clock_t)delay + 1);