ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

QEMU/KVM 半虚拟化特性(kvm-pv)完整指南:CPU 标志、默认行为与配置实战

QEMU/KVM 半虚拟化特性(kvm-pv)完整指南:CPU 标志、默认行为与配置实战 QEMU/KVM 半虚拟化特性kvm-pv完整指南CPU 标志、默认行为与配置实战【免费下载链接】qemuOfficial QEMU mirror. Please see https://www.qemu.org/contribute/ for how to submit changes to QEMU. Pull Requests are disabled. Please only use release tarballs from the QEMU website.项目地址: https://gitcode.com/gh_mirrors/qe/qemu导读当某些硬件接口用软件模拟效率过低时KVM 会提供一套专用的半虚拟化接口来消除这些开销。本文基于 QEMU 官方文档 docs/system/i386/kvm-pv.rst结合源码逐项剖析 KVM 半虚拟化特性kvmclock、kvm-asyncpf、kvm-steal-time、kvm-pv-eoi 等的含义、默认开启规则与 Linux 内核版本支持情况并给出可直接复用的-cpu命令行配置方案帮助你按需开启或裁剪这些特性。KVM 半虚拟化特性是什么在 x86 虚拟化场景中许多硬件接口时钟、中断、内存缺页处理等如果完全靠虚拟化软件模拟实现会产生大量 VM-Exit/VM-Entry 开销性能显著下降。为此KVM 在保留标准硬件虚拟化能力的同时实现了一套半虚拟化paravirtualized接口guest 内核与 hypervisor 通过约定的 CPUID 特性位达成协议绕过慢速的模拟路径直接使用高效通道。在 QEMU 中这些半虚拟化特性全部以CPU 标志CPU flags的形式暴露并集中编码在 KVM 的专用 CPUID 叶子KVM_CPUID_FEATURES中。从 target/i386/cpu.c 的FEAT_KVM特性字定义可以看到完整映射[FEAT_KVM] { .type CPUID_FEATURE_WORD, .feat_names { kvmclock, kvm-nopiodelay, kvm-mmu, kvmclock, kvm-asyncpf, kvm-steal-time, kvm-pv-eoi, kvm-pv-unhalt, NULL, kvm-pv-tlb-flush, kvm-asyncpf-vmexit, kvm-pv-ipi, kvm-poll-control, kvm-pv-sched-yield, kvm-asyncpf-int, kvm-msi-ext-dest-id, ... kvmclock-stable-bit, ... }, .cpuid { .eax KVM_CPUID_FEATURES, .reg R_EAX, }, },注意feat_names数组的下标就是 CPUID 叶子内的位号例如 bit 4 是kvm-asyncpf、bit 5 是kvm-steal-time、bit 6 是kvm-pv-eoi、bit 9 是kvm-pv-tlb-flush、bit 11 是kvm-pv-ipi而 bit 24 是kvmclock-stable-bit。这也解释了为什么kvmclock在数组中出现了两次——它对应 KVM 的两个 clocksource 特性位KVM_FEATURE_CLOCKSOURCE与KVM_FEATURE_CLOCKSOURCE2。默认开启的 KVM 半虚拟化特性官方文档明确当 KVM 加速启用时以下特性对任何 CPU 模型默认开启CPU 标志作用概述kvmclock向 guest 暴露 KVM 专用的半虚拟化时钟源kvm-nopiodelayguest 在 PIO 操作上无需做延迟等待kvm-asyncpf异步缺页机制Linux v5.10 起已被弃用kvm-steal-timeguest vCPU 未运行被抢占时间的记账kvm-pv-eoi半虚拟化中断结束EOI信号kvmclock-stable-bit告知 guest 可见 TSC 可完全信任kvmclock 计算无回绕这一默认行为在源码中有直接证据。target/i386/kvm/kvm-cpu.c 中的kvm_default_props表定义了在 KVM 加速下自动施加到内置 CPU 模型上的属性static PropValue kvm_default_props[] { { kvmclock, on }, { kvm-nopiodelay, on }, { kvm-asyncpf, on }, { kvm-steal-time, on }, { kvm-pv-eoi, on }, { kvmclock-stable-bit, on }, { x2apic, on }, { kvm-msi-ext-dest-id, off }, { acpi, off }, { monitor, off }, { svm, off }, { NULL, NULL }, };这段代码还有两个值得注意的细节kvm-msi-ext-dest-id默认是off只有当 irqchip 处于 split 模式时才被动态改写为on见下文特殊情况。该表只在内置 CPU 模型builtin_x86_defs即qemu64、qemu32、Opteron_G*等初始化时生效由x86_cpu_instance_init通过x86_cpu_apply_props应用同时源码注释强调默认开启的特性必须保证在 KVM 加速器所支持的最老内核版本上就已存在。特殊情况x2apic split irqchip 下的kvm-msi-ext-dest-idkvm-msi-ext-dest-id的默认开启存在前提条件。官方文档给出的条件是在 x2apic 模式 split irqchip 时默认开启例如-machine ...,kernel-irqchipsplit -cpu ...,x2apic。源码印证了这一逻辑。target/i386/kvm/kvm-cpu.c 在实例初始化时根据 irqchip 配置动态调整默认值if (!kvm_irqchip_in_kernel()) { x86_cpu_change_kvm_default(x2apic, off); } else if (kvm_irqchip_is_split()) { x86_cpu_change_kvm_default(kvm-msi-ext-dest-id, on); }也就是说kernel-irqchipoff时连x2apic都会被强制关闭而kernel-irqchipsplit中断控制器在用户态、其他部分在内核态时kvm-msi-ext-dest-id才会被打开。逐项详解KVM 半虚拟化特性全表除默认开启项外QEMU 还支持若干需要显式开启或视场景而定的特性。下表汇总全部特性及其 Linux 内核支持版本以下内核版本信息来自官方文档供配置时参考CPU 标志内核支持版本说明kvmclockLinux v2.6.26 起暴露 KVM 专用半虚拟化时钟源guest 用它代替 TSC/HPET 等模拟时钟kvm-nopiodelayLinux v2.6.26 起guest 无需在 PIO 操作上插入延迟省去不必要的忙等待kvm-mmu已废弃该特性已被废弃deprecated不应再使用kvm-asyncpfLinux v2.6.38 起异步缺页机制。注意Linux v5.10 起已被弃用且不再由 KVM 启用请改用kvm-asyncpf-intkvm-steal-timeLinux v3.1 起当 guest vCPU 未运行时被调度出去的 stolen time 记账供 guest 准确统计 CPU 占用kvm-pv-eoiLinux v3.10 起半虚拟化 EOI 信号加速中断完成确认减少 VM-Exitkvm-pv-unhaltLinux v3.12 起半虚拟化自旋锁支持避免自旋锁等待导致的频繁 HLT/唤醒kvm-pv-tlb-flushLinux v4.16 起半虚拟化 TLB 刷写机制批量刷新远端 vCPU 的 TLBkvm-pv-ipiLinux v4.19 起半虚拟化 IPI处理器间中断机制kvm-poll-controlLinux v5.10 起允许 guest 控制 host 侧 HLT 时的轮询行为kvm-pv-sched-yieldLinux v5.10 起半虚拟化调度让出sched yield特性kvm-asyncpf-intLinux v5.10 起基于中断的异步缺页机制kvm-asyncpf的继任者kvm-msi-ext-dest-idLinux v5.10 起外部中断支持扩展目标 ID无需 IRQ remapping 即可支持最多 32768 个 CPU实际 vCPU 数可能因其他限制更少kvmclock-stable-bitLinux v2.6.35 起告知 guestguest 可见的 TSC 值可完全信任用于 kvmclock 计算不会发生回绕warp各特性底层原理速览kvmclock / kvmclock-stable-bitguest 通过 MSR 读取 KVM 提供的时钟信息与 hypervisor 共享时间源避免每个时间读取都触发 VM-Exitkvmclock-stable-bit进一步承诺 TSC 无回绕使 guest 可以放心用 TSC 做精确实时计算如gettimeofday的快速路径。kvm-steal-timeKVM 在 host 调度周期内记录 guest vCPU 被偷走的运行时间guest 据此修正自身 CPU 使用率统计是容器/云环境计量与负载均衡的重要数据来源。kvm-pv-eoi传统 EOI 写入 APIC 会触发 VM-Exit半虚拟化 EOI 让 guest 通过 hypercall/专用页直接完成 EOI显著减少中断密集场景下的开销。kvm-pv-tlb-flush / kvm-pv-ipi这两项针对多 vCPU 场景用少量 hypercall 替代大量跨 vCPU 的注入操作降低 TLB 一致性维护与 IPI 投递成本。kvm-asyncpf / kvm-asyncpf-intguest 在缺页时不必同步等待 host 从磁盘/远端换入页面而是先调度其他任务页面就绪后再继续——前者以事件/同步方式通知后者改为中断驱动减少 guest 侧忙等。补充特性kvm-pv-enforce-cpuid除上述作为 CPUID 特性位暴露的特性外文档还介绍了一个补充特性supplementary featurekvm-pv-enforce-cpuid将支持的半虚拟化特性集限制为仅 guest 可见 CPUID 中暴露出的那些。默认情况下即使某些半虚拟化特性未在 guest 可见的 CPUID 中宣告KVM 也允许 guest 使用当前支持的全部半虚拟化特性。Linux v5.10 起支持。这本质上是一个安全/可迁移性开关开启后KVM 会通过KVM_CAP_ENFORCE_PV_FEATURE_CPUIDcapability 强制 guest 只能使用其在 CPUID 中看到并认可的特性避免 guest 因特性未宣告却依赖它而在迁移到其他 host 时行为不一致或崩溃。源码中该标志的处理位于 target/i386/kvm/kvm.cif (cpu-kvm_pv_enforce_cpuid) { r kvm_vcpu_enable_cap(cs, KVM_CAP_ENFORCE_PV_FEATURE_CPUID, 0, 1); if (r 0) { fprintf(stderr, failed to enable KVM_CAP_ENFORCE_PV_FEATURE_CPUID: %s, strerror(-r)); abort(); } }对应的 QEMU 属性定义在 target/i386/cpu.cDEFINE_PROP_BOOL(kvm-pv-enforce-cpuid, X86CPU, kvm_pv_enforce_cpuid, false)默认关闭。CPU 模型host特性直通文档特别强调使用 CPU 模型host时QEMU 将把所有受支持的半虚拟化 KVM 特性直通pass through给 guest。这意味着-cpu host下宿主机 KVM 支持的全部 PV 特性位会如实反映到 guest 的 CPUID 中guest 内核可自动选用前提是 guest 内核版本足够新能识别对应特性。这种方式的优势是开箱即用、性能最优代价是特性集合与宿主机强绑定迁移到不同内核/hardware 的 host 时可能遇到 CPUID 不一致问题。从源码看QEMU 通过kvm_arch_get_supported_cpuid()见 target/i386/kvm/kvm.c 起读取 KVM 的GET_SUPPORTED_CPUID结果并在此基础上做若干修正例如kvm_pv_unhalt虽然会被GET_SUPPORTED_CPUID报告但没有 in-kernel irqchip 时不能启用必须屏蔽target/i386/kvm/kvm.cif (!kvm_irqchip_in_kernel()) { ret ~CPUID_KVM_PV_UNHALT; } if (kvm_irqchip_is_split()) { ret | CPUID_KVM_MSI_EXT_DEST_ID; }kvm-msi-ext-dest-id在 split irqchip 下会被强制加上。这解释了为何文档中kvm-pv-unhalt支持自旋锁与 irqchip 配置存在耦合该特性依赖内核态中断控制器的配合。实战命令行配置示例1. 基础默认开启全部默认 PV 特性qemu-system-x86_64 \ -machine accelkvm \ -cpu qemu64 \ -smp 4 \ -m 4096使用内置模型如qemu64时6 个默认 PV 特性kvmclock、kvm-nopiodelay、kvm-asyncpf、kvm-steal-time、kvm-pv-eoi、kvmclock-stable-bit会自动生效。2. host 直通获得完整 PV 特性集qemu-system-x86_64 \ -machine accelkvm \ -cpu host \ -smp 4 \ -m 4096-cpu host会直通宿主机 KVM 支持的所有半虚拟化特性适合单机性能优先、不迁移的场景。3. 按需裁剪关闭特定 PV 特性若 guest 内核较旧如 RHEL/CentOS 老版本或怀疑某个特性引发稳定性问题可显式关闭qemu-system-x86_64 \ -machine accelkvm \ -cpu host,kvm-asyncpfoff,kvm-pv-eoioff \ -smp 4 \ -m 4096kvm-asyncpf在 Linux v5.10 起已被弃用关闭它并使用kvm-asyncpf-int是更现代的做法qemu-system-x86_64 \ -machine accelkvm \ -cpu host,kvm-asyncpfoff,kvm-asyncpf-inton \ -smp 4 \ -m 40964. 启用kvm-msi-ext-dest-idsplit irqchip x2apicqemu-system-x86_64 \ -machine accelkvm,kernel-irqchipsplit \ -cpu qemu64,x2apicon \ -smp 16 \ -m 4096如上文所述此时kvm-msi-ext-dest-id会被自动置为on允许在无 IRQ remapping 的情况下寻址更多 CPU理论上限 32768实际受其他限制约束。5. 启用kvm-pv-enforce-cpuid严格特性一致性qemu-system-x86_64 \ -machine accelkvm \ -cpu host,kvm-pv-enforce-cpuidon \ -smp 4 \ -m 4096适合需要保证guest 只用它看到的特性的场景如跨 host 迁移前的特性集固化。与 irqchip 配置的耦合关系小结配置影响kernel-irqchipoffx2apic默认被关闭kvm-pv-unhalt不可用kernel-irqchipsplitkvm-msi-ext-dest-id默认开启kvm-pv-unhalt可用kernel-irqchipon默认kvm-msi-ext-dest-id默认关闭可手动开启这些耦合关系可直接在 target/i386/kvm/kvm-cpu.c 与 target/i386/kvm/kvm.c 的对应逻辑中找到依据。诊断与验证在 guest 内使用lscpu查看kvm相关标志位确认特性是否暴露lscpu | grep -i kvm查看/proc/cpuinfo中的flags段hypervisor标志表明运行在虚拟化环境中kvm_*相关位由 guest 内核识别后驱动对应半虚拟化路径。host 侧可用kvm_stat观测 VM-Exit 计数对比开启/关闭kvm-pv-eoi、kvm-pv-tlb-flush等特性前后的中断与 TLB 相关 exit 数量。参考与延伸阅读官方文档docs/system/i386/kvm-pv.rst特性位定义target/i386/cpu.cFEAT_KVM特性字KVM 默认属性表target/i386/kvm/kvm-cpu.cCPUID 支持探测与修正target/i386/kvm/kvm.ckvm_arch_get_supported_cpuidkvm-pv-enforce-cpuid实现target/i386/kvm/kvm.c特性别名属性kvm_asyncpf、kvm_steal_time、kvm_pv_eoi等方便脚本按旧命名访问target/i386/cpu.cLinux 内核侧关于这些半虚拟化特性的更深入资料可参考 Linux 内核源码树中的Documentation/virt/kvm目录。【免费下载链接】qemuOfficial QEMU mirror. Please see https://www.qemu.org/contribute/ for how to submit changes to QEMU. Pull Requests are disabled. Please only use release tarballs from the QEMU website.项目地址: https://gitcode.com/gh_mirrors/qe/qemu创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进