kvm 创建虚拟机核心分析

功能概述

kvm_vm_ioctl_create_vcpu是KVM模块中用于创建虚拟CPU(vCPU)的核心函数,主要功能包括:

  • 参数校验与并发控制
  • vCPU架构相关初始化
  • 调试信息注册
  • 用户态接口(文件描述符)创建
  • 资源管理与错误回滚

** 1.0kvm_vm_ioctl_create_vcpu 代码逐层解析**

** 1.1. 参数校验与资源限制检查**
if (id >= KVM_MAX_VCPU_ID)
    return -EINVAL;

mutex_lock(&kvm->lock);
if (kvm->created_vcpus == KVM_MAX_VCPUS) {
    mutex_unlock(&kvm->lock);
    return -EINVAL;
}
kvm->created_vcpus++;
mutex_unlock(&kvm->lock);
  • 校验逻辑:
    • 检查id是否超出KVM_MAX_VCPU_ID(避免非法ID)。
    • 通过互斥锁kvm->lock保护临界区,确保并发安全。
    • 检查当前已创建的vCPU数量是否达到上限KVM_MAX_VCPUS,若超出则返回-EINVAL
  • 资源管理:
    • 若校验通过,递增kvm->created_vcpus计数器,标记一个新的vCPU被创建。

** 1.2. 架构相关vCPU初始化**
vcpu = kvm_arch_vcpu_create(kvm, id);
if (IS_ERR(vcpu)) {
    r = PTR_ERR(vcpu);
    goto vcpu_decrement;
}
  • 关键函数: kvm_arch_vcpu_create
    • 由具体硬件架构(如x86、ARM)实现,负责分配并初始化struct kvm_vcpu结构体。
    • 若初始化失败(如内存不足),返回错误码,跳转到vcpu_decrement回滚已递增的计数器。

** 1.3. 抢占通知器与调试信息**
preempt_notifier_init(&vcpu->preempt_notifier, &kvm_preempt_ops);
r = kvm_arch_vcpu_setup(vcpu);
if (r)
    goto vcpu_destroy;
kvm_create_vcpu_debugfs(vcpu);
  • 抢占通知器:
    • 初始化preempt_notifier,用于在宿主CPU发生抢占时通知KVM模块,确保vCPU状态正确保存/恢复。
  • 架构相关配置:
    • 调用kvm_arch_vcpu_setup完成vCPU的进一步设置(如寄存器初始化、中断控制器绑定)。
  • 调试支持:
    • 在debugfs中创建vCPU的调试信息节点,供开发者查看内部状态。

** 1.4. vCPU唯一性检查与索引分配**
mutex_lock(&kvm->lock);
if (kvm_get_vcpu_by_id(kvm, id)) {
    r = -EEXIST;
    goto unlock_vcpu_destroy;
}
vcpu->vcpu_idx = atomic_read(&kvm->online_vcpus);
BUG_ON(kvm->vcpus[vcpu->vcpu_idx]);
  • 唯一性校验:
    • 再次检查是否存在相同id的vCPU,避免重复创建(返回-EEXIST)。
  • 索引分配:
    • 通过原子操作atomic_read获取当前在线vCPU数量,作为新vCPU的索引vcpu_idx
    • 断言检查索引位置未被占用(BUG_ON确保调试环境发现问题)。

** 1.5. 用户态接口与资源提交**
kvm_get_kvm(kvm); // 增加KVM模块的引用计数
r = create_vcpu_fd(vcpu); // 创建用户态可访问的文件描述符
if (r < 0) {
    kvm_put_kvm(kvm);
    goto unlock_vcpu_destroy;
}
kvm->vcpus[vcpu->vcpu_idx] = vcpu; // 将vCPU加入KVM的vcpus数组

// 内存屏障确保写入顺序
smp_wmb();
atomic_inc(&kvm->online_vcpus); // 在线vCPU计数加1
  • 用户态接口:
    • 调用create_vcpu_fd生成文件描述符,用户态通过此fd操作vCPU(如启动、配置)。
  • 引用计数管理:
    • kvm_get_kvm增加KVM对象的引用计数,防止模块提前卸载。
  • 并发安全:
    • 使用内存屏障smp_wmb()确保kvm->vcpus数组写入先于online_vcpus计数更新,避免其他线程读到不一致状态。

** 1.6. 后初始化与错误处理**
mutex_unlock(&kvm->lock);
kvm_arch_vcpu_postcreate(vcpu); // 调用之前分析的函数完成后续初始化
return r;

// 错误处理路径
unlock_vcpu_destroy:
    mutex_unlock(&kvm->lock);
    debugfs_remove_recursive(vcpu->debugfs_dentry);
vcpu_destroy:
    kvm_arch_vcpu_destroy(vcpu);
vcpu_decrement:
    mutex_lock(&kvm->lock);
    kvm->created_vcpus--;
    mutex_unlock(&kvm->lock);
  • 后初始化:
    • 调用kvm_arch_vcpu_postcreate(如初始化TSC、Hyper-V特性),完成架构相关的最终设置。
  • 错误回滚:
    • 若任一阶段失败,依次释放资源:
      1. 删除调试文件节点。
      2. 调用架构销毁函数释放vCPU内存。
      3. 递减已创建vCPU计数器。

关键结构体与机制

结构体/机制说明
struct kvm表示一个KVM虚拟机实例,管理所有vCPU和资源。
atomic_t online_vcpus原子计数器,记录当前在线的vCPU数量,确保并发安全。
preempt_notifier抢占通知器,用于在宿主调度vCPU时同步状态。
smp_wmb()写内存屏障,确保多核环境下内存写入顺序一致性。

小结

此函数是KVM创建vCPU的核心逻辑,主要完成以下任务:

  1. 参数校验与资源限制检查
  2. 架构相关vCPU初始化
  3. 调试与用户态接口暴露
  4. 并发控制与状态提交
  5. 错误回滚与资源释放

其设计重点在于:

  • 并发安全:通过互斥锁和原子操作避免竞争。
  • 模块化:架构相关操作通过kvm_arch_*函数分离。
  • 用户态交互:通过文件描述符提供操作接口。
  • 健壮性:完善的错误处理链确保资源泄漏最小化。

此函数是虚拟机创建vCPU的“最后一环”,与kvm_arch_vcpu_postcreate共同确保vCPU从内核到硬件的完整初始化。

2.0 kvm_arch_vcpu_setup

int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
{
	vcpu->arch.arch_capabilities = kvm_get_arch_capabilities();
	vcpu->arch.msr_platform_info = MSR_PLATFORM_INFO_CPUID_FAULT;
	kvm_vcpu_mtrr_init(vcpu);
	vcpu_load(vcpu);
	kvm_vcpu_reset(vcpu, false);
	kvm_init_mmu(vcpu, false);
	vcpu_put(vcpu);
	return 0;
}
kvm_x86_ops->vcpu_load(vcpu, cpu);

static void svm_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
{
	struct vcpu_svm *svm = to_svm(vcpu);
	struct svm_cpu_data *sd = per_cpu(svm_data, cpu);
	int i;

	if (unlikely(cpu != vcpu->cpu)) {
		svm->asid_generation = 0;
		mark_all_dirty(svm->vmcb);
	}

#ifdef CONFIG_X86_64
	rdmsrl(MSR_GS_BASE, to_svm(vcpu)->host.gs_base);
#endif
	savesegment(fs, svm->host.fs);
	savesegment(gs, svm->host.gs);
	svm->host.ldt = kvm_read_ldt();

	for (i = 0; i < NR_HOST_SAVE_USER_MSRS; i++)
		rdmsrl(host_save_user_msrs[i], svm->host_user_msrs[i]);

	if (static_cpu_has(X86_FEATURE_TSCRATEMSR)) {
		u64 tsc_ratio = vcpu->arch.tsc_scaling_ratio;
		if (tsc_ratio != __this_cpu_read(current_tsc_ratio)) {
			__this_cpu_write(current_tsc_ratio, tsc_ratio);
			wrmsrl(MSR_AMD64_TSC_RATIO, tsc_ratio);
		}
	}
	/* This assumes that the kernel never uses MSR_TSC_AUX */
	if (static_cpu_has(X86_FEATURE_RDTSCP))
		wrmsrl(MSR_TSC_AUX, svm->tsc_aux);

	if (sd->current_vmcb != svm->vmcb) {
		sd->current_vmcb = svm->vmcb;
		indirect_branch_prediction_barrier();
	}
	avic_vcpu_load(vcpu, cpu);
}

2.1 函数作用

在创建或重置 vCPU 时,完成以下核心任务:

  1. 初始化架构能力(如 CPU 特性标志)
  2. 配置关键 MSR(模型特定寄存器)
  3. 设置内存管理单元(MMU)
  4. 重置 vCPU 状态

2.2 关键步骤解析

  1. 设置架构能力标志

    vcpu->arch.arch_capabilities = kvm_get_arch_capabilities();
    
    • kvm_get_arch_capabilities():从宿主 CPU 读取硬件支持的能力(如 Intel 的 ARCH_CAPABILITIES MSR),包括安全特性(如 RDCL_NO 缓解 Meltdown)、性能优化标志等。
    • 这些标志会通过 CPUID 指令暴露给虚拟机,供 Guest OS 使用。
  2. 配置平台信息 MSR

    vcpu->arch.msr_platform_info = MSR_PLATFORM_INFO_CPUID_FAULT;
    
    • MSR_PLATFORM_INFO_CPUID_FAULT:启用 CPUID Faulting 特性。
      • 当 Guest 用户态程序执行 CPUID 指令时,触发 #UD 异常(而非直接返回宿主信息)。
      • 由 KVM 截获异常并模拟响应,增强对 CPU 特性的控制力(例如隐藏某些特性)。
  3. 初始化 MTRR(内存类型范围寄存器)

    kvm_vcpu_mtrr_init(vcpu);
    
    • MTRR 用于定义物理内存区域的缓存策略(如 Write-Back、Uncacheable 等)。
    • 虚拟化时需要为 vCPU 初始化虚拟 MTRR,确保 Guest 访问内存时符合预期缓存行为。
  4. 加载 vCPU 上下文

    vcpu_load(vcpu);
    
    • 将 vCPU 绑定到当前物理 CPU,加载其寄存器状态和上下文到宿主硬件(如 VMCS 结构)。
    • 确保后续操作(如重置、MMU 初始化)在正确的上下文中执行。
  5. 重置 vCPU 状态

    kvm_vcpu_reset(vcpu, false);
    
    • 参数 false:表示不强制冷启动(保留部分状态,如调试寄存器)。
    • 核心操作:
      • 将寄存器(CR0、CR4 等)设为默认值(如实模式状态)。
      • 清除中断标志、重置 APIC(高级可编程中断控制器)。
      • 重置 TSC(时间戳计数器)偏移。
  6. 初始化 MMU(内存管理单元)

    kvm_init_mmu(vcpu, false);
    
    • 参数 false:表示不跳过初始化页表
    • 核心操作:
      • 初始化 分页模式(如未启用分页、32 位分页或 64 位长模式)。
      • 配置 EPT(扩展页表)NPT(嵌套页表) 的根页表(对 Intel VT-x/AMD-V 硬件虚拟化扩展的支持)。
      • 设置 影子页表(若无硬件虚拟化支持)。
  7. 卸载 vCPU 上下文

    vcpu_put(vcpu);
    
    • 解除 vCPU 与当前物理 CPU 的绑定,保存上下文状态。
    • 平衡之前的 vcpu_load,确保宿主 CPU 状态恢复。
  8. 返回值

    return 0; // 固定返回成功
    
    • 此函数设计为始终成功(错误处理在子函数内部完成)。

2.3 技术背景

  • KVM(Kernel-based Virtual Machine):Linux 内核的虚拟化模块,通过 /dev/kvm 接口供用户态程序(如 QEMU)创建和管理虚拟机。
  • vCPU 生命周期kvm_arch_vcpu_setup 通常在 vCPU 首次创建热重置时调用,是虚拟机启动的关键路径。
  • MMU 虚拟化:通过硬件辅助(EPT/NPT)或软件模拟(影子页表)实现 Guest 物理地址到宿主物理地址的转换。

2.4 总结

此函数为 vCPU 提供架构相关的初始化,确保:

  1. Guest 能正确感知硬件能力(通过 CPUID/MSR)。
  2. 内存管理(MTRR/MMU)符合虚拟化需求。
  3. vCPU 状态处于可运行的标准初始状态。
    这是虚拟机正常启动和运行的基础设施保障。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值