hv: nested: flush L2 VPID only when it could conflict with L1 VPIDs

By changing the way to assign L1 VPID from bottom-up to top-down,
the possibilities for VPID conflicts between L1 and L2 guests are
small.

Then we can flush VPID just in case of conflicting.

Tracked-On: #6289
Signed-off-by: Anthony Xu <anthony.xu@intel.com>
Signed-off-by: Zide Chen <zide.chen@intel.com>
Acked-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
Zide Chen 2021-08-10 16:24:20 -07:00 committed by wenlingz
parent 730a275ecc
commit e9eb72d319
3 changed files with 25 additions and 21 deletions

View File

@ -238,7 +238,7 @@ int32_t read_vmx_msr(struct acrn_vcpu *vcpu, uint32_t msr, uint64_t *val)
return err; return err;
} }
#define MAX_SHADOW_VMCS_FIELDS 114U #define MAX_SHADOW_VMCS_FIELDS 113U
/* /*
* VMCS fields included in the dual-purpose VMCS: as shadow for L1 and * VMCS fields included in the dual-purpose VMCS: as shadow for L1 and
* as hardware VMCS for nested guest (L2). * as hardware VMCS for nested guest (L2).
@ -254,8 +254,6 @@ int32_t read_vmx_msr(struct acrn_vcpu *vcpu, uint32_t msr, uint64_t *val)
*/ */
static const uint32_t vmcs_shadowing_fields[MAX_SHADOW_VMCS_FIELDS] = { static const uint32_t vmcs_shadowing_fields[MAX_SHADOW_VMCS_FIELDS] = {
/* 16-bits */ /* 16-bits */
VMX_VPID,
VMX_GUEST_ES_SEL, VMX_GUEST_ES_SEL,
VMX_GUEST_CS_SEL, VMX_GUEST_CS_SEL,
VMX_GUEST_SS_SEL, VMX_GUEST_SS_SEL,
@ -875,6 +873,7 @@ int32_t vmwrite_vmexit_handler(struct acrn_vcpu *vcpu)
if ((vmcs_field == VMX_MSR_BITMAP_FULL) if ((vmcs_field == VMX_MSR_BITMAP_FULL)
|| (vmcs_field == VMX_EPT_POINTER_FULL) || (vmcs_field == VMX_EPT_POINTER_FULL)
|| (vmcs_field == VMX_VPID)
|| (vmcs_field == VMX_ENTRY_CONTROLS) || (vmcs_field == VMX_ENTRY_CONTROLS)
|| (vmcs_field == VMX_EXIT_CONTROLS)) { || (vmcs_field == VMX_EXIT_CONTROLS)) {
vcpu->arch.nested.control_fields_dirty = true; vcpu->arch.nested.control_fields_dirty = true;
@ -945,6 +944,8 @@ static void merge_and_sync_control_fields(struct acrn_vcpu *vcpu)
/* Host is alway runing in 64-bit mode */ /* Host is alway runing in 64-bit mode */
value64 = vmcs12->vm_exit_controls | VMX_EXIT_CTLS_HOST_ADDR64; value64 = vmcs12->vm_exit_controls | VMX_EXIT_CTLS_HOST_ADDR64;
exec_vmwrite(VMX_EXIT_CONTROLS, value64); exec_vmwrite(VMX_EXIT_CONTROLS, value64);
exec_vmwrite(VMX_VPID, vmcs12->vpid);
} }
/** /**
@ -1323,6 +1324,17 @@ static void set_vmcs01_guest_state(struct acrn_vcpu *vcpu)
vcpu_set_rflags(vcpu, 0x2U); vcpu_set_rflags(vcpu, 0x2U);
} }
/**
* @pre vcpu != NULL
*/
static void sanitize_l2_vpid(struct acrn_vmcs12 *vmcs12)
{
/* Flush VPID if the L2 VPID could be conflicted with any L1 VPIDs */
if (vmcs12->vpid >= ALLOCATED_MIN_L1_VPID) {
flush_vpid_single(vmcs12->vpid);
}
}
/** /**
* @brief handler for all VMEXITs from nested guests * @brief handler for all VMEXITs from nested guests
* *
@ -1331,7 +1343,6 @@ static void set_vmcs01_guest_state(struct acrn_vcpu *vcpu)
*/ */
int32_t nested_vmexit_handler(struct acrn_vcpu *vcpu) int32_t nested_vmexit_handler(struct acrn_vcpu *vcpu)
{ {
struct acrn_vmcs12 *vmcs12 = (struct acrn_vmcs12 *)&vcpu->arch.nested.vmcs12;
bool is_l1_vmexit = true; bool is_l1_vmexit = true;
if ((vcpu->arch.exit_reason & 0xFFFFU) == VMX_EXIT_REASON_EPT_VIOLATION) { if ((vcpu->arch.exit_reason & 0xFFFFU) == VMX_EXIT_REASON_EPT_VIOLATION) {
@ -1339,13 +1350,7 @@ int32_t nested_vmexit_handler(struct acrn_vcpu *vcpu)
} }
if (is_l1_vmexit) { if (is_l1_vmexit) {
/* sanitize_l2_vpid(&vcpu->arch.nested.vmcs12);
* Currerntly VMX_VPID is shadowing to L1, so we flush L2 VPID before
* L1 VM entry to avoid conflicting with L1 VPID.
*
* TODO: emulate VPID for L2 so that we can save this VPID flush
*/
flush_vpid_single(vmcs12->vpid);
/* /*
* Clear VMCS02 because: ISDM: Before modifying the shadow-VMCS indicator, * Clear VMCS02 because: ISDM: Before modifying the shadow-VMCS indicator,
@ -1411,15 +1416,11 @@ static void nested_vmentry(struct acrn_vcpu *vcpu, bool is_launch)
vmcs12->launch_state = VMCS12_LAUNCH_STATE_LAUNCHED; vmcs12->launch_state = VMCS12_LAUNCH_STATE_LAUNCHED;
} }
sanitize_l2_vpid(&vcpu->arch.nested.vmcs12);
/* /*
* There are two reasons to set vcpu->launched to false even for VMRESUME: * set vcpu->launched to false because the launch state of VMCS02 is
* * clear at this moment, even for VMRESUME
* - the launch state of VMCS02 is clear at this moment.
* - currently VMX_VPID is shadowing to L1, and it could happens that
* L2 VPID will be conflicted with L1 VPID. We rely on run_vcpu() to
* flush global vpid in the VMLAUNCH path to resolve this conflict.
*
* TODO: emulate L2 VPID to avoid VPID flush.
*/ */
vcpu->launched = false; vcpu->launched = false;
} }

View File

@ -545,7 +545,7 @@ int32_t create_vcpu(uint16_t pcpu_id, struct acrn_vm *vm, struct acrn_vcpu **rtn
* *
* This assignment guarantees a unique non-zero per vcpu vpid at runtime. * This assignment guarantees a unique non-zero per vcpu vpid at runtime.
*/ */
vcpu->arch.vpid = 1U + (vm->vm_id * MAX_VCPUS_PER_VM) + vcpu->vcpu_id; vcpu->arch.vpid = ALLOCATED_MIN_L1_VPID + (vm->vm_id * MAX_VCPUS_PER_VM) + vcpu->vcpu_id;
/* /*
* ACRN uses the following approach to manage VT-d PI notification vectors: * ACRN uses the following approach to manage VT-d PI notification vectors:
@ -667,7 +667,9 @@ int32_t run_vcpu(struct acrn_vcpu *vcpu)
* A power-up or a reset invalidates all linear mappings, * A power-up or a reset invalidates all linear mappings,
* guest-physical mappings, and combined mappings * guest-physical mappings, and combined mappings
*/ */
flush_vpid_global(); if (!is_vcpu_in_l2_guest(vcpu)) {
flush_vpid_global();
}
#ifdef CONFIG_HYPERV_ENABLED #ifdef CONFIG_HYPERV_ENABLED
if (is_vcpu_bsp(vcpu)) { if (is_vcpu_bsp(vcpu)) {

View File

@ -238,6 +238,7 @@ struct acrn_vcpu_arch {
/* common MSRs, world_msrs[] is a subset of it */ /* common MSRs, world_msrs[] is a subset of it */
uint64_t guest_msrs[NUM_GUEST_MSRS]; uint64_t guest_msrs[NUM_GUEST_MSRS];
#define ALLOCATED_MIN_L1_VPID (0x10000U - CONFIG_MAX_VM_NUM * MAX_VCPUS_PER_VM)
uint16_t vpid; uint16_t vpid;
/* Holds the information needed for IRQ/exception handling. */ /* Holds the information needed for IRQ/exception handling. */