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;
}
#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
* 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] = {
/* 16-bits */
VMX_VPID,
VMX_GUEST_ES_SEL,
VMX_GUEST_CS_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)
|| (vmcs_field == VMX_EPT_POINTER_FULL)
|| (vmcs_field == VMX_VPID)
|| (vmcs_field == VMX_ENTRY_CONTROLS)
|| (vmcs_field == VMX_EXIT_CONTROLS)) {
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 */
value64 = vmcs12->vm_exit_controls | VMX_EXIT_CTLS_HOST_ADDR64;
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);
}
/**
* @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
*
@ -1331,7 +1343,6 @@ static void set_vmcs01_guest_state(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;
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) {
/*
* 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);
sanitize_l2_vpid(&vcpu->arch.nested.vmcs12);
/*
* 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;
}
sanitize_l2_vpid(&vcpu->arch.nested.vmcs12);
/*
* There are two reasons to set vcpu->launched to false 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.
* set vcpu->launched to false because the launch state of VMCS02 is
* clear at this moment, even for VMRESUME
*/
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.
*/
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:
@ -667,7 +667,9 @@ int32_t run_vcpu(struct acrn_vcpu *vcpu)
* A power-up or a reset invalidates all linear mappings,
* guest-physical mappings, and combined mappings
*/
flush_vpid_global();
if (!is_vcpu_in_l2_guest(vcpu)) {
flush_vpid_global();
}
#ifdef CONFIG_HYPERV_ENABLED
if (is_vcpu_bsp(vcpu)) {

View File

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