2026/10/4 20:58:57

tlb flush_tlb_kernel_range

tlb flush_tlb_kernel_range flush_tlb_kernel_range是 x86 架构中用于刷新内核地址空间一段范围 TLB 条目的顶层函数。它负责根据刷新范围的大小和硬件能力选择最高效的刷新策略确保内核页表修改对所有 CPU 立即可见。核心决策全量刷新还是范围刷新根据 2026 年 Chuyi Zhou 提交的补丁该函数当前的实现逻辑已被简化为直接基于地址范围做决策不再复用flush_tlb_info结构体void flush_tlb_kernel_range(unsigned long start, unsigned long end) { guard(preempt)(); if (end TLB_FLUSH_ALL || tlb_range_exceeds_ceiling(start, end, PAGE_SHIFT)) kernel_tlb_flush_all(); else kernel_tlb_flush_range(start, end); }决策逻辑全量刷新路径如果请求的是全量刷新end TLB_FLUSH_ALL或者刷新范围超过共享的阈值判断tlb_range_exceeds_ceiling则调用kernel_tlb_flush_all()。后者会根据 CPU 是否支持 INVLPGB 选择硬件广播或 IPI 广播。范围刷新路径对于较小的范围调用kernel_tlb_flush_range(start, end)同样根据 INVLPGB 支持情况选择硬件广播或 IPI 逐页刷新。历史演进从简单到精细这个函数的实现经历了多次优化反映了内核对 TLB 刷新开销的持续关注早期实现简单但粗暴在 2012 年的补丁之前x86 的flush_tlb_kernel_range直接调用flush_tlb_all()即无论范围多小都执行全量刷新。这在内核频繁建立/拆除 vmalloc 映射时造成了显著的性能浪费。2012 年优化引入基于范围大小的决策当范围较小时使用逐页INVLPG刷新只有大范围才退化为全量刷新。这使内核执行该函数时用户程序的内存访问性能提升了 30% 以上。2026 年解耦Chuyi Zhou 的补丁将flush_tlb_kernel_range与flush_tlb_info解耦直接传递start/end参数消除了内核刷新路径对mm状态、TLB 世代和 CPU 固定的不必要依赖并允许在等待 IPI 期间启用抢占改善了调度延迟。架构差异不同架构对flush_tlb_kernel_range的处理方式存在显著差异这源于 TLB 硬件设计和缓存一致性模型的不同x86内核映射修改通常只需刷新单个 CPU如clear_fixmap路径使用flush_tlb_one_kernel因为内核映射是全局的且 x86 的 TLB 一致性由硬件维护。ARM64即使只修改单个内核页的映射也必须广播到所有 CPU。注释指出这是为了防止其他 CPU 因推测执行而残留“垃圾条目”junk entries且该函数可能在中断上下文中被调用如ghes_iounmap_irq因此必须确保不依赖 IPI 完成刷新。RISC-V2023 年的补丁将其从简单的flush_tlb_all()改进为基于范围的刷新但受限于无法得知底层映射的页面大小只能以PAGE_SIZE为步长进行。这个函数是内核内存管理中“变更页表后必须通知 CPU”这一基本原则的具体体现其演进方向始终是在正确性确保所有 CPU 看到最新映射与性能避免不必要的全量刷新和 IPI之间寻找最佳平衡点。