pageset_update()是 PCP(Per-CPU Pages)缓存配置的底层写入函数,负责在运行时安全地更新某个 CPU 的 PCP 参数(batch、high_min、high_max)。
核心实现
static void pageset_update(struct per_cpu_pages *pcp, unsigned long high_min, unsigned long high_max, unsigned long batch) { WRITE_ONCE(pcp->batch, batch); WRITE_ONCE(pcp->high_min, high_min); WRITE_ONCE(pcp->high_max, high_max); }逐行解析
1. 三个字段的含义
| 字段 | 含义 |
|---|---|
pcp->batch | 批量补充/归还的页数 |
pcp->high_min | 高水位下限(自动调节的下界) |
pcp->high_max | 高水位上限(自动调节的上界) |
2.WRITE_ONCE的作用
防止编译器优化:确保每次写入都真实发生,不被优化掉或重排。
保证原子性:对于对齐的
unsigned long,WRITE_ONCE保证写入是单次、不可分割的,读者不会看到"写了一半"的值。避免数据竞争:并发读者(如
nr_pcp_alloc、nr_pcp_free、nr_pcp_high)用READ_ONCE读取这些字段,WRITE_ONCE与READ_ONCE配对,符合内核的并发访问规范。
3. 为什么不加锁?
这些字段是单个
unsigned long,WRITE_ONCE保证单次写入的原子性。读者读取时即使看到旧值,也只是暂时的——不影响正确性,只影响性能决策(如 batch 用旧值,下次更新就生效)。
加锁会引入不必要的开销,而这些字段的更新是低频的(zone 初始化、CPU 热插拔、sysctl 修改时)。
在 PCP 调节机制中的位置
它被__zone_set_pageset_high_and_batch()调用,后者负责更新 zone 内所有 CPU的 PCP 参数:
static void __zone_set_pageset_high_and_batch(struct zone *zone, unsigned long high_min, unsigned long high_max, unsigned long batch) { struct per_cpu_pages *pcp; int cpu; for_each_possible_cpu(cpu) { pcp = per_cpu_ptr(zone->per_cpu_pageset, cpu); pageset_update(pcp, high_min, high_max, batch); } }调用链:
zone_set_pageset_high_and_batch(zone, cpu_online) ├── new_batch = zone_batchsize(zone) ├── new_high = zone_highsize(zone, new_batch, cpu_online) └── __zone_set_pageset_high_and_batch(zone, new_high, new_high, new_batch) └── for each cpu: pageset_update(pcp, high_min, high_max, batch) // ← 这里
更新时机的考量
pageset_update()在以下场景被调用:
zone 初始化:
zone_pcp_init()/setup_per_cpu_pageset()时首次设置。CPU 热插拔:CPU 上线/下线时,需要重新分配 PCP 份额。
内存热插拔:zone 大小变化时,重新计算
high。sysctl 修改:用户调整
vm.percpu_pagelist_high_fraction时。自动调节:
pcp->high在high_min和high_max之间动态变化,但high_min/high_max/batch本身的更新走这里。
与自动调节机制的配合
2023 年引入的high_min/high_max机制让pcp->high可以在运行时自动增长和衰减:
nr_pcp_alloc()在分配压力大时抬高pcp->high(不超过high_max);decay_pcp_high()在压力小时降低pcp->high(不低于high_min);pageset_update()则更新调节的边界(high_min/high_max),以及batch。
这样,pcp->high的具体值可以频繁变化(由自动调节逻辑处理),而调节范围只在配置变化时才由pageset_update()更新。
总结
pageset_update()是 PCP 配置的原子写入接口:它用WRITE_ONCE安全地更新某个 CPU 的batch、high_min、high_max三个字段,避免数据竞争,同时不加锁以保持轻量。它被__zone_set_pageset_high_and_batch()在 zone 初始化、CPU/内存热插拔、sysctl 修改时调用,是 PCP 自动调节机制中更新"调节边界"的底层函数。