ARTICLE DETAIL

资讯详情

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

tlb should_trim_cpumask

tlb should_trim_cpumask should_trim_cpumask是 x86 架构 TLB 刷新机制中一个用于控制mm_cpumask清理频率的节流函数。它通过“每秒最多清理一次”的策略在减少不必要 IPI 开销的同时避免过度清理导致的 TLB 刷新遗漏。核心作用节流式清理这个函数在 TLB 刷新路径中被调用用来决定是否应该对目标mm的mm_cpumask进行清理。mm_cpumask记录了哪些 CPU 正在使用该地址空间清理的目的是移除那些已经不再运行该进程的 CPU从而减少后续 TLB 刷新时的 IPI 目标数量。实现逻辑static bool should_trim_cpumask(struct mm_struct *mm) { if (time_after(jiffies, READ_ONCE(mm-context.next_trim_cpumask))) { WRITE_ONCE(mm-context.next_trim_cpumask, jiffies HZ); return true; } return false; }关键点时间检查通过time_after检查当前jiffies是否已经超过了next_trim_cpumask记录的下一次允许清理的时间点。频率控制如果允许清理将next_trim_cpumask更新为jiffies HZ即1 秒后然后返回true。这意味着同一个mm的mm_cpumask最多每秒被清理一次。并发安全由于should_trim_cpumask可能被多个 CPU 同时调用例如多个 CPU 同时触发 TLB 刷新代码使用了READ_ONCE和WRITE_ONCE来确保对next_trim_cpumask的访问是原子的避免数据竞争。在 TLB 刷新中的使用should_trim_cpumask的返回值被存入struct flush_tlb_info的trim_cpumask字段if (cpumask_any_but(mm_cpumask(mm), cpu) nr_cpu_ids) { info-trim_cpumask should_trim_cpumask(mm); }在后续的 TLB 刷新执行路径如flush_tlb_func中如果trim_cpumask为true就会从mm_cpumask中移除当前 CPU因为它已经不再运行该进程。这样下一次 TLB 刷新就不会再向这个 CPU 发送 IPI。设计背景与权衡这个函数的引入是为了解决一个性能与正确性的平衡问题背景2024 年的补丁将mm_cpumask的更新改为“惰性”的——不再在每次上下文切换时原子地清除 CPU 位而是等到第一次向该 CPU 发送 TLB 刷新、发现它已经不再运行该进程时才将其清除。这减少了上下文切换时的原子操作开销但会导致mm_cpumask中积累越来越多的“僵尸”CPU 位使得后续 TLB 刷新向不必要的 CPU 发送 IPI。should_trim_cpumask的作用作为“惰性清理”的补充它以每秒一次的频率主动清理mm_cpumask移除那些已经不再运行该进程的 CPU。这样既避免了每次切换时的原子开销又防止了 IPI 目标集合的无限膨胀。已知问题与修复这个函数本身也曾引发一个严重 bug。由于清理过于“积极”它会在switch_mm_irqs_off的一个关键窗口期内错误地跳过 TLB 刷新——此时新 CR3 已经加载但loaded_mm尚未更新should_flush_tlb会误认为目标 CPU 不需要刷新。修复方案是在should_flush_tlb中增加对LOADED_MM_SWITCHING状态的检查确保这个窗口期内的 CPU 仍然会收到 IPI。
返回列表