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