☰
page_alloc setup_per_zone_wmarks
2026/10/19 2:10:47 网站建设 项目流程

setup_per_zone_wmarks()是__setup_per_zone_wmarks()的外层封装与串行化函数。它的职责很简单:获取保护水位配置的全局互斥锁,然后调用__setup_per_zone_wmarks()完成实际计算。

核心实现

void setup_per_zone_wmarks(void) { mutex_lock(&pcp_batch_high_lock); __setup_per_zone_wmarks(); mutex_unlock(&pcp_batch_high_lock); }

关键点解析

1. 使用pcp_batch_high_lock保护

pcp_batch_high_lock:

/* prevent >1 _updater_ of zone percpu pageset ->high and ->batch fields */ static DEFINE_MUTEX(pcp_batch_high_lock);

这个锁的注释是针对 PCP 的high/batch字段更新的,但它同样被setup_per_zone_wmarks()复用来保护水位配置。原因是水位和 PCP 的high值紧密相关:

  • zone_highsize()在默认策略下基于low_wmark_pages(zone)计算 PCP 的high_min;

  • 如果水位在计算过程中被并发修改,PCP 的high也会基于不一致的水位计算,导致配置错乱。

所以两者共用同一把锁,确保水位和 PCP 配置的更新是串行且一致的。

2. 为什么需要互斥?

__setup_per_zone_wmarks()会遍历所有 zone,对每个 zone 持zone->lock修改_watermark[]。但遍历本身和全局参数(如min_free_kbytes、watermark_scale_factor)的读取需要保护,避免:

  • 多个更新者(如两个 CPU 同时写 sysctl)并发执行;

  • 一个更新者在修改watermark_scale_factor时,另一个在执行__setup_per_zone_wmarks()读到半更新的值。

pcp_batch_high_lock作为全局互斥锁,保证同一时间只有一个更新者。

调用场景

它被以下路径调用:

调用者场景
init_per_zone_wmark_min()启动初始化
min_free_kbytes_sysctl_handler()用户修改vm.min_free_kbytes
watermark_scale_factor_sysctl_handler()用户修改vm.watermark_scale_factor
watermark_boost_factor_sysctl_handler()用户修改vm.watermark_boost_factor
内存热插拔zone 大小变化后
CPU 热插拔在线 CPU 数变化影响水位计算时

与__setup_per_zone_wmarks()的分工

函数职责
setup_per_zone_wmarks()加锁/解锁,串行化
__setup_per_zone_wmarks()实际计算和设置各 zone 的_watermark[]

这种“外层加锁 + 内层执行”的模式在内核中很常见,便于在已持锁的上下文中直接调用内层函数(避免重复加锁)。

与calculate_totalreserve_pages()的关系

__setup_per_zone_wmarks()末尾会调用calculate_totalreserve_pages(),因为水位变化会影响totalreserve_pages的值。这个调用在持锁状态下完成,确保汇总值的一致性。

总结

setup_per_zone_wmarks()是水位配置的串行化入口:它获取pcp_batch_high_lock,调用__setup_per_zone_wmarks()完成各 zone 水位的计算和设置,然后释放锁。使用同一把锁保护水位和 PCP 配置,是因为两者在默认策略下相互依赖——水位决定了 PCP 的high_min。它被启动初始化、sysctl 修改、内存/CPU 热插拔等路径调用,确保水位更新在并发环境下的一致性和串行化。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询