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)));
  • 初始化管理结构:vCPU 链表、GPA/HVA 红黑树、memslot (memslot = GPA→HVA 映射描述单元)哈希、有效/已映射页位图等;
  • 根据 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模式的详细内容,可以参考

    https://gw-c.nowcoder.com/api/sparta/jump/link?link=https%3A%2F%2Fwww.nowcoder.com%2Fdiscuss%2F798609443775578112%3FsourceSSR%3Dusers

    简言: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 文章被收录于专栏

    qemu+kernel

    全部评论

    相关推荐

    11-03 18:50
    门头沟学院 Java
    迷茫的大四🐶:问就是马上到,一周五天,6个月以上,全国可飞
    点赞 评论 收藏
    分享
    评论
    点赞
    收藏
    分享

    创作者周榜

    更多
    牛客网
    牛客网在线编程
    牛客网题解
    牛客企业服务