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指令 K VM_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 < 0) {
- kvm_put_kvm(kvm);
- goto unlock_vcpu_destroy;
- }
- // 将新创建的vcpu填入kvm->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);
- }
KVM vCPU创建过程相关推荐
- kernel 3.10代码分析--KVM相关--虚拟机创建\VCPU创建\虚拟机运行
分三部分:一是KVM虚拟机创建.二是VCPU创建.三是KVM虚拟机运行 第一部分: 1.基本原理 如之前分析,kvm虚拟机通过对/dev/kvm字符设备的ioctl的System指令KVM_CREAT ...
- Ubuntu20.4系统下安装kvm并创建虚拟机
Ubuntu20.4系统下安装kvm并创建虚拟机 一.安装Ubuntu20.4系统 二.在Ubuntu20.4系统下安装KVM 三.kvm虚拟机及其磁盘映像文件(CentOS 7.9)制作 一.安装U ...
- java初始化实例化_Java对象的创建过程:类的初始化与实例化
一.Java对象创建时机 我们知道,一个对象在可以被使用之前必须要被正确地实例化.在Java代码中,有很多行为可以引起对象的创建,最为直观的一种就是使用new关键字来调用一个类的构造函数显式地创建对象 ...
- iOS 的本地化使用和创建过程
在使用本地化语言之前,来看看本地化语言文件内容的结构(这里我以Chinese为例): "Cancel"="取消"; "OK"="确 ...
- python不能创建新变量_Python之变量的创建过程!
Python之变量的创建过程 一.变量创建过程 首先,当我们定义了一个变量name = 'Kwan'的时候,在内存中其实是做了这样一件事: 程序开辟了一块内存空间,将'Kwan'存储进去,再让变量名n ...
- 深入浅出Spring Security(二):FilterChainProxy的创建过程
上篇回顾 框架的核心是一个过滤器,这个过滤器名字叫springSecurityFilterChain,类型是FilterChainProxy WebSecurity和HttpSecurity都是建造者 ...
- OpenStack虚机网卡的创建过程
OpenStack虚机网卡的创建过程 OpenStack最基本和常用的操作就是启动虚机.虚机启动的过程中涉及很多内容,其中非常重要的一个环节就是创建并绑定虚机的虚拟网卡.虚机的创建和管理是Nova的任 ...
- 深入理解WMS(一):Window的创建过程
8.3 Window的创建过程 View是Android中的视图的呈现方式,但是View不能单独存在,它必须附着在Window这个抽象的概念上面,因此有视图的地方就有Window.Android中可以 ...
- Android10.0应用进程创建过程以及Zygote的fork流程-[Android取经之路]
摘要:点击手机桌面图标,例如微信,它是如何启动的呢,让我们从系统源码级来一起分析. 阅读本文大约需要花费1小时. 文章的内容主要还是从源码进行分析,虽然又臭又长,但是如果想要学习Android系统源码 ...
- netty 5 alph1源码分析(服务端创建过程)
研究了netty的服务端创建过程.至于netty的优势,可以参照网络其他文章.<Netty系列之Netty 服务端创建>是 李林锋撰写的netty源码分析的一篇好文,绝对是技术干货.但抛开 ...
最新文章
- DayDayUp:寒门女孩考入北大→换角度看待表达《感谢贫穷》—关于吃苦与穷~~~Python之wordcloud词云图可视化
- 从营销手段到商业新基建,“以旧换新”还有多少价值等待挖掘?
- k8s集群部分常见问题处理
- C# 动态创建数据库三(MySQL)
- win7局域网计算机 慢,Win7系统开机宽带连接很慢怎么办?
- vivado中如何读取十进制小数_二进制十进制间小数怎么转换,原来是这样的
- centos搭建rsync服务!
- 域管理员在域计算机安装程序,本人电脑菜鸟,现在正用管理员用户登录,如何设置某个域用户拥有安装软件的权限?尽量详解。谢谢!...
- JS If...Else
- Extjs GridPanel用法详解
- 多维数组的本质和指针数组
- Spring扩展之BeanFactoryPostProcessor接口
- 字符串逆序输出的三种方法
- 数据治理标准体系框架
- INVECAS发布全球首创的HDMI(R)2.1,搭载HDCP2.3芯片和IP解决方案,适用于电视、AVR、条形音响和机顶盒
- 世界 6 大航空动力巨头企业
- Yahoo.cn邮箱的IMAP设置方法
- Qt中qrc文件编译后缺少预编译头文件
- 如何利用python给女友制造惊喜(微信每日weather report)
- 初学者如何查阅自然语言处理(NLP)领域学术会议