先来看一下相关结构体的相关部分
struct vcpu_vmx {
struct kvm_vcpu vcpu;
/*
* loaded_vmcs points to the VMCS currently used in this vcpu. For a
* non-nested (L1) guest, it always points to vmcs01. For a nested
* guest (L2), it points to a different VMCS.
*/
struct loaded_vmcs vmcs01;
struct loaded_vmcs *loaded_vmcs;
}
struct kvm_vcpu {
int cpu; /*运行当前VCPU的物理CPU编号*/
}
struct loaded_vmcs {
struct vmcs *vmcs; /*本VCPU对应的VMCS*/
int cpu; /*上一次运行的CPU编号*/
int launched;
struct list_head loaded_vmcss_on_cpu_link;
};
VMX其实是VCPU的一个运行环境,理解为environment。集中通过loaded_vmcs和vcpu成员将vmcs和CPU及VMCS关联起来。
一个VCPU当然可以运行在不同的物理CPU之上,只要更换loaded_vmcs中cpu编号即可;
但是为什么会一个VCPU对应多个不同的VMCS呢?其实是因为层叠虚拟化的原因,当L2虚拟机的VMCS加载后,VCPU所使用的VMCS不是L1层的VMCS;而是L2层的VMCS;其实就是把L1的VCPU在L2中当做了物理CPU用,物理CPU当然可以有多个VMCS了。
一般L1中,loaded_vmcs就执行vmcs01,当VCPU运行的物理CPU发生切换的时候,修改loaded_vmcs中的cpu成员即可
static struct kvm_vcpu *vmx_create_vcpu(struct kvm *kvm, unsigned int id)
{
/*vmcs的分配*/
vmx->loaded_vmcs = &vmx->vmcs01;
vmx->loaded_vmcs->vmcs = alloc_vmcs();
}
在L2中,loaded_vmcs会在L1 VMCS和各个L2 VMCS之间切换,那么就需要修改loaded_vmcs指针的指向
static int nested_vmx_run(struct kvm_vcpu *vcpu, bool launch)
{
cpu = get_cpu();
vmx->loaded_vmcs = vmcs02;
vmx_vcpu_put(vcpu);
vmx_vcpu_load(vcpu, cpu);
vcpu->cpu = cpu;
put_cpu();
}