__drain_all_pages()是 PCP 排空机制的核心调度器,负责协调所有 CPU 排空各自的 PCP 缓存。它是drain_all_pages()的内部实现,通过force_all_cpus参数提供更强的排空保证。
核心作用
它的主要职责是遍历所有在线 CPU,将那些 PCP 缓存非空的 CPU 选出来,然后通过工作队列(workqueue)在这些 CPU 上执行排空操作,最后等待所有排空完成。
参数解析
| 参数 | 含义 |
|---|---|
zone | 目标 zone。若为NULL,则排空所有 populated zone |
force_all_cpus | 是否强制在所有 CPU 上执行排空,跳过“非空检查” |
核心流程
1. 安全检查与互斥
if (WARN_ON_ONCE(!mm_percpu_wq)) return; if (unlikely(!mutex_trylock(&pcpu_drain_mutex))) { if (!zone) return; mutex_lock(&pcpu_drain_mutex); }确保
mm_percpu_wq已初始化;尝试获取
pcpu_drain_mutex。如果获取失败且zone为NULL,直接返回(不等待);如果指定了特定zone(如 CMA、内存热插拔场景),则阻塞等待,因为这些调用者需要排空必须完成。
2. 筛选需要排空的 CPU
for_each_online_cpu(cpu) { if (force_all_cpus) { has_pcps = true; } else if (zone) { pcp = per_cpu_ptr(zone->pageset, cpu); if (pcp->pcp.count) has_pcps = true; } else { for_each_populated_zone(z) { pcp = per_cpu_ptr(z->pageset, cpu); if (pcp->pcp.count) { has_pcps = true; break; } } } ... }force_all_cpus为真时,跳过非空检查,将所有在线 CPU 都加入排空列表。否则,只选择那些 PCP 缓存非空的 CPU。
3. 投递并等待排空工作
for_each_cpu(cpu, &cpus_with_pcps) { struct pcpu_drain *drain = per_cpu_ptr(&pcpu_drain, cpu); drain->zone = zone; INIT_WORK(&drain->work, drain_local_pages_wq); queue_work_on(cpu, mm_percpu_wq, &drain->work); } for_each_cpu(cpu, &cpus_with_pcps) flush_work(&per_cpu_ptr(&pcpu_drain, cpu)->work);在每个选中 CPU 上排队一个
drain_local_pages_wq工作项;然后等待所有工作项完成(
flush_work),确保排空真正完成后再返回。
force_all_cpus的用途与竞态问题
注释解释了该参数存在的原因:
drain_all_pages()is optimized to only execute on cpus where pcplists are not empty. The check for non-emptiness can however race with a free to pcplist that has not yet increased thepcp->countfrom 0 to 1. Callers that need the guarantee that every CPU has drained can disable the optimizing racy check.
竞态场景:一个 CPU 的 PCP 缓存看起来是空的(pcp->count == 0),但另一个 CPU 正好在释放一个页到该 PCP 列表中,且尚未增加count。此时非空检查会漏掉这个 CPU,导致排空后仍有页残留在 PCP 中。
force_all_cpus = true让调用者强制排空所有 CPU,跳过这个有竞态的优化检查。根据补丁讨论,这个参数不会导出到page_alloc.c之外,仅内部使用。
与drain_all_pages()的关系
void drain_all_pages(struct zone *zone) { __drain_all_pages(zone, false); }drain_all_pages()是对外接口,固定force_all_cpus = false,使用优化后的非空检查。__drain_all_pages()则保留force_all_cpus参数供内部需要强保证的调用者使用。
总结
__drain_all_pages()是 PCP 排空机制的调度中枢:它筛选出需要排空的 CPU(或通过force_all_cpus强制覆盖所有在线 CPU),通过mm_percpu_wq向每个 CPU 投递排空工作,并用flush_work等待全部完成。force_all_cpus参数用于解决“非空检查”与“释放到 PCP 但未增加 count”之间的竞态,为 CMA 分配和内存热插拔等需要强保证的场景提供可靠的排空语义。