背景

内核释放页表时尽量批量回收。若批量分配失败(GFP_NOWAIT 拿不到内存),__tlb_remove_table_one() 立即退回单页释放。在未启用 CONFIG_PT_RECLAIM 时,这条回退路径用 tlb_remove_table_sync_one() 发 IPI 广播,等所有 CPU 同步后才释放。

┌────────────────────────────────────┐
│ 释放页表批量分配失败,回退单页释放 │
└─────────────────┬──────────────────┘
                  ▼
┌────────────────────────────────────┐
│     回退用处理器间中断广播同步     │  即使只有一个进程在取消映射,也打扰所有 CPU
└─────────────────┬──────────────────┘
                  ▼
┌────────────────────────────────────┐
│      中断广播伤害实时工作负载      │
└────────────────────────────────────┘

问题是这个广播一刀切:哪怕只有一个进程在 unmap,也得打断所有 CPU。据报告,IPI 广播会伤害实时(RT)工作负载。

问题

  • 批量分配失败回退用 IPI 广播同步
  • 即使只有一个进程在取消映射也打扰所有 CPU
  • 中断广播打断其他 CPU,伤害实时工作负载
  • 回退路径本就是慢路径,可接受睡眠

方案

核心是把那道 IPI 广播换成 RCU 宽限期。

旧:回退发中断广播
┌──────────────────────────────────────┐
│       单页释放发中断给所有 CPU       │
└──────────────────┬───────────────────┘
                   ▼
┌──────────────────────────────────────┐
│ 无锁页表walker靠禁中断,即读侧临界区 │
└──────────────────────────────────────┘

新:回退等读侧宽限期
┌──────────────────────────┐
│   用宽限期替代中断广播   │
└────────────┬─────────────┘
             ▼
┌──────────────────────────┐  慢路径进程上下文可睡眠;旧广播同步留给非慢路径调用
│ 同等保证,不打扰其他 CPU │  者
└──────────────────────────┘

tlb_remove_table_sync_one 与无锁页表walker(如 GUP-fast)同步,后者靠 local_irq_disable 禁中断,而这本身就是一个 RCU 读侧临界区。所以等一个 RCU 宽限期(synchronize_rcu)能提供与 IPI 同等的"没有walker在跑"的保证,却不用打断任何 CPU。这条回退本就是慢路径,且处于进程上下文(unmap_region/exit_mmap,只持 mmap_lock),睡眠等待宽限期完全可接受。

旧的 tlb_remove_table_sync_one 不删,留给其他非慢路径调用者(如 khugepaged 在 pmdp_collapse_flush 后、tlb_finish_mmu 处理 fully_unshared_tables)。那些路径不适合直接睡宽限期,转换它们可能需要别的办法(如定向 IPI)。

收益

作者未提供量化 benchmark。从机制与回归动机推断的预期收益:

  • 页表释放回退不再发 IPI 广播,不打扰其他 CPU
  • 实时工作负载不再被回退路径的广播打断
  • RCU 宽限期提供与 IPI 同等的同步保证,正确性不受影响
  • 旧广播同步保留给真正不适合睡眠的非慢路径调用者