ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

tlb invlpgb_kernel_range_flush

tlb invlpgb_kernel_range_flush invlpgb_kernel_range_flush是 AMD 广播 TLB 失效INVLPGB补丁集中用于刷新内核地址空间一段范围 TLB 条目的专用函数。它通过硬件广播指令替代传统的 IPI 风暴显著降低了内核 TLB 刷新的开销。核心作用内核范围的硬件广播刷新当内核需要修改自己的页表映射如vmalloc区域的建立或拆除时需要使所有 CPU 上对应的 TLB 条目失效。传统方式是通过on_each_cpu向所有 CPU 发送 IPI让每个 CPU 执行flush_tlb_one_kernel。invlpgb_kernel_range_flush利用 AMD EPYC 3 及更新 CPU 上的INVLPGB 指令从当前 CPU 直接向全系统广播失效请求无需发送任何 IPI。核心实现逻辑根据补丁其典型实现如下static void invlpgb_kernel_range_flush(struct flush_tlb_info *info) { unsigned long addr, nr; for (addr info-start; addr info-end; addr nr PAGE_SHIFT) { nr (info-end - addr) PAGE_SHIFT; /* * INVLPGB has a limit on the size of ranges it can * flush. Break up large flushes. */ nr clamp_val(nr, 1, invlpgb_count_max); invlpgb_flush_addr_nosync(addr, nr); } __tlbsync(); }关键步骤循环切分范围INVLPGB 单次操作能刷新的页面数量有硬件上限存储在invlpgb_count_max中通过 CPUID 读取。因此函数需要将大范围切分成多个不超过上限的批次。clamp_val限幅确保每次刷新的页数nr在[1, invlpgb_count_max]之间。invlpgb_count_max在早期启动阶段会被初始化为1以防止在硬件值尚未读取时发生无限循环 。invlpgb_flush_addr_nosync调用底层 INVLPGB 封装指定地址和页数。nosync后缀表示不等待完成——INVLPGB 是异步指令刷新请求发出后立即返回 。__tlbsync()在循环结束后调用TLBSYNC 指令等待本 CPU 发出的所有 INVLPGB 请求真正完成。这是必须的同步点——在 TLB 刷新实际生效之前内核不能释放相关的物理页 。调用关系flush_tlb_kernel_range(start, end) └── kernel_tlb_flush_range(info) └── invlpgb_kernel_range_flush(info) // ← 你问的函数 ├── invlpgb_flush_addr_nosync() // 多次异步 └── __tlbsync() // 最后同步等待flush_tlb_kernel_range是上层入口。如果 CPU 支持X86_FEATURE_INVLPGB就直接走invlpgb_kernel_range_flush否则回退到传统的on_each_cpu(do_kernel_range_flush, ...)。设计要点ASID 有效位必须设置INVLPGB 的 ASID 有效位INVLPGB_FLAG_ASID需要始终置位。在宿主机上硬件会使用 EDX[15:0] 中指定的 ASID 值应为 0在客户机中硬件会自动替换为客户机的实际 ASID。如果不设置这个位可能会意外刷新客户机的 TLB 条目 。cant_migrate()的必要性__tlbsync()内部调用了cant_migrate()因为TLBSYNC 只等待本 CPU 发出的 INVLPGB 完成。如果任务在 INVLPGB 发出后、TLBSYNC 执行前被迁移到其他 CPUTLBSYNC 就无法等待到之前的刷新请求导致同步失效 。与broadcast_tlb_flush的区别broadcast_tlb_flush用于用户地址空间的刷新基于全局 ASID而invlpgb_kernel_range_flush用于内核地址空间的刷新。内核映射是全局的不属于任何特定 ASID因此 INVLPGB 使用INVLPGB_FLAG_VA | INVLPGB_FLAG_INCLUDE_GLOBAL模式来刷新所有地址空间中的内核映射 。
返回列表