
zone_pcp_reset()是一个内部函数用于在内存热移除memory hot-remove时释放某个 zone 已分配并排空的 per-CPU 页缓存PCP结构并将其重置回boot_pageset。核心作用内存热移除时的 PCP 清理当 zone 被完全移除不再 populated时它之前通过setup_zone_pageset()动态分配的 per-CPU 结构就不再需要了。zone_pcp_reset()负责将这些动态结构归还把 zone 的per_cpu_pageset和per_cpu_zonestats指针重新指回静态的boot_pageset和boot_zonestats。典型实现与关键修复根据内核源码其当前实现大致如下void zone_pcp_reset(struct zone *zone) { int cpu; struct per_cpu_zonestat *pzstats; if (zone-per_cpu_pageset ! boot_pageset) { for_each_online_cpu(cpu) { pzstats per_cpu_ptr(zone-per_cpu_zonestats, cpu); drain_zonestat(zone, pzstats); } free_percpu(zone-per_cpu_pageset); zone-per_cpu_pageset boot_pageset; if (zone-per_cpu_zonestats ! boot_zonestats) { free_percpu(zone-per_cpu_zonestats); zone-per_cpu_zonestats boot_zonestats; } } }逐段解析检查是否为动态分配if (zone-per_cpu_pageset ! boot_pageset)确保只对真正动态分配的结构做释放。如果 zone 从未被 populated 过它可能一直指向boot_pageset。排空剩余的 vmstat 增量在释放per_cpu_zonestats之前遍历所有在线 CPU调用drain_zonestat()将累积的 per-CPU 统计增量刷入全局统计中。如果直接释放这些增量数据就丢失了。释放并重置free_percpu()释放动态分配的 per-CPU 内存然后将指针重置为boot_pageset/boot_zonestats使 zone 回到启动早期的“占位”状态。历史演进与关键修复1. 移除无效的 IRQ 保护2021年早期版本在函数开头使用了local_irq_save()/local_irq_restore()注释声称是为了“避免与drain_pages()的竞态”。但 Mel Gorman 指出这是一个无效的保护local_irq_save只影响当前 CPU如果内存热移除在 CPU A 上运行而drain_pages在 CPU B 上运行禁用 CPU A 的中断对 CPU B 毫无作用。修复方案删除了这对 IRQ 操作转而依赖内存热移除路径的既有顺序保证在调用zone_pcp_reset()之前zone 的 PCP 已经被zone_pcp_disable()排空和禁用并且 zone 已从 zonelist 中移除因此不会有新的分配路径访问到这些结构。2.boot_zonestats的重复释放修复2022年在!SMP !NUMA配置下struct per_cpu_zonestat的大小可能为 0此时zone-per_cpu_zonestats始终等于boot_zonestats。旧代码无条件调用free_percpu(zone-per_cpu_zonestats)会错误地释放静态的boot_zonestats。修复方案增加了if (zone-per_cpu_zonestats ! boot_zonestats)检查。3. 可能的后续重构2021年Mel Gorman 曾提议将zone_pcp_reset()重命名为zone_pcp_destroy()并调整调用顺序让build_all_zonelists()在释放 PCP 结构之前执行从而从机制上彻底消除 use-after-free 的可能。这个提案在社区中引发过讨论但最终是否完全采纳取决于后续的内核版本演进。与相关函数的关系函数职责zone_pcp_disable()热移除前调用排空 PCP将 high/batch 设为 0/1zone_pcp_reset()热移除完成后调用释放动态 PCP 结构重置为 bootzone_pcp_enable()如果 zone 重新上线恢复 PCP 配置zone_pcp_update()zone 大小变化时更新 PCP 配置非完全移除总结zone_pcp_reset()是内存热移除路径的 PCP 清理器当 zone 被完全下线后它排空 per-CPU 统计增量释放动态分配的per_cpu_pageset和per_cpu_zonestats并将指针重置回静态的boot_pageset/boot_zonestats。2021年删除了无效的 IRQ 保护2022年修复了boot_zonestats被重复释放的 bug其正确性依赖于热移除路径中“PCP 已排空且 zone 已从 zonelist 移除”的顺序保证。