设为首页 收藏本站
查看: 1672|回复: 0

[经验分享] kernel 3.10代码分析--KVM相关--VCPU创建

[复制链接]

尚未签到

发表于 2015-12-24 14:51:19 | 显示全部楼层 |阅读模式
1、基本原理
如之前的文章分析,在KVM虚拟化环境中,
硬件虚拟化使用VCPU(Virtual CPU)描述符来描述虚拟CPU,VCPU描述符与OS中进程描述符类似,本质是一个结构体kvm_vcpu,其中包含如下信息: VCPU标识信息,如VCPUID号,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、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-155807-1-1.html 上篇帖子: kernel 3.10代码分析--KVM相关--虚拟机创建 下篇帖子: kernel 3.10代码分析--KVM相关--KVM_SET_USER_MEMORY_REGION流程
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表