kernel/timeout: Fix announcement tick logic
This was wrong in subtle ways. In tickless mode it's possible to get an announcement for multiple ticks at a time and have multiple callbacks to execute that were technically scheduled at different times. We want to fix the current tick at the value represented by the currently-executing callback's EXPIRATION (even if we missed it!), so that any new timeouts it sets (c.f. a k_timer period) happen at the right point, in phase with the expected series. In single-tick mode the code ends up the same always, so the bug wasn't visible. Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
This commit is contained in:
parent
3630641bcc
commit
1cfff07480
|
@ -23,10 +23,8 @@ static struct k_spinlock timeout_lock;
|
||||||
|
|
||||||
static bool can_wait_forever;
|
static bool can_wait_forever;
|
||||||
|
|
||||||
/* During a call to z_clock_announce(), the "current" time is "ahead"
|
/* Cycles left to process in the currently-executing z_clock_announce() */
|
||||||
* of the reference used by timeout_list by this amount.
|
static int announce_remaining;
|
||||||
*/
|
|
||||||
static int announce_advance;
|
|
||||||
|
|
||||||
#if defined(CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME)
|
#if defined(CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME)
|
||||||
int z_clock_hw_cycles_per_sec = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC;
|
int z_clock_hw_cycles_per_sec = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC;
|
||||||
|
@ -58,8 +56,8 @@ static void remove(struct _timeout *t)
|
||||||
|
|
||||||
static s32_t adjust_elapsed(s32_t ticks)
|
static s32_t adjust_elapsed(s32_t ticks)
|
||||||
{
|
{
|
||||||
ticks -= z_clock_elapsed();
|
ticks -= announce_remaining == 0 ? z_clock_elapsed() : 0;
|
||||||
return ticks < 0 ? 0 : ticks;
|
return max(0, ticks);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _add_timeout(struct _timeout *to, _timeout_func_t fn, s32_t ticks)
|
void _add_timeout(struct _timeout *to, _timeout_func_t fn, s32_t ticks)
|
||||||
|
@ -70,7 +68,7 @@ void _add_timeout(struct _timeout *to, _timeout_func_t fn, s32_t ticks)
|
||||||
LOCKED(&timeout_lock) {
|
LOCKED(&timeout_lock) {
|
||||||
struct _timeout *t;
|
struct _timeout *t;
|
||||||
|
|
||||||
to->dticks = adjust_elapsed(ticks) + announce_advance;
|
to->dticks = adjust_elapsed(ticks) + announce_remaining;
|
||||||
for (t = first(); t != NULL; t = next(t)) {
|
for (t = first(); t != NULL; t = next(t)) {
|
||||||
__ASSERT(t->dticks >= 0, "");
|
__ASSERT(t->dticks >= 0, "");
|
||||||
|
|
||||||
|
@ -133,21 +131,18 @@ void z_clock_announce(s32_t ticks)
|
||||||
z_time_slice(ticks);
|
z_time_slice(ticks);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
LOCKED(&timeout_lock) {
|
announce_remaining = ticks;
|
||||||
curr_tick += ticks;
|
|
||||||
announce_advance = ticks;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
LOCKED(&timeout_lock) {
|
LOCKED(&timeout_lock) {
|
||||||
t = first();
|
t = first();
|
||||||
if (t != NULL) {
|
if (t != NULL) {
|
||||||
if (t->dticks <= announce_advance) {
|
if (t->dticks <= announce_remaining) {
|
||||||
announce_advance -= t->dticks;
|
announce_remaining -= t->dticks;
|
||||||
|
curr_tick += t->dticks;
|
||||||
t->dticks = 0;
|
t->dticks = 0;
|
||||||
remove(t);
|
remove(t);
|
||||||
} else {
|
} else {
|
||||||
t->dticks -= announce_advance;
|
t->dticks -= announce_remaining;
|
||||||
t = NULL;
|
t = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,7 +155,11 @@ void z_clock_announce(s32_t ticks)
|
||||||
t->fn(t);
|
t->fn(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
announce_advance = 0;
|
LOCKED(&timeout_lock) {
|
||||||
|
curr_tick += announce_remaining;
|
||||||
|
announce_remaining = 0;
|
||||||
|
}
|
||||||
|
|
||||||
z_clock_set_timeout(_get_next_timeout_expiry(), false);
|
z_clock_set_timeout(_get_next_timeout_expiry(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue