diff --git a/hypervisor/arch/x86/cpu.c b/hypervisor/arch/x86/cpu.c index ce403b7ec..9b68a8d98 100644 --- a/hypervisor/arch/x86/cpu.c +++ b/hypervisor/arch/x86/cpu.c @@ -462,7 +462,25 @@ void stop_pcpus(void) void cpu_do_idle(void) { +#ifdef CONFIG_KEEP_IRQ_DISABLED asm_pause(); +#else + uint16_t pcpu_id = get_pcpu_id(); + + if (per_cpu(mode_to_idle, pcpu_id) == IDLE_MODE_HLT) { + asm_safe_hlt(); + } else { + struct acrn_vcpu *vcpu = get_ever_run_vcpu(pcpu_id); + + if ((vcpu != NULL) && !is_lapic_pt_enabled(vcpu)) { + CPU_IRQ_ENABLE_ON_CONFIG(); + } + asm_pause(); + if ((vcpu != NULL) && !is_lapic_pt_enabled(vcpu)) { + CPU_IRQ_DISABLE_ON_CONFIG(); + } + } +#endif } /** diff --git a/hypervisor/arch/x86/guest/vcpu.c b/hypervisor/arch/x86/guest/vcpu.c index 86ff3ec1a..8a53f97d7 100755 --- a/hypervisor/arch/x86/guest/vcpu.c +++ b/hypervisor/arch/x86/guest/vcpu.c @@ -528,9 +528,22 @@ int32_t create_vcpu(uint16_t pcpu_id, struct acrn_vm *vm, struct acrn_vcpu **rtn per_cpu(ever_run_vcpu, pcpu_id) = vcpu; if (is_lapic_pt_configured(vm) || is_using_init_ipi()) { + /* Lapic_pt pCPU does not enable irq in root mode. So it + * should be set to PAUSE idle mode. + * At this point the pCPU is possibly in HLT idle. And the + * kick mode is to be set to INIT kick, which will not be + * able to wake root mode HLT. So a kick(if pCPU is in HLT + * idle, the kick mode is certainly ipi kick) will change + * it to PAUSE idle right away. + */ + if (per_cpu(mode_to_idle, pcpu_id) == IDLE_MODE_HLT) { + per_cpu(mode_to_idle, pcpu_id) = IDLE_MODE_PAUSE; + kick_pcpu(pcpu_id); + } per_cpu(mode_to_kick_pcpu, pcpu_id) = DEL_MODE_INIT; } else { per_cpu(mode_to_kick_pcpu, pcpu_id) = DEL_MODE_IPI; + per_cpu(mode_to_idle, pcpu_id) = IDLE_MODE_HLT; } pr_info("pcpu=%d, kick-mode=%d, use_init_flag=%d", pcpu_id, per_cpu(mode_to_kick_pcpu, pcpu_id), is_using_init_ipi()); diff --git a/hypervisor/common/event.c b/hypervisor/common/event.c index 7735b3ae3..8de7ceae2 100644 --- a/hypervisor/common/event.c +++ b/hypervisor/common/event.c @@ -25,7 +25,13 @@ void reset_event(struct sched_event *event) spinlock_irqrestore_release(&event->lock, rflag); } -/* support exclusive waiting only */ +/* support exclusive waiting only + * + * During wait, the pCPU could be scheduled to run the idle thread when run queue + * is empty. Signal_event() can happen when schedule() is in process. + * This signal_event is not going to be lost, for the idle thread will always + * check need_reschedule() after it is switched to at schedule(). + */ void wait_event(struct sched_event *event) { uint64_t rflag; diff --git a/hypervisor/common/hv_main.c b/hypervisor/common/hv_main.c index 01a0e50ca..3f8b2c949 100644 --- a/hypervisor/common/hv_main.c +++ b/hypervisor/common/hv_main.c @@ -86,9 +86,7 @@ void default_idle(__unused struct thread_object *obj) } else if (need_shutdown_vm(pcpu_id)) { shutdown_vm_from_idle(pcpu_id); } else { - CPU_IRQ_ENABLE_ON_CONFIG(); cpu_do_idle(); - CPU_IRQ_DISABLE_ON_CONFIG(); } } } diff --git a/hypervisor/common/schedule.c b/hypervisor/common/schedule.c index 422fca744..32cadd7b6 100644 --- a/hypervisor/common/schedule.c +++ b/hypervisor/common/schedule.c @@ -65,6 +65,9 @@ void init_sched(uint16_t pcpu_id) { struct sched_control *ctl = &per_cpu(sched_ctl, pcpu_id); + per_cpu(mode_to_idle, pcpu_id) = IDLE_MODE_HLT; + per_cpu(mode_to_kick_pcpu, pcpu_id) = DEL_MODE_IPI; + spinlock_init(&ctl->scheduler_lock); ctl->flags = 0UL; ctl->curr_obj = NULL; diff --git a/hypervisor/include/arch/x86/asm/cpu.h b/hypervisor/include/arch/x86/asm/cpu.h index d9fe71641..5dacf910f 100755 --- a/hypervisor/include/arch/x86/asm/cpu.h +++ b/hypervisor/include/arch/x86/asm/cpu.h @@ -533,6 +533,15 @@ static inline void asm_hlt(void) asm volatile ("hlt"); } +/* interrupts remain inhibited on the instruction boundary following + * an execution of STI. This will make sure pending interrupts will + * wake hlt. + */ +static inline void asm_safe_hlt(void) +{ + asm volatile ("sti; hlt; cli" : : : "cc"); +} + /* Disables interrupts on the current CPU */ #ifdef CONFIG_KEEP_IRQ_DISABLED #define CPU_IRQ_DISABLE_ON_CONFIG() do { } while (0) diff --git a/hypervisor/include/arch/x86/asm/per_cpu.h b/hypervisor/include/arch/x86/asm/per_cpu.h index f7cbfe1a5..97e4c058b 100644 --- a/hypervisor/include/arch/x86/asm/per_cpu.h +++ b/hypervisor/include/arch/x86/asm/per_cpu.h @@ -55,6 +55,7 @@ struct per_cpu_region { uint32_t lapic_ldr; uint32_t softirq_servicing; uint32_t mode_to_kick_pcpu; + uint32_t mode_to_idle; struct smp_call_info_data smp_call_info; struct list_head softirq_dev_entry_list; #ifdef PROFILING_ON diff --git a/hypervisor/include/common/schedule.h b/hypervisor/include/common/schedule.h index 04bb6c872..fa2a6e44f 100644 --- a/hypervisor/include/common/schedule.h +++ b/hypervisor/include/common/schedule.h @@ -15,6 +15,9 @@ #define DEL_MODE_INIT (1U) #define DEL_MODE_IPI (2U) +#define IDLE_MODE_PAUSE (1U) +#define IDLE_MODE_HLT (2U) + #define THREAD_DATA_SIZE (256U) enum thread_object_state {