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:
parent
fe6e017649
commit
df1125a58c
|
@ -50,6 +50,40 @@ static int hw_wdt_channel;
|
|||
static bool hw_wdt_started;
|
||||
#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.
|
||||
*
|
||||
|
@ -67,20 +101,20 @@ static bool hw_wdt_started;
|
|||
static void task_wdt_trigger(struct k_timer *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 (channel_id == TASK_WDT_BACKGROUND_CHANNEL) {
|
||||
if (hw_wdt_dev) {
|
||||
wdt_feed(hw_wdt_dev, hw_wdt_channel);
|
||||
}
|
||||
/* If the timeout expired for the background channel (so the hardware
|
||||
* watchdog needs to be fed) or for a channel that has been deleted,
|
||||
* only schedule a new timeout (the hardware watchdog, if used, will be
|
||||
* 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;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (channels[channel_id].reload_period == 0) {
|
||||
/* channel was deleted */
|
||||
return;
|
||||
} else if (channels[channel_id].callback) {
|
||||
if (channels[channel_id].callback) {
|
||||
channels[channel_id].callback(channel_id,
|
||||
channels[channel_id].user_data);
|
||||
} else {
|
||||
|
@ -157,8 +191,6 @@ int task_wdt_delete(int channel_id)
|
|||
int task_wdt_feed(int channel_id)
|
||||
{
|
||||
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)) {
|
||||
return -EINVAL;
|
||||
|
@ -178,34 +210,7 @@ int task_wdt_feed(int channel_id)
|
|||
channels[channel_id].timeout_abs_ticks = current_ticks +
|
||||
k_ms_to_ticks_ceil64(channels[channel_id].reload_period);
|
||||
|
||||
#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_TIMEOUT_ABS_TICKS(next_timeout));
|
||||
|
||||
#ifdef CONFIG_TASK_WDT_HW_FALLBACK
|
||||
if (hw_wdt_dev) {
|
||||
wdt_feed(hw_wdt_dev, hw_wdt_channel);
|
||||
}
|
||||
#endif
|
||||
schedule_next_timeout(current_ticks);
|
||||
|
||||
k_sched_unlock();
|
||||
|
||||
|
|
Loading…
Reference in a new issue