Have the core timer code always call into the SBE timers with the
soonest time, so the SBE code can be more careful with maintaining the
hardware timer.
This fixes a bug where the SBE timer is not being set immediately on
schedule_timer. With a subsequent change to SBE code, it allows an SBE
timer that fires too early to cause a re-schedule of the SBE timer.
Signed-off-by: Nicholas Piggin <npiggin at gmail.com>
---
core/timer.c | 27 +++++++++++++++++++--------
include/cpu.h | 1 +
2 files changed, 20 insertions(+), 8 deletions(-)
diff --git a/core/timer.c b/core/timer.c
index cd7112199..f512972eb 100644
--- a/core/timer.c
+++ b/core/timer.c
@@ -119,15 +119,18 @@ static void __schedule_timer_at(struct timer *t, uint64_t when)
if (when >= lt->target)
continue;
list_add_before(&timer_list, <->link, &t->link);
- goto bail;
+ goto added;
}
list_add_tail(&timer_list, &t->link);
- }
- bail:
- /* Pick up the next timer and upddate the SBE HW timer */
- lt = list_top(&timer_list, struct timer, link);
- if (lt) {
- update_timer_expiry(lt->target);
+ added:
+ /* Timer running code will update expiry at the end */
+ if (!this_cpu()->running_timer) {
+ /* Pick the next timer and upddate the SBE HW timer */
+ lt = list_top(&timer_list, struct timer, link);
+ if (lt && (lt == t || when < lt->target)) {
+ update_timer_expiry(lt->target);
+ }
+ }
}
}
@@ -190,6 +193,7 @@ static void __check_poll_timers(uint64_t now)
/* Allright, first remove it and mark it running */
__remove_timer(t);
t->running = this_cpu();
+ this_cpu()->running_timer = true;
/* Now we can unlock and call it's expiry */
unlock(&timer_lock);
@@ -197,6 +201,7 @@ static void __check_poll_timers(uint64_t now)
/* Re-lock and mark not running */
lock(&timer_lock);
+ this_cpu()->running_timer = false;
t->running = NULL;
}
timer_in_poll = false;
@@ -210,8 +215,12 @@ static void __check_timers(uint64_t now)
t = list_top(&timer_list, struct timer, link);
/* Top of list not expired ? that's it ... */
- if (!t || t->target > now)
+ if (!t)
break;
+ if (t->target > now) {
+ update_timer_expiry(t->target);
+ break;
+ }
/* Top of list still running, we have to delay handling it,
* let's reprogram the SLW/SBE with a small delay. We chose
@@ -225,6 +234,7 @@ static void __check_timers(uint64_t now)
/* Allright, first remove it and mark it running */
__remove_timer(t);
t->running = this_cpu();
+ this_cpu()->running_timer = true;
/* Now we can unlock and call it's expiry */
unlock(&timer_lock);
@@ -232,6 +242,7 @@ static void __check_timers(uint64_t now)
/* Re-lock and mark not running */
lock(&timer_lock);
+ this_cpu()->running_timer = false;
t->running = NULL;
/* Update time stamp */
diff --git a/include/cpu.h b/include/cpu.h
index d0fc6ccd5..2558d31a6 100644
--- a/include/cpu.h
+++ b/include/cpu.h
@@ -56,6 +56,7 @@ struct cpu_thread {
uint32_t con_suspend;
struct list_head locks_held;
bool con_need_flush;
+ bool running_timer;
bool in_mcount;
bool in_poller;
bool in_reinit;
--
2.45.2