kvm_riscv_nacl_enable()的原理

📅 2026/6/24 10:47:19
kvm_riscv_nacl_enable()的原理
下面详细解析 Linux KVM RISC-V 中kvm_riscv_nacl_enable()的原理、源码和在H-ext 仿真中的意义。一、NACL 是什么前置理解NACL SBI Nested Acceleration ExtensionEID0x4E41434C NACL它只在嵌套虚拟化场景有意义当 KVML1 Hypervisor自己作为 Guest 跑在另一个 L0 Hypervisor 上时H-ext CSR 访问 /HFENCE/HLV/HSV原本要 VM-Exit 到 L0 逐条模拟。NACL 通过Per-CPU Shared Memory共享内存页​ 让 L1 KVM 批量写入 H-ext CSR 值和 HFENCE 请求再一次性调 SBI 同步减少 trap 次数。⚠️如果你的仿真器是直接跑 KVM 在裸硬件/H-ext 上非嵌套OpenSBI 通常不实现 NACLkvm_riscv_nacl_available()为 falsekvm_riscv_nacl_enable()直接 return 0无任何副作用。二、核心数据结构arch/riscv/kvm/nacl.c / include/asm/kvm_nacl.h/* 每 CPU 一个 NACL 上下文 */ struct kvm_riscv_nacl { void *shmem; /* 共享内存虚拟地址页 */ phys_addr_t shmem_phys; /* 共享内存物理地址页对齐*/ }; DEFINE_PER_CPU(struct kvm_riscv_nacl, kvm_riscv_nacl); /* 静态键 —— 若 OpenSBI 不支持 NACL 则 jump 走零开销 */ DEFINE_STATIC_KEY_FALSE(kvm_riscv_nacl_available); DEFINE_STATIC_KEY_FALSE(kvm_riscv_nacl_sync_csr_available); DEFINE_STATIC_KEY_FALSE(kvm_riscv_nacl_sync_hfence_available); DEFINE_STATIC_KEY_FALSE(kvm_riscv_nacl_sync_sret_available); DEFINE_STATIC_KEY_FALSE(kvm_riscv_nacl_autoswap_csr_available);共享内存布局SBI 规范Offset 0x0000 — 0x0FFF : Scratch SpaceHFENCE 队列 / SRET 参数 / Autoswap Offset 0x1000 — end : CSR Space1024 × XLEN索引 ((csr0xc00)2)|(csr0xff)三、kvm_riscv_nacl_enable() 源码解析来自arch/riscv/kvm/nacl.cint kvm_riscv_nacl_enable(void) { struct kvm_riscv_nacl *nacl; /* 若 OpenSBI 未声明支持 NACL → 直接返回不影响非嵌套场景 */ if (!kvm_riscv_nacl_available()) return 0; nacl this_cpu_ptr(kvm_riscv_nacl); /* * SBI 调用SBI_EXT_NACL, SBI_EXT_NACL_SET_SHMEM * 参数shmem_phys 本 CPU NACL 共享内存物理地址 * flags 0保留 * 作用告诉 OpenSBI(L0)此 CPU 的 NACL shmem 现在是这张物理页 * OpenSBI 此后可从 shmem CSR Space 读取 H-ext CSR 批量值 * 并在 SYNC_CSR / SYNC_HFENCE / SYNC_SRET 时应用 */ struct sbiret ret sbi_ecall(SBI_EXT_NACL, SBI_EXT_NACL_SET_SHMEM, nacl-shmem_phys, 0, 0, 0, 0, 0); return sbi_err_map_linux_errno(ret.error); }对应disablevoid kvm_riscv_nacl_disable(void) { if (!kvm_riscv_nacl_available()) return; sbi_ecall(SBI_EXT_NACL, SBI_EXT_NACL_SET_SHMEM, SBI_SHMEM_DISABLE, SBI_SHMEM_DISABLE, 0, 0, 0, 0); }四、初始化时机kvm_init → 之前看的 main.cstatic int __init riscv_kvm_init(void) { ... rc kvm_riscv_nacl_init(); // 分配 per-CPU shmem 页探测 SBI NACL if (rc rc ! -ENODEV) // -ENODEV OpenSBI 不支持 NACL忽略 return rc; ... } int kvm_arch_enable_virtualization_cpu(void) { rc kvm_riscv_nacl_enable(); // 每 CPU online 时绑定 shmem 到当前 CPU if (rc) return rc; csr_write(CSR_HEDELEG, ...); // 之后才是 HEDELEG/HIDELEG/HCOUNTEREN ... }kvm_riscv_nacl_init()分配页 sbi_probe_extension(SBI_EXT_NACL)SBI_EXT_NACL_PROBE_FEATURE→ 设 static keykvm_riscv_nacl_enable()在每 CPU 上调SET_SHMEM激活本 CPU 的 NACL 通道五、NACL enable 后对 vcpu_run 的影响原理启用后KVM vcpu world-switch 不再逐条csr_write(CSR_HGATP, ...)/csr_write(CSR_HSTATUS, ...)改为写 H-ext CSR 到 NACL shmem CSR Space标记 dirty bitmap调SBI_EXT_NACL_SYNC_CSR(-1UL)→ L0 批量应用 HGATP/HSTATUS/HEDELEG…若有 HFENCE → 填 HFENCE queue →SBI_EXT_NACL_SYNC_HFENCE若有SBI_NACL_FEAT_SYNC_SRET→ 填 GPR SRET 参数 →SBI_EXT_NACL_SYNC_SRET原子完成CSR apply HFENCE SRET效果嵌套虚拟化时 L1 KVM world-switch 的 VM-Exit 数大幅下降。六、对 H-ext 仿真器的影响场景NACL 影响裸机跑 KVM lkvm 场景OpenSBI 无 NACL​kvm_riscv_nacl_available()falsekvm_riscv_nacl_enable()直接 return 0完全不触 SBI无影响​OpenSBI 假声明支持 NACL 但仿真器不实现SBI_EXT_NACL_SET_SHMEMsbi_ecall返回SBI_ERR_NOT_SUPPORTED→ KVM 模块加载失败或 cpuhp 报错 →kvm.ko 加载中止​未来做嵌套虚拟化KVM-on-KVM需仿真器实现 SBI NACL分配 shmem、解析 SYNC_CSR/SYNC_HFENCE/SYNC_SRET七、一句话总结kvm_riscv_nacl_enable()是 KVM 对每个 CPU 调 SBI NACLSET_SHMEM把预分配的 Per-CPU 共享内存页注册给 OpenSBI用于嵌套虚拟化时批量同步 H-ext CSR / HFENCE / SRET减少 VM-Exit。非嵌套场景裸 H-ext 跑 KVMOpenSBI 不实现 NACLkvm_riscv_nacl_available()为 false该函数直接 return 0对功能无任何影响。若仿真器错误暴露 NACL 却不实现会导致 KVM 加载失败。如果你想确认你环境是否走了 NACL 分支搜 dmesgdmesg | grep -i nested acceleration有输出 NACL 激活需仿真器支持无输出 正常非嵌套可忽略 NACL 完全。