__get_current_cr3_fast是一个快速获取当前 CR3 值的辅助函数。它的核心目的是在不执行缓慢的read_cr3()指令的情况下,通过软件状态(cpu_tlbstate)直接“拼装”出当前的 CR3 值。
核心用途与限制
这个函数主要服务于KVM(内核虚拟机)这类会“偷偷”修改 CR3、之后又需要恢复它的代码。
它的使用有非常严格的限制:
必须在原子上下文(
in_atomic())中调用,且不能处于 NMI(不可屏蔽中断)中。在
CONFIG_DEBUG_VM下,它会用VM_BUG_ON断言拼装出的值与真实read_cr3()一致,用于调试。
实现逻辑与演进
基础实现(较新版本):
unsigned long __get_current_cr3_fast(void) { unsigned long cr3 = __pa(this_cpu_read(cpu_tlbstate.loaded_mm)->pgd); VM_WARN_ON(in_nmi() || !in_atomic()); VM_BUG_ON(cr3 != read_cr3()); return cr3; }它直接读取当前 CPU 已加载的mm的pgd物理地址,而不包含 PCID 信息。
更完整的实现(2020 年及以后):
unsigned long __get_current_cr3_fast(void) { unsigned long cr3 = build_cr3(this_cpu_read(cpu_tlbstate.loaded_mm)->pgd, this_cpu_read(cpu_tlbstate.loaded_mm_asid), tlbstate_lam_cr3_mask()); VM_WARN_ON(in_nmi() || preemptible()); VM_BUG_ON(cr3 != __read_cr3()); return cr3; }这里通过build_cr3()将页表基址、ASID(进程上下文标识符)和 LAM(线性地址掩码)完整地拼装出来,以匹配硬件 CR3 的真实值。
演进原因:2020 年,Thomas Gleixner 将__get_current_cr3_fast从内联头文件移到了arch/x86/mm/tlb.c中,并改为EXPORT_SYMBOL_GPL。这是为了避免将敏感的cpu_tlbstate直接暴露给内核模块,让模块通过这个受控的接口来获取 CR3。
与 ASI(地址空间隔离)的交互
在支持ASI(Address Space Isolation)的补丁集中,该函数需要感知当前是否处于隔离会话中。如果 ASI 处于活动状态,它不能直接读取cpu_tlbstate(因为 ASI 页表可能未映射该结构),而是从 ASI 会话缓存中获取 CR3 值。
这个函数是 KVM 等底层代码在无法使用慢速read_cr3时,获取当前 CR3 值的“快捷通道”。