[Virtualization](六):RISC-V KVM 的 trap/exit 处理路径

📅 2026/8/1 10:49:52
[Virtualization](六):RISC-V KVM 的 trap/exit 处理路径
第五篇讨论了 memory slot、hgatp和 stage-2 page table。本篇继续沿着 vCPU 运行路径往下看当 Guest Linux 离开虚拟 CPU 执行现场时Linux KVM/RISC-V 如何判断它该回到 Guest、留在 Kernel还是返回 QEMU1. 为什么 exit handling 是虚拟化核心路径虚拟化并不是只要把 guest 放到 CPU 上运行就结束了。Guest 运行期间会不断遇到需要控制权切换的事件缺页MMIOSBI call中断timerdebugshutdown非法指令这些事件会让 guest trap 到 host也就是常说的 exit。exit handling 的质量决定了两件事语义是否正确性能是否可接受如果一个高频事件总是返回 QEMU虚拟机性能会很差。如果一个必须由 QEMU 设备模型处理的事件被 KVM 错误吞掉设备语义又会不正确。2. 一个最小 exit 模型可以先把 exit 分成三种归宿。Guest exit | -- handled by Guest itself through delegation | -- trapped to KVM and handled in kernel | -- returned to QEMU userspace第一种其实不一定算 KVM exit因为事件被委派给 VS-mode guestGuest Linux 自己处理。第二种进入 KVM但不返回 QEMU。第三种进入 KVM 后还要通过struct kvm_run把原因交给 QEMU。理解这三层是读 RISC-V KVM exit 代码的关键。3. RISC-V trap 信息从哪里来RISC-V trap 通常会带着几类信息causetrap valueexception PCinterrupt or exception 类型guest/host 相关状态在虚拟化场景里KVM 需要判断trap 来自 guest 还是 host是 exception 还是 interrupt是否和 stage-2 translation 有关是否需要注入回 guest是否需要返回 userspace可以粗略理解为hardware trap metadata | v KVM decodes exit reason | -- memory fault -- MMIO -- SBI -- interrupt/timer -- debug -- fatal/error真正源码会更细但主线就是从架构 trap 信息翻译成 KVM 的处理分支。4. delegation能交给 Guest 的就交给 GuestRISC-V H-extension 提供hedeleg和hideleg。它们控制哪些 exception 和 interrupt 可以被委派到 VS-mode guest。目标是减少不必要的 host 介入。例如 guest 用户进程触发普通 page fault通常应该由 Guest Linux 自己处理。Guest userspace page fault | | delegated v Guest Linux page fault handler如果每次 guest 用户态缺页都要陷入 Host KVM性能会非常糟糕语义上也没有必要。所以第一条原则是属于 guest OS 正常职责的 trap应尽量留在 guest 内部处理。5. stage-2 faultKVM 内核态处理的典型路径stage-2 fault 属于 host 控制边界。Guest Linux 无法自己修复 stage-2 page table因为这张表由 Host/KVM 管理。典型路径Guest accesses GPA | | stage-2 fault v KVM trap handler | | find memory slot | install mapping or classify as MMIO v resume guest or return to QEMU如果 fault 对应 guest RAMKVM 可以在内核态处理后直接恢复 guest。如果 fault 对应 MMIOKVM 可能要返回 QEMU。所以 stage-2 fault 是一个分流点。6. MMIO exit设备模型边界当 guest 访问 QEMU 负责模拟的 MMIO 设备时KVM 需要返回用户态。例如 virtio-mmio、UART、某些平台设备。路径如下Guest MMIO access | v KVM detects userspace MMIO | | fill kvm_run.mmio v Return from KVM_RUN | v QEMU dispatches MemoryRegion operation | v KVM_RUN again这条路径的代价比纯内核态处理更高因为它跨越了 kernel/userspace 边界。因此高频 I/O 通常会引入 virtio、vhost、批处理和中断优化避免每次操作都走慢路径。7. SBI exit介于架构和平台之间SBI call 是 RISC-V guest 访问底层平台服务的接口。Guest Linux 可能通过 SBI 请求设置 timer发送 IPIremote fencesystem resethart state managementdebug consoleKVM 收到 SBI trap 后要根据 extension 和 function id 分流。Guest SBI call | v KVM SBI dispatcher | -- emulate in KVM -- forward/lower layer -- return to QEMU for selected cases -- inject error to guestSBI 的特点是它既不是普通 MMIO也不是纯 CPU fault而是 RISC-V 软件栈约定出来的固件接口。所以它非常适合作为观察 KVM/RISC-V 架构边界的入口。8. timer exit 与 virtual interruptGuest timer 是虚拟机运行中非常高频、非常关键的机制。Guest Linux 需要 timer 来做scheduler tickhrtimertimeoutsleep/wakeuptime accounting在 RISC-V guest 中timer 相关操作可能通过 SBI 或架构 timer 能力进入 KVM。KVM 需要把 guest 的 timer 请求转换成 host 侧的 timer 事件并在到期时向 guest 注入 virtual timer interrupt。简化模型Guest sets timer | v KVM records guest timer deadline | v Host timer fires | v KVM marks virtual timer interrupt pending | v Guest receives timer interrupttimer exit 的优化非常重要因为它直接影响 guest 调度延迟和吞吐。9. debug exit调试场景下guest 可能因为断点、单步或调试事件退出到 host。这类 exit 通常不在性能热路径上但对内核开发非常重要。例如你用 QEMU gdbstub 或 KVM 相关调试能力观察 guest 时debug exit 能让 host 或 QEMU 接管控制权。简化路径Guest hits breakpoint | v KVM debug exit | v QEMU/debugger handles event在写虚拟化调试文章时debug exit 是一个可以单独展开的主题。10. external interrupt 与 interrupt window外部中断到达时KVM 需要决定如何影响 guest。可能情况包括host 自己要处理中断中断对应某个虚拟设备中断需要注入给 guestguest 当前是否允许接收中断vCPU 是否正在运行这会牵涉虚拟中断 pending state、guest interrupt enable、interrupt controller 虚拟化等内容。可以先记住一句话中断不是简单地“来了就给 guest”而是要通过 host/KVM 的虚拟中断状态机投递。后续中断专题会深入 PLIC、AIA、IMSIC 和 virtual interrupt injection。11. exit reason 如何返回 QEMU当 KVM 决定某个 exit 必须由 QEMU 处理时会把信息写入共享的struct kvm_run。QEMU 从KVM_RUN返回后读取exit reasonMMIO 地址、长度、方向、数据debug 信息shutdown 状态architecture-specific 信息然后 QEMU 根据 exit reason 分发到对应处理函数。KVM fills struct kvm_run | v ioctl(KVM_RUN) returns | v QEMU reads exit_reason | v QEMU handles and calls KVM_RUN againstruct kvm_run是 QEMU/KVM 运行循环里非常关键的共享 ABI。12. resume guest处理完后如何继续exit 处理的最后一步通常是恢复 guest。KVM 需要保证guest PC 是否要前进guest register 是否要写返回值exception 是否要注入interrupt pending state 是否更新MMIO read 数据是否准备好SBI 返回值是否符合 ABITLB 或页表状态是否一致如果这些状态处理错guest 可能重复执行同一条指令、拿到错误返回值或者直接崩溃。所以 exit handling 的难点不只是分类还包括恢复语义。13. 性能视角exit 越少越好吗一般来说减少 exit 是优化方向但不是唯一目标。更准确地说不必要的 exit 应该减少高频 exit 应该尽量留在内核态或硬件路径必须返回 QEMU 的 exit 应该减少次数或批量化保持语义正确比盲目减少 exit 更重要例如 MMIO 设备模型必须由 QEMU 处理时返回 QEMU 是正确边界。但高吞吐块设备和网络设备通常会通过 virtio/vhost 降低频繁 exit 的成本。14. 源码阅读入口RISC-V KVM exit 路径可以从这些文件入手arch/riscv/kvm/vcpu.carch/riscv/kvm/vcpu_exit.carch/riscv/kvm/vcpu_sbi.carch/riscv/kvm/vcpu_timer.carch/riscv/kvm/mmu.carch/riscv/include/asm/kvm_host.hvirt/kvm/kvm_main.c阅读时可以围绕几个问题trap 信息在哪里被读取exit reason 如何分类stage-2 fault 的处理入口在哪里SBI call 如何 dispatch哪些路径返回 userspacestruct kvm_run在哪里被填充guest PC 和返回值在哪里更新15. 本篇小结这一篇把 RISC-V KVM 的 trap/exit 处理分成三层。第一能委派给 Guest Linux 的 trap尽量由 guest 自己处理。第二需要 host 介入但不需要设备模型的事件尽量由 KVM 在内核态处理。第三需要 QEMU 设备模型或用户态逻辑的事件通过struct kvm_run返回 QEMU。这条分界线决定了 QEMU/KVM 的系统结构也决定了很多性能优化的方向。可以把本篇压缩成一句话RISC-V KVM 的 exit handling 本质上是在判断一个 guest 事件的归属它属于 Guest、属于 Kernel还是属于 QEMU。16. 下一篇预告RISC-V 虚拟化中的中断下一篇进入中断路径重点看 timer、IPI、外部中断和虚拟中断注入。会讨论RISC-V 中断模型基础PLIC、CLINT、AIA、IMSIC 的角色guest interrupt pending statevirtual timer interruptIPI 如何虚拟化KVM 如何向 guest 注入中断中断路径对 vCPU 调度和性能的影响