task_wdt: Fix the way the kernel timer is used

Do not use periodic executions of the timer handler, as in certain
circumstances (the fallback hardware watchdog used, one or more
task_wdt channel activated but none of them being ever fed) this
would lead to no callback/reset being executed for any channel.
Instead, schedule the next timeout from the timer handler function
when the function is executed for the dummy background channel or
for a channel that was deleted.

Signed-off-by: Andrzej Głąbek <andrzej.glabek@nordicsemi.no>
This commit is contained in:
Andrzej Głąbek 2021-07-02 08:52:12 +02:00 committed by Maureen Helm
parent fe6e017649
commit df1125a58c

View file

@ -50,6 +50,40 @@ static int hw_wdt_channel;
static bool hw_wdt_started; static bool hw_wdt_started;
#endif #endif
static void schedule_next_timeout(uint32_t current_ticks)
{
int next_channel_id; /* channel which will time out next */
int64_t next_timeout; /* timeout in absolute ticks of this channel */
#ifdef CONFIG_TASK_WDT_HW_FALLBACK
next_channel_id = TASK_WDT_BACKGROUND_CHANNEL;
next_timeout = current_ticks +
k_ms_to_ticks_ceil64(CONFIG_TASK_WDT_MIN_TIMEOUT);
#else
next_channel_id = 0;
next_timeout = INT64_MAX;
#endif
/* find minimum timeout of all channels */
for (int id = 0; id < ARRAY_SIZE(channels); id++) {
if (channels[id].reload_period != 0 &&
channels[id].timeout_abs_ticks < next_timeout) {
next_channel_id = id;
next_timeout = channels[id].timeout_abs_ticks;
}
}
/* update task wdt kernel timer */
k_timer_user_data_set(&timer, (void *)next_channel_id);
k_timer_start(&timer, K_TIMEOUT_ABS_TICKS(next_timeout), K_FOREVER);
#ifdef CONFIG_TASK_WDT_HW_FALLBACK
if (hw_wdt_dev) {
wdt_feed(hw_wdt_dev, hw_wdt_channel);
}
#endif
}
/** /**
* @brief Task watchdog timer callback. * @brief Task watchdog timer callback.
* *
@ -67,20 +101,20 @@ static bool hw_wdt_started;
static void task_wdt_trigger(struct k_timer *timer_id) static void task_wdt_trigger(struct k_timer *timer_id)
{ {
int channel_id = (int)k_timer_user_data_get(timer_id); int channel_id = (int)k_timer_user_data_get(timer_id);
bool bg_channel = IS_ENABLED(CONFIG_TASK_WDT_HW_FALLBACK) &&
(channel_id == TASK_WDT_BACKGROUND_CHANNEL);
#ifdef CONFIG_TASK_WDT_HW_FALLBACK /* If the timeout expired for the background channel (so the hardware
if (channel_id == TASK_WDT_BACKGROUND_CHANNEL) { * watchdog needs to be fed) or for a channel that has been deleted,
if (hw_wdt_dev) { * only schedule a new timeout (the hardware watchdog, if used, will be
wdt_feed(hw_wdt_dev, hw_wdt_channel); * fed right after that new timeout is scheduled).
} */
if (bg_channel || channels[channel_id].reload_period == 0) {
schedule_next_timeout(sys_clock_tick_get());
return; return;
} }
#endif
if (channels[channel_id].reload_period == 0) { if (channels[channel_id].callback) {
/* channel was deleted */
return;
} else if (channels[channel_id].callback) {
channels[channel_id].callback(channel_id, channels[channel_id].callback(channel_id,
channels[channel_id].user_data); channels[channel_id].user_data);
} else { } else {
@ -157,8 +191,6 @@ int task_wdt_delete(int channel_id)
int task_wdt_feed(int channel_id) int task_wdt_feed(int channel_id)
{ {
int64_t current_ticks; int64_t current_ticks;
int next_channel_id; /* channel which will time out next */
int64_t next_timeout; /* timeout in absolute ticks of this channel */
if (channel_id < 0 || channel_id >= ARRAY_SIZE(channels)) { if (channel_id < 0 || channel_id >= ARRAY_SIZE(channels)) {
return -EINVAL; return -EINVAL;
@ -178,34 +210,7 @@ int task_wdt_feed(int channel_id)
channels[channel_id].timeout_abs_ticks = current_ticks + channels[channel_id].timeout_abs_ticks = current_ticks +
k_ms_to_ticks_ceil64(channels[channel_id].reload_period); k_ms_to_ticks_ceil64(channels[channel_id].reload_period);
#ifdef CONFIG_TASK_WDT_HW_FALLBACK schedule_next_timeout(current_ticks);
next_channel_id = TASK_WDT_BACKGROUND_CHANNEL;
next_timeout = current_ticks +
k_ms_to_ticks_ceil64(CONFIG_TASK_WDT_MIN_TIMEOUT);
#else
next_channel_id = 0;
next_timeout = INT64_MAX;
#endif
/* find minimum timeout of all channels */
for (int id = 0; id < ARRAY_SIZE(channels); id++) {
if (channels[id].reload_period != 0 &&
channels[id].timeout_abs_ticks < next_timeout) {
next_channel_id = id;
next_timeout = channels[id].timeout_abs_ticks;
}
}
/* update task wdt kernel timer */
k_timer_user_data_set(&timer, (void *)next_channel_id);
k_timer_start(&timer, K_TIMEOUT_ABS_TICKS(next_timeout),
K_TIMEOUT_ABS_TICKS(next_timeout));
#ifdef CONFIG_TASK_WDT_HW_FALLBACK
if (hw_wdt_dev) {
wdt_feed(hw_wdt_dev, hw_wdt_channel);
}
#endif
k_sched_unlock(); k_sched_unlock();