task_wdt: fix race condition for task_wdt_add function
The task_wdt_add function changes the reload_period of the channel to a non-null value, which indicates that the channel is used. If the function is interrupted by a task_wdt_trigger running in ISR context before adding of the new channel has finished, the next timeout will be scheduled based on inconsistent channel data. Using a spinlock avoids such data races. Fixes #61004 Signed-off-by: Martin Jäger <martin@libre.solar>
This commit is contained in:
parent
474aa963ff
commit
33bd2fed08
@ -39,6 +39,7 @@ struct task_wdt_channel {
|
||||
|
||||
/* array of all task watchdog channels */
|
||||
static struct task_wdt_channel channels[CONFIG_TASK_WDT_CHANNELS];
|
||||
static struct k_spinlock channels_lock;
|
||||
|
||||
/* timer used for watchdog handling */
|
||||
static struct k_timer timer;
|
||||
@ -153,10 +154,18 @@ int task_wdt_init(const struct device *hw_wdt)
|
||||
int task_wdt_add(uint32_t reload_period, task_wdt_callback_t callback,
|
||||
void *user_data)
|
||||
{
|
||||
k_spinlock_key_t key;
|
||||
|
||||
if (reload_period == 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* k_spin_lock instead of k_sched_lock required here to avoid being interrupted by a
|
||||
* triggering other task watchdog channel (executed in ISR context).
|
||||
*/
|
||||
key = k_spin_lock(&channels_lock);
|
||||
|
||||
/* look for unused channel (reload_period set to 0) */
|
||||
for (int id = 0; id < ARRAY_SIZE(channels); id++) {
|
||||
if (channels[id].reload_period == 0) {
|
||||
@ -176,21 +185,31 @@ int task_wdt_add(uint32_t reload_period, task_wdt_callback_t callback,
|
||||
/* must be called after hw wdt has been started */
|
||||
task_wdt_feed(id);
|
||||
|
||||
k_spin_unlock(&channels_lock, key);
|
||||
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
k_spin_unlock(&channels_lock, key);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int task_wdt_delete(int channel_id)
|
||||
{
|
||||
k_spinlock_key_t key;
|
||||
|
||||
if (channel_id < 0 || channel_id >= ARRAY_SIZE(channels)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
key = k_spin_lock(&channels_lock);
|
||||
|
||||
channels[channel_id].reload_period = 0;
|
||||
|
||||
k_spin_unlock(&channels_lock, key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user