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;
|
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();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue