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:
Andy Ross 2018-10-03 08:50:52 -07:00 committed by Anas Nashif
parent 3630641bcc
commit 1cfff07480

View file

@ -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);
} }