6.18 虚拟化(10)selftests(2)kvm_create_max_vcpus
1.代码
https://elixir.bootlin.com/linux/v6.18/source/tools/testing/selftests/kvm/kvm_create_max_vcpus.c#L37
2. main逐行解析
2.1 kvm_check_cap
2.1.1open_kvm_dev_path_or_exit
打开一个vm,获取fd, 成功就成功,不成功一般会出两个判断:是否为root或者kvm是否enabled
2.1.2 _kvm_ioctl
https://elixir.bootlin.com/linux/v6.18/source/tools/testing/selftests/kvm/lib/kvm_util.c#L154
利用ioctl来获取一些capacity常量
kvm的ioctl本质会调用到这里,它自己的file_operations, 再加上ioctl的类型 KVM_CHECK_EXTENSION, 搭配上传进来的cap的具体参数
https://elixir.bootlin.com/linux/v6.18/source/virt/kvm/kvm_main.c#L5560
然后就是分两批创建vcpu
why:系统允许 vCPU ID 空间更大(可以跳号或分配在高位),但每个 VM最多只能创建 max_vcpus 个 vCPU
3.test_vcpu_creation逐行解析
3.1 vm_create_barebones
____vm_create
本质:
struct kvm_vm 容器(calloc(1, sizeof(*vm)));vm_shape(模式、PA/VA 位宽、页大小)计算页表层级、最大 GFN 等元数据;vm_open(vm) 调用 KVM_CREATE_VM 在内核中“打开”一个 VM 对象(拿到 VM fd)。3.2 __vm_vcpu_add
开始添加vcpu, 直到此时,我才意识到kvm的ioctl有多少种
(1)之前用到的ioctl, 其中包含vm创建,以及查看API版本,capacity这些,原来创建完vm之后,后续创建vcpu和mem的时候,就不再需要这个ioctl, 然后我们引入第二个版本的ioctl
(2) 目测我们看到的就有
https://elixir.bootlin.com/linux/v6.18/source/virt/kvm/kvm_main.c#L5147
而__vm_vcpu_add的本质是会通过kvm_vm_ioctl调用KVM_CREATE_VCPU的
https://elixir.bootlin.com/linux/v6.18/source/tools/testing/selftests/kvm/lib/kvm_util.c#L1330
(3)kvm_vm_ioctl_create_vcpu的本质
https://elixir.bootlin.com/linux/v6.18/source/virt/kvm/kvm_main.c#L4158
1)先检验vcpu ID合法性以及VM最大vCPU数限制
2)vcpu/kvm_run/dirty_ring(host关于VM脏页的处理手段)/xa数组(XA 数组想象成 “带索引的智能书架”:每个 vCPU 是一本书,vcpu_idx 是书架的层号,要找某本 vCPU 时,不用逐行翻,直接按层号(索引)就能定位)插入内存空间分配,本节不过分纠结这两个概念,主要是vcpu的创建
3)kvm_arch_vcpu_precreate/init/create
precreate: 确保create vpu之前,中断并未初始化,vgic需要提前规划,动态加载新的vcpu会导致gic中断路由混乱/检查vcpu id合法性
init: 初始化通用状态 锁/关联VM/初始化读写锁并置空/等等
create:
vcpu->arch.mmu_page_cache.gfp_zero = __GFP_ZERO; 为vcpu分配页,置零
kvm_timer_vcpu_init:虚拟化 ARM 通用定时器,Guest 时钟 / 延时依赖
kvm_vgic_vcpu_init:为 vCPU 初始化虚拟中断控制器,Guest 中断全靠它
vcpu->arch.hw_mmu = &vcpu->kvm->arch.mmu; 关联vCPU到VM的MMU(硬件内存管理单元),Guest地址翻译依赖
kvm_share_hyp:我们在此处加了打印,确保了当前host内核处于EL2且为VHE模式
所以会在540行直接return0, 不会继续后续执行。关于VHE模式的详细内容,可以参考
简言:VHE: 令Linux kernel 运行在EL2(Hypervisor模式), 减少EL1->EL2切换开销, 是一种armv8的硬件特性
4)
create_vcpu_fd() 把 内核里的 vCPU 对象 封装成一个 匿名 inode 的 fd,并挂上 kvm_vcpu_fops,让用户态通过这个 fd 完成 设置寄存器、映射 struct kvm_run、执行 KVM_RUN 等操作。它是把 vCPU 暴露为“文件式接口”的关键一步,也是 KVM_CREATE_VCPU 能返回 vcpu_fd 的根本原因。
到此为止,kvm_create_max_vcpus结束,此时只是创建vcpu,我们并没有使用vcpu, 并没有令vm,vcpu跑起来
#Linux##嵌入式##笔试##嵌入式转岗的难度怎么样#
qemu+kernel
