hv: pause all other vCPUs in same VM when do wbinvd emulation

Invalidate cache by scanning and flushing the whole guest memory is
inefficient which might cause long execution time for WBINVD emulation.
A long execution in hypervisor might cause a vCPU stuck phenomenon what
impact Windows Guest booting.

This patch introduce a workaround method that pausing all other vCPUs in
the same VM when do wbinvd emulation.

Tracked-On: #4703
Signed-off-by: Shuo A Liu <shuo.a.liu@intel.com>
Acked-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
Shuo A Liu 2020-04-16 13:45:43 +08:00 committed by wenlingz
parent a335679ce9
commit 9a15ea82ee
4 changed files with 42 additions and 2 deletions

View File

@ -241,6 +241,17 @@ void walk_ept_table(struct acrn_vm *vm, pge_handler cb)
}
}
}
/*
* Walk through the whole page tables of one VM is a time-consuming
* operation. Preemption is not support by hypervisor scheduling
* currently, so the walk through page tables operation might occupy
* CPU for long time what starve other threads.
*
* Give chance to release CPU to make other threads happy.
*/
if (need_reschedule(get_pcpu_id())) {
schedule();
}
}
}
}

View File

@ -374,6 +374,9 @@ int32_t acrn_handle_pending_request(struct acrn_vcpu *vcpu)
pr_fatal("Triple fault happen -> shutdown!");
ret = -EFAULT;
} else {
if (bitmap_test_and_clear_lock(ACRN_REQUEST_WAIT_WBINVD, pending_req_bits)) {
wait_event(&vcpu->events[VCPU_EVENT_SYNC_WBINVD]);
}
if (bitmap_test_and_clear_lock(ACRN_REQUEST_EPT_FLUSH, pending_req_bits)) {
invept(vcpu->vm->arch_vm.nworld_eptp);

View File

@ -379,10 +379,30 @@ static int32_t xsetbv_vmexit_handler(struct acrn_vcpu *vcpu)
static int32_t wbinvd_vmexit_handler(struct acrn_vcpu *vcpu)
{
uint16_t i;
struct acrn_vcpu *other;
if (has_rt_vm() == false) {
cache_flush_invalidate_all();
} else {
walk_ept_table(vcpu->vm, ept_flush_leaf_page);
if (is_rt_vm(vcpu->vm)) {
walk_ept_table(vcpu->vm, ept_flush_leaf_page);
} else {
/* Pause other vcpus and let them wait for the wbinvd completion */
foreach_vcpu(i, vcpu->vm, other) {
if (other != vcpu) {
vcpu_make_request(other, ACRN_REQUEST_WAIT_WBINVD);
}
}
walk_ept_table(vcpu->vm, ept_flush_leaf_page);
foreach_vcpu(i, vcpu->vm, other) {
if (other != vcpu) {
signal_event(&other->events[VCPU_EVENT_SYNC_WBINVD]);
}
}
}
}
return 0;

View File

@ -93,6 +93,11 @@
*/
#define ACRN_REQUEST_INIT_VMCS 8U
/**
* @brief Request for sync waiting WBINVD
*/
#define ACRN_REQUEST_WAIT_WBINVD 9U
/**
* @}
*/
@ -148,7 +153,8 @@ enum vm_cpu_mode {
#define VCPU_EVENT_IOREQ 0
#define VCPU_EVENT_VIRTUAL_INTERRUPT 1
#define VCPU_EVENT_NUM 2
#define VCPU_EVENT_SYNC_WBINVD 2
#define VCPU_EVENT_NUM 3
enum reset_mode;