From a7706e0c39a01c1786b2599503cf86ac08e1b79f Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Tue, 27 Aug 2019 16:25:39 +0800 Subject: [PATCH] HV: Use the mwait instead of pause for cpu_idle Now it will use the pause when cpu is in idle state. It will consume more power. If the mwait is supported, it will use the monitor/mwait to enter the deep CPU C-state. It helps to save power. Signed-off-by: Zhao Yakui --- hypervisor/arch/x86/cpu.c | 39 ++++++++++++++++++---------- hypervisor/common/hv_main.c | 2 -- hypervisor/common/schedule.c | 19 ++++++++++++++ hypervisor/include/common/schedule.h | 6 +++-- 4 files changed, 49 insertions(+), 17 deletions(-) diff --git a/hypervisor/arch/x86/cpu.c b/hypervisor/arch/x86/cpu.c index 7e1d5c1dc..19d0234cb 100644 --- a/hypervisor/arch/x86/cpu.c +++ b/hypervisor/arch/x86/cpu.c @@ -367,9 +367,34 @@ void stop_pcpus(void) wait_pcpus_offline(mask); } +static +inline void asm_monitor(const uint64_t *addr, uint64_t ecx, uint64_t edx) +{ + asm volatile("monitor\n" : : "a" (addr), "c" (ecx), "d" (edx)); +} + +static +inline void asm_mwait(uint64_t eax, uint64_t ecx) +{ + asm volatile("mwait\n" : : "a" (eax), "c" (ecx)); +} + void cpu_do_idle(void) { - asm_pause(); + uint16_t pcpu_id = get_pcpu_id(); + struct sched_context *ctx = &per_cpu(sched_ctx, pcpu_id); + + if (ctx->mwait_flags) { + CPU_IRQ_DISABLE(); + asm_monitor(&ctx->flags, 0UL, 0UL); + if (!bitmap_test(NEED_RESCHEDULE, &ctx->flags)) + asm_mwait(0x60UL, 1UL); + CPU_IRQ_ENABLE(); + } else { + CPU_IRQ_ENABLE(); + asm_pause(); + CPU_IRQ_DISABLE(); + } } /** @@ -415,18 +440,6 @@ static void print_hv_banner(void) printf(boot_msg); } -static -inline void asm_monitor(const uint64_t *addr, uint64_t ecx, uint64_t edx) -{ - asm volatile("monitor\n" : : "a" (addr), "c" (ecx), "d" (edx)); -} - -static -inline void asm_mwait(uint64_t eax, uint64_t ecx) -{ - asm volatile("mwait\n" : : "a" (eax), "c" (ecx)); -} - /* wait until *sync == wake_sync */ void wait_sync_change(uint64_t *sync, uint64_t wake_sync) { diff --git a/hypervisor/common/hv_main.c b/hypervisor/common/hv_main.c index 67e8c0cc7..70926751e 100644 --- a/hypervisor/common/hv_main.c +++ b/hypervisor/common/hv_main.c @@ -92,9 +92,7 @@ void default_idle(__unused struct sched_object *obj) } else if (need_shutdown_vm(pcpu_id)) { shutdown_vm_from_idle(pcpu_id); } else { - CPU_IRQ_ENABLE(); cpu_do_idle(); - CPU_IRQ_DISABLE(); } } } diff --git a/hypervisor/common/schedule.c b/hypervisor/common/schedule.c index c54bbf97e..5b6c7b327 100644 --- a/hypervisor/common/schedule.c +++ b/hypervisor/common/schedule.c @@ -12,6 +12,12 @@ #include #include #include +#include +#include + +#define CPUID_MWAIT_LEAF 5 +#define CPUID5_ECX_EXTENSIONS_SUPPORTED 0x1 +#define CPUID5_ECX_INTERRUPT_BREAK 0x2 static uint64_t pcpu_used_bitmap; @@ -20,7 +26,19 @@ void init_scheduler(void) struct sched_context *ctx; uint32_t i; uint16_t pcpu_nums = get_pcpu_nums(); + uint32_t mwait_flag = 0; + if (has_monitor_cap()) { + uint32_t eax, ebx, ecx, edx; + + mwait_flag = 1; + cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx); + if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) || + !(ecx & CPUID5_ECX_INTERRUPT_BREAK) || + !edx) { + mwait_flag = 0; + } + } for (i = 0U; i < pcpu_nums; i++) { ctx = &per_cpu(sched_ctx, i); @@ -29,6 +47,7 @@ void init_scheduler(void) INIT_LIST_HEAD(&ctx->runqueue); ctx->flags = 0UL; ctx->curr_obj = NULL; + ctx->mwait_flags = mwait_flag; } } diff --git a/hypervisor/include/common/schedule.h b/hypervisor/include/common/schedule.h index 3cd45a915..29bd3e8c1 100644 --- a/hypervisor/include/common/schedule.h +++ b/hypervisor/include/common/schedule.h @@ -28,12 +28,14 @@ struct sched_object { }; struct sched_context { + uint64_t flags; + uint64_t mwait_flags; + uint64_t rserved[6]; spinlock_t runqueue_lock; struct list_head runqueue; - uint64_t flags; struct sched_object *curr_obj; spinlock_t scheduler_lock; -}; +} __aligned(64); void init_scheduler(void); void switch_to_idle(run_thread_t idle_thread);