|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Minios-devel] [UNIKRAFT PATCHv2 7/7] plat/common: Implement generic_timer_cpu_block
From: Wei Chen <wei.chen@xxxxxxx>
This function will be used when system enter sleep and need wakeup
in a specific time. For ns_to_ticks precision, we limited the max
sleep time to 3600 seconds.
Signed-off-by: Wei Chen <wei.chen@xxxxxxx>
Signed-off-by: Jianyong Wu <jianyong.wu@xxxxxxx>
Signed-off-by: Jia He <justin.he@xxxxxxx>
---
plat/common/arm/time.c | 78 +++++++++++++++++++++++++++++++++++++++---
1 file changed, 73 insertions(+), 5 deletions(-)
diff --git a/plat/common/arm/time.c b/plat/common/arm/time.c
index 9365ceb..328973d 100644
--- a/plat/common/arm/time.c
+++ b/plat/common/arm/time.c
@@ -79,6 +79,11 @@ static uint32_t tick_per_ns;
*/
#define __MAX_CONVERT_SECS 3600UL
+/*
+ * Minimum delta to sleep using generic timer.
+ */
+static uint32_t counter_mini_delta;
+
/* How many nanoseconds per second */
#define NSEC_PER_SEC ukarch_time_sec_to_nsec(1)
@@ -222,6 +227,67 @@ static uint64_t generic_timer_epochoffset(void)
return 0;
}
+/*
+ * Returns early if any interrupts are serviced, or if the requested delay is
+ * too short. Must be called with interrupts disabled, will enable interrupts
+ * "atomically" during idle loop.
+ *
+ * This function must be called only from the scheduler. It will screw
+ * your system if you do otherwise. And, there is no reason you
+ * actually want to use it anywhere else. THIS IS NOT A YIELD or any
+ * kind of mutex_lock. It will simply halt the cpu, not allowing any
+ * other thread to execute.
+ */
+static void generic_timer_cpu_block(uint64_t until_ns)
+{
+ uint64_t now_ns, delta_ns;
+ uint64_t now_ticks, delta_ticks;
+
+ UK_ASSERT(ukplat_lcpu_irqs_disabled());
+
+ /* Record current ticks */
+ now_ticks = generic_timer_get_ticks();
+ now_ns = ticks_to_ns(now_ticks - boot_ticks);
+
+ /*
+ * Compute delta in counter ticks. Return if it is less than minimum
+ * safe amount of ticks. Essentially this will cause us to spin until
+ * the timeout.
+ */
+ delta_ns = until_ns - now_ns;
+ delta_ticks = ns_to_ticks(delta_ns);
+ if (delta_ticks < counter_mini_delta) {
+ /*
+ * Since we are "spinning", quickly enable interrupts in
+ * the hopes that we might get new work and can do something
+ * else than spin.
+ */
+ ukplat_lcpu_enable_irq();
+ nop();
+ ukplat_lcpu_disable_irq();
+
+ return;
+ }
+
+ /* Calculate the next match ticks for compare counter */
+ generic_timer_update_compare(now_ticks + delta_ticks);
+
+ /* Unmask the IRQ for next match interrupt */
+ generic_timer_unmask_irq();
+
+ /*
+ * Wait for any interrupt. If we got an interrupt then just
+ * return into the scheduler (this func is called _ONLY_ from
+ * a scheduler, see the note above) which will check if there
+ * is work to do and call us again here if not.
+ *
+ * TODO: It would be more efficient for longer sleeps to be
+ * able to distinguish if the interrupt was the timer interrupt
+ * and no other, but this will do for now.
+ */
+ ukplat_lcpu_halt_irq();
+}
+
static int generic_timer_init(int fdt_timer)
{
/* Get counter frequency from DTB or register */
@@ -247,6 +313,12 @@ static int generic_timer_init(int fdt_timer)
/* We disallow zero ns_per_tick */
UK_BUGON(!tick_per_ns);
+ /*
+ * Set minimal counter delta, programming seems to have an overhead
+ * of 3-4us, but play it safe here.
+ */
+ counter_mini_delta = ns_to_ticks(4000);
+
return 0;
}
@@ -267,11 +339,7 @@ unsigned long sched_have_pending_events;
void time_block_until(__snsec until)
{
while ((__snsec) ukplat_monotonic_clock() < until) {
- /*
- * TODO:
- * As we haven't support interrupt on Arm, so we just
- * use busy polling for now.
- */
+ generic_timer_cpu_block(until);
if (__uk_test_and_clear_bit(0, &sched_have_pending_events))
break;
}
--
2.17.1
_______________________________________________
Minios-devel mailing list
Minios-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/minios-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |