美奇科技 发表于 2015-12-24 14:51:19

kernel 3.10代码分析--KVM相关--VCPU创建

1、基本原理
如之前的文章分析,在KVM虚拟化环境中,硬件虚拟化使用VCPU(Virtual CPU)描述符来描述虚拟CPU,VCPU描述符与OS中进程描述符类似,本质是一个结构体kvm_vcpu,其中包含如下信息: VCPU标识信息,如VCPU的ID号,VCPU属于哪个Guest等。
虚拟寄存器信息,在VT-x的环境中,这些信息包含在VMCS中。
VCPU状态信息,标识白VCPU当前所处的状态(睡眠、运行等),主要供调度器使用。
额外的寄存器/部件信息,主要指未包含在VMCS中的寄存器或CPU部件,比如:浮点寄存器和虚拟的LAPIC等。
其他信息:用户VMM进行优化或存储额外信息的字段,如:存放该VCPU私有数据的指针。
当VMM创建虚拟机时,首先要为虚拟机创建VCPU,整个虚拟机的运行实际上可以看做VMM调度不同的VCPU运行。
虚拟机的VCPU通过ioctl VM指令KVM_CREATE_VCPU实现,实质为创建kvm_vcpu结构体,并进行相关初始化。本文简单分析VCPU创建过程,qemu-kvm用户态实现部分暂不包括。

2、基本流程
kvm_vm_ioctl() // kvm ioctl vm指令入口
    kvm_vm_ioctl_create_vcpu() // 为虚拟机创建VCPU的ioctl调用的入口函数
      kvm_arch_vcpu_create() // 创建vcpu结构,架构相关,对于intel x86来说,最终调用vmx_create_vcpu
      kvm_arch_vcpu_setup() // 设置VCPU结构
      create_vcpu_fd() // 为新创建的vcpu创建对应的fd,以便于后续通过该fd进行ioctl操作
      kvm_arch_vcpu_postcreate() // 架构相关的善后工作,比如再次调用vcpu_load,以及tsc相关处理

3、代码分析
kvm_vcpu结构:




[*]struct kvm_vcpu {

[*]    // 指向此vcpu所属的虚拟机对应的kvm结构
[*]    struct kvm *kvm;
[*]#ifdef CONFIG_PREEMPT_NOTIFIERS
[*]    struct preempt_notifier preempt_notifier;
[*]#endif
[*]    int cpu;
[*]    // vcpu id,用于唯一标识该vcpu
[*]    int vcpu_id;
[*]    int srcu_idx;
[*]    int mode;
[*]    unsigned long requests;
[*]    unsigned long guest_debug;
[*]
[*]    struct mutex mutex;
[*]    // 执行虚拟机对应的kvm_run结构
[*]    struct kvm_run *run;
[*]
[*]    int fpu_active;
[*]    int guest_fpu_loaded, guest_xcr0_loaded;
[*]    wait_queue_head_t wq;
[*]    struct pid *pid;
[*]    int sigset_active;
[*]    sigset_t sigset;
[*]    // vcpu状态信息
[*]    struct kvm_vcpu_stat stat;
[*]    // mmio相关部分
[*]#ifdef CONFIG_HAS_IOMEM
[*]    int mmio_needed;
[*]    int mmio_read_completed;
[*]    int mmio_is_write;
[*]    int mmio_cur_fragment;
[*]    int mmio_nr_fragments;
[*]    struct kvm_mmio_fragment mmio_fragments[KVM_MAX_MMIO_FRAGMENTS];
[*]#endif
[*]
[*]#ifdef CONFIG_KVM_ASYNC_PF
[*]    struct {
[*]      u32 queued;
[*]      struct list_head queue;
[*]      struct list_head done;
[*]      spinlock_t lock;
[*]    } async_pf;
[*]#endif
[*]
[*]#ifdef CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT
[*]    /*
[*]   * Cpu relax intercept or pause loop exit optimization
[*]   * in_spin_loop: set when a vcpu does a pause loop exit
[*]   * or cpu relax intercepted.
[*]   * dy_eligible: indicates whether vcpu is eligible for directed yield.
[*]   */
[*]    struct {
[*]      bool in_spin_loop;
[*]      bool dy_eligible;
[*]    } spin_loop;
[*]#endif
[*]    bool preempted;
[*]    // 架构相关部分,包括的寄存器、apic、mmu相关等架构相关的内容
[*]    struct kvm_vcpu_arch arch;
[*]};

kvm_vm_ioctl()-->kvm_vm_ioctl_create_vcpu():




[*]/*

[*]* 为虚拟机创建VCPU的ioctl调用的入口函数,本质为创建vcpu结构并初始化,并将其填入kvm结构中。
[*]*/
[*]static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, u32 id)
[*]{
[*]    int r;
[*]    struct kvm_vcpu *vcpu, *v;
[*]
[*]    // 创建vcpu结构,架构相关,对于intel x86来说,最终调用vmx_create_vcpu
[*]    vcpu = kvm_arch_vcpu_create(kvm, id);
[*]    if (IS_ERR(vcpu))
[*]      return PTR_ERR(vcpu);
[*]
[*]    preempt_notifier_init(&vcpu->preempt_notifier, &kvm_preempt_ops);
[*]
[*]    /*
[*]   * 设置vcpu结构,主要调用kvm_x86_ops->vcpu_load,KVM虚拟机VCPU数据结构载入物理CPU,
[*]   * 并进行虚拟机mmu相关设置,比如进行ept页表的相关初始工作或影子页表
[*]   * 相关的设置。
[*]   */
[*]    r = kvm_arch_vcpu_setup(vcpu);
[*]    if (r)
[*]      goto vcpu_destroy;
[*]
[*]    mutex_lock(&kvm->lock);
[*]    if (!kvm_vcpu_compatible(vcpu)) {
[*]      r = -EINVAL;
[*]      goto unlock_vcpu_destroy;
[*]    }
[*]    if (atomic_read(&kvm->online_vcpus) == KVM_MAX_VCPUS) {
[*]      r = -EINVAL;
[*]      goto unlock_vcpu_destroy;
[*]    }
[*]
[*]    // 检测分配的vcpu id是否已经存在
[*]    kvm_for_each_vcpu(r, v, kvm)
[*]      if (v->vcpu_id == id) {
[*]            r = -EEXIST;
[*]            goto unlock_vcpu_destroy;
[*]      }
[*]    /*
[*]   * kvm->vcpus[]数组包括该vm的所有vcpu,定义为KVM_MAX_VCPUS大小的数组。
[*]   * 在kvm结构初始化时,其中所有成员都初始化为0,在vcpu还没有
[*]   * 分配之前,如果不为0,那就是bug了。
[*]   */
[*]    BUG_ON(kvm->vcpus[atomic_read(&kvm->online_vcpus)]);
[*]
[*]    /* Now it's all set up, let userspace reach it */
[*]    // 增加kvm的引用计数
[*]    kvm_get_kvm(kvm);
[*]    // 为新创建的vcpu创建对应的fd,以便于后续通过该fd进行ioctl操作
[*]    r = create_vcpu_fd(vcpu);
[*]    if (r vcpus[]数组中
[*]    kvm->vcpus[atomic_read(&kvm->online_vcpus)] = vcpu;
[*]    // 内存屏障,防止同时访问kvm结构时乱序
[*]    smp_wmb();
[*]    // 增加online vcpu的数量
[*]    atomic_inc(&kvm->online_vcpus);
[*]
[*]    mutex_unlock(&kvm->lock);
[*]    // 架构相关的善后工作,比如再次调用vcpu_load,以及tsc相关处理
[*]    kvm_arch_vcpu_postcreate(vcpu);
[*]    return r;
[*]
[*]unlock_vcpu_destroy:
[*]    mutex_unlock(&kvm->lock);
[*]vcpu_destroy:
[*]    kvm_arch_vcpu_destroy(vcpu);
[*]    return r;
[*]}

kvm_vm_ioctl()-->kvm_vm_ioctl_create_vcpu()-->kvm_arch_vcpu_create()-->kvm_x86_ops->vcpu_create()-->vmx_create_vcpu():



[*]/*

[*]* Intel x86架构中创建并初始化VCPU中架构相关部分
[*]*/
[*]static struct kvm_vcpu *vmx_create_vcpu(struct kvm *kvm, unsigned int id)
[*]{
[*]    int err;
[*]    // 从slab中,分配vcpu_vmx结构体,其中包括VMX技术硬件相关信息。
[*]    struct vcpu_vmx *vmx = kmem_cache_zalloc(kvm_vcpu_cache, GFP_KERNEL);
[*]    int cpu;
[*]
[*]    if (!vmx)
[*]      return ERR_PTR(-ENOMEM);
[*]    // 分配vpid,vpid为VCPU的唯一标识。
[*]    allocate_vpid(vmx);
[*]    // 初始化vmx中的vcpu结构
[*]    err = kvm_vcpu_init(&vmx->vcpu, kvm, id);
[*]    if (err)
[*]      goto free_vcpu;
[*]    // 分配Guest的msr寄存器保存区
[*]    vmx->guest_msrs = kmalloc(PAGE_SIZE, GFP_KERNEL);
[*]    err = -ENOMEM;
[*]    if (!vmx->guest_msrs) {
[*]      goto uninit_vcpu;
[*]    }
[*]
[*]    vmx->loaded_vmcs = &vmx->vmcs01;
[*]    /*
[*]   * 分配VMCS结构,该结构用于保存虚拟机和虚拟机监控器的系统编程接口状态。
[*]   * 当执行VM exit和VM entry操作时,VT-x自动根据VMCS中的内容完成虚拟机和虚拟机监
[*]   * 控器间的系统编程接口状态切换。
[*]   */
[*]    vmx->loaded_vmcs->vmcs = alloc_vmcs();
[*]    if (!vmx->loaded_vmcs->vmcs)
[*]      goto free_msrs;
[*]    // 是否设置了vmm_exclusive
[*]    if (!vmm_exclusive)
[*]      // VMXON指令用于开启VMX模式
[*]      kvm_cpu_vmxon(__pa(per_cpu(vmxarea, raw_smp_processor_id())));
[*]    loaded_vmcs_init(vmx->loaded_vmcs);
[*]    if (!vmm_exclusive)
[*]      // VMXON指令用于关闭VMX模式
[*]      kvm_cpu_vmxoff();
[*]    // 当前cpu
[*]    cpu = get_cpu();
[*]    // KVM虚拟机VCPU数据结构载入物理CPU
[*]    vmx_vcpu_load(&vmx->vcpu, cpu);
[*]    vmx->vcpu.cpu = cpu;
[*]    // 设置vmx相关信息
[*]    err = vmx_vcpu_setup(vmx);
[*]    vmx_vcpu_put(&vmx->vcpu);
[*]    put_cpu();
[*]    if (err)
[*]      goto free_vmcs;
[*]    if (vm_need_virtualize_apic_accesses(kvm)) {
[*]      err = alloc_apic_access_page(kvm);
[*]      if (err)
[*]            goto free_vmcs;
[*]    }
[*]    // 是否支持EPT
[*]    if (enable_ept) {
[*]      if (!kvm->arch.ept_identity_map_addr)
[*]            kvm->arch.ept_identity_map_addr =
[*]                VMX_EPT_IDENTITY_PAGETABLE_ADDR;
[*]      err = -ENOMEM;
[*]      // 分配identity页表
[*]      if (alloc_identity_pagetable(kvm) != 0)
[*]            goto free_vmcs;
[*]      // 初始化identity页表
[*]      if (!init_rmode_identity_map(kvm))
[*]            goto free_vmcs;
[*]    }
[*]
[*]    vmx->nested.current_vmptr = -1ull;
[*]    vmx->nested.current_vmcs12 = NULL;
[*]
[*]    return &vmx->vcpu;
[*]
[*]free_vmcs:
[*]    free_loaded_vmcs(vmx->loaded_vmcs);
[*]free_msrs:
[*]    kfree(vmx->guest_msrs);
[*]uninit_vcpu:
[*]    kvm_vcpu_uninit(&vmx->vcpu);
[*]free_vcpu:
[*]    free_vpid(vmx);
[*]    kmem_cache_free(kvm_vcpu_cache, vmx);
[*]    return ERR_PTR(err);
[*]}
kvm_vm_ioctl()-->kvm_vm_ioctl_create_vcpu()-->kvm_arch_vcpu_setup():




[*]int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)

[*]{
[*]    int r;
[*]
[*]    vcpu->arch.mtrr_state.have_fixed = 1;
[*]    // KVM虚拟机VCPU数据结构载入物理CPU
[*]    r = vcpu_load(vcpu);
[*]    if (r)
[*]      return r;
[*]    // vcpu重置,包括相关寄存器、时钟、pmu等,最终调用vmx_vcpu_reset
[*]    kvm_vcpu_reset(vcpu);
[*]    /*
[*]   * 进行虚拟机mmu相关设置,比如进行ept页表的相关初始工作或影子页表
[*]   * 相关的设置。
[*]   */
[*]    r = kvm_mmu_setup(vcpu);
[*]    vcpu_put(vcpu);
[*]
[*]    return r;
[*]}
kvm_vm_ioctl()-->kvm_vm_ioctl_create_vcpu()-->kvm_arch_vcpu_setup()-->kvm_mmu_setup()-->init_kvm_mmu():




[*]static int init_kvm_mmu(struct kvm_vcpu *vcpu)

[*]{
[*]    // NPT(Nested page table,AMD x86硬件提供的内存虚拟化技术,相当于Intel中的EPT技术)相关初始化
[*]    if (mmu_is_nested(vcpu))
[*]      return init_kvm_nested_mmu(vcpu);
[*]    /*
[*]   * EPT(Extended page table,Intel x86硬件提供的内存虚拟化技术)相关初始化
[*]   * 主要是设置一些函数指针,其中比较重要的如缺页异常处理函数
[*]   */
[*]    else if (tdp_enabled)
[*]      return init_kvm_tdp_mmu(vcpu);
[*]    // 影子页表(软件实现内存虚拟化技术)相关初始化
[*]    else
[*]      return init_kvm_softmmu(vcpu);
[*]}

页: [1]
查看完整版本: kernel 3.10代码分析--KVM相关--VCPU创建