功能概述
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
回滚已递增的计数器。
- 由具体硬件架构(如x86、ARM)实现,负责分配并初始化
** 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特性),完成架构相关的最终设置。
- 调用
- 错误回滚:
- 若任一阶段失败,依次释放资源:
- 删除调试文件节点。
- 调用架构销毁函数释放vCPU内存。
- 递减已创建vCPU计数器。
- 若任一阶段失败,依次释放资源:
关键结构体与机制
结构体/机制 | 说明 |
---|---|
struct kvm | 表示一个KVM虚拟机实例,管理所有vCPU和资源。 |
atomic_t online_vcpus | 原子计数器,记录当前在线的vCPU数量,确保并发安全。 |
preempt_notifier | 抢占通知器,用于在宿主调度vCPU时同步状态。 |
smp_wmb() | 写内存屏障,确保多核环境下内存写入顺序一致性。 |
小结
此函数是KVM创建vCPU的核心逻辑,主要完成以下任务:
- 参数校验与资源限制检查
- 架构相关vCPU初始化
- 调试与用户态接口暴露
- 并发控制与状态提交
- 错误回滚与资源释放
其设计重点在于:
- 并发安全:通过互斥锁和原子操作避免竞争。
- 模块化:架构相关操作通过
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 时,完成以下核心任务:
- 初始化架构能力(如 CPU 特性标志)
- 配置关键 MSR(模型特定寄存器)
- 设置内存管理单元(MMU)
- 重置 vCPU 状态
2.2 关键步骤解析
-
设置架构能力标志
vcpu->arch.arch_capabilities = kvm_get_arch_capabilities();
kvm_get_arch_capabilities()
:从宿主 CPU 读取硬件支持的能力(如 Intel 的ARCH_CAPABILITIES
MSR),包括安全特性(如RDCL_NO
缓解 Meltdown)、性能优化标志等。- 这些标志会通过 CPUID 指令暴露给虚拟机,供 Guest OS 使用。
-
配置平台信息 MSR
vcpu->arch.msr_platform_info = MSR_PLATFORM_INFO_CPUID_FAULT;
MSR_PLATFORM_INFO_CPUID_FAULT
:启用 CPUID Faulting 特性。- 当 Guest 用户态程序执行
CPUID
指令时,触发 #UD 异常(而非直接返回宿主信息)。 - 由 KVM 截获异常并模拟响应,增强对 CPU 特性的控制力(例如隐藏某些特性)。
- 当 Guest 用户态程序执行
-
初始化 MTRR(内存类型范围寄存器)
kvm_vcpu_mtrr_init(vcpu);
- MTRR 用于定义物理内存区域的缓存策略(如 Write-Back、Uncacheable 等)。
- 虚拟化时需要为 vCPU 初始化虚拟 MTRR,确保 Guest 访问内存时符合预期缓存行为。
-
加载 vCPU 上下文
vcpu_load(vcpu);
- 将 vCPU 绑定到当前物理 CPU,加载其寄存器状态和上下文到宿主硬件(如
VMCS
结构)。 - 确保后续操作(如重置、MMU 初始化)在正确的上下文中执行。
- 将 vCPU 绑定到当前物理 CPU,加载其寄存器状态和上下文到宿主硬件(如
-
重置 vCPU 状态
kvm_vcpu_reset(vcpu, false);
- 参数
false
:表示不强制冷启动(保留部分状态,如调试寄存器)。 - 核心操作:
- 将寄存器(CR0、CR4 等)设为默认值(如实模式状态)。
- 清除中断标志、重置 APIC(高级可编程中断控制器)。
- 重置 TSC(时间戳计数器)偏移。
- 参数
-
初始化 MMU(内存管理单元)
kvm_init_mmu(vcpu, false);
- 参数
false
:表示不跳过初始化页表。 - 核心操作:
- 初始化 分页模式(如未启用分页、32 位分页或 64 位长模式)。
- 配置 EPT(扩展页表) 或 NPT(嵌套页表) 的根页表(对 Intel VT-x/AMD-V 硬件虚拟化扩展的支持)。
- 设置 影子页表(若无硬件虚拟化支持)。
- 参数
-
卸载 vCPU 上下文
vcpu_put(vcpu);
- 解除 vCPU 与当前物理 CPU 的绑定,保存上下文状态。
- 平衡之前的
vcpu_load
,确保宿主 CPU 状态恢复。
-
返回值
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 提供架构相关的初始化,确保:
- Guest 能正确感知硬件能力(通过 CPUID/MSR)。
- 内存管理(MTRR/MMU)符合虚拟化需求。
- vCPU 状态处于可运行的标准初始状态。
这是虚拟机正常启动和运行的基础设施保障。