PCP(Per-CPU Pages)缓存的禁用与启用函数,用于在内存热插拔等需要“冻结”PCP 缓存的场景中,临时关闭某个 zone 的 PCP 机制。一、zone_pcp_disable()—— 禁用 zone 的 PCP 缓存void zone_pcp_disable(struct zone *zone) { mutex_lock(&pcp_batch_high_lock); __zone_set_pageset_high_and_batch(zone, 0, 0, 1); __drain_all_pages(zone, true); }逐步解析1. 获取全局互斥锁mutex_lock(&pcp_batch_high_lock);前面讲过的pcp_batch_high_lock:保护 PCP 的high/batch字段更新。获取它确保在禁用期间,没有其他更新者并发修改 PCP 配置。注意:这把锁在zone_pcp_enable()中才释放,形成disable/enable 的配对临界区。2. 把 PCP 配置设为“禁用”状态__zone_set_pageset_high_and_batch(zone, 0, 0, 1);前面讲过的__zone_set_pageset_high_and_batch():遍历所有 CPU,更新 PCP 的high_min、high_max、batch。参数(0, 0, 1)的含义:high_min = 0、high_max = 0:高水位为 0,意味着 PCP 缓存不能保留任何页——一旦有页放入,立即触发批量归还。batch = 1:批量大小为 1,每次只处理一页。效果:PCP 缓存形同虚设,所有分配/释放几乎直接走伙伴系统。3. 强制排空所有 CPU 的 PCP 缓存__drain_all_pages(zone, true);前面讲过的__drain_all_pages(zone, force_all_cpus):排空指定 zone 在所有 CPU 上的 PCP 缓存。force_all_cpus = true:强制在所有 CPU 上执行排空,跳过“非空检查”的竞态优化。这是为了确保彻底清空——因为内存热插拔要求该 zone 的 PCP 缓存中不能有任何页残留,否则隔离/下线会失败。为什么需要force_all_cpus = true?前面讲__drain_all_pages()时提到过:普通的非空检查存在竞态——一个 CPU 的 PCP 看起来是空的,但另一个 CPU 正好在释放页到该 PCP 且尚未增加count。内存热插拔对“彻底清空”有强要求,所以必须强制排空所有 CPU。二、zone_pcp_enable()—— 重新启用 zone 的 PCP 缓存void zone_pcp_enable(struct zone *zone) { __zone_set_pageset_high_and_batch(zone, zone->pageset_high_min, zone->pageset_high_max, zone->pageset_batch); mutex_unlock(&pcp_batch_high_lock); }逐步解析1. 恢复 zone 的正常 PCP 配置__zone_set_pageset_high_and_batch(zone, zone->pageset_high_min, zone->pageset_high_max, zone->pageset_batch);从 zone 的pageset_*字段中取回禁用前的正常配置:zone->pageset_high_min/pageset_high_max:该 zone 的高水位范围;zone->pageset_batch:批量大小。这些值是在zone_set_pageset_high_and_batch()中计算并保存的。通过__zone_set_pageset_high_and_batch()重新分发到所有 CPU 的 PCP 结构。2. 释放全局互斥锁mutex_unlock(&pcp_batch_high_lock);释放pcp_batch_high_lock,结束 disable/enable 的临界区。之后其他更新者可以正常修改 PCP 配置。三、disable/enable 的配对使用这两个函数必须成对调用:zone_pcp_disable(zone); // 加锁,禁用 PCP,排空 /* ... 执行需要 PCP 无残留的操作,如内存隔离、下线 ... */ zone_pcp_enable(zone); // 恢复 PCP,解锁典型使用场景1. 内存热插拔下线offline_pages()在隔离 pageblock 之前,需要确保 PCP 缓存中没有该 zone 的页,否则这些页可能“躲过”隔离。所以先zone_pcp_disable(),下线完成后再zone_pcp_enable()。2. CMA 分配alloc_contig_range()在需要大块连续内存时,可能通过zone_pcp_disable()确保 PCP 缓存不干扰隔离。3. 内存规整某些极端场景下,规整需要 PCP 缓存无残留。四、与前面内容的呼应前面出现过的这里的关联pcp_batch_high_lockdisable 时获取,enable 时释放__zone_set_pageset_high_and_batch()两函数都调用它更新 PCP 配置__drain_all_pages(zone, true)disable 时强制排空zone->pageset_high_min/pageset_high_max/pageset_batchenable 时恢复这些值page_alloc_cpu_online()/page_alloc_cpu_dead()CPU 热插拔也涉及 PCP 配置内存热插拔offline_pages()典型调用场景五、小结表函数操作锁zone_pcp_disable(zone)设置 PCP 为high=0, batch=1(禁用)+ 强制排空所有 CPU获取pcp_batch_high_lockzone_pcp_enable(zone)恢复 zone 的正常high_min/high_max/batch释放pcp_batch_high_lock一句话总结zone_pcp_disable()和zone_pcp_enable()是 PCP 缓存的禁用/启用配对函数:disable 时获取pcp_batch_high_lock,把 zone 的 PCP 配置设为high=0, batch=1使其形同虚设,并强制排空所有 CPU 的 PCP 缓存以确保无页残留;enable 时从 zone 保存的pageset_*字段恢复正常配置并释放锁。它们用于内存热插拔、CMA 分配等需要 PCP 缓存彻底清空的场景,确保隔离和迁移操作不受 PCP 中滞留页的干扰。
PCP(Per-CPU Pages)缓存的禁用与启用函数,用于在内存热插拔等需要“冻结”PCP 缓存的场景中,临时关闭某个 zone 的 PCP 机制。一、zone_pcp_disable()—— 禁用 zone 的 PCP 缓存void zone_pcp_disable(struct zone *zone) { mutex_lock(&pcp_batch_high_lock); __zone_set_pageset_high_and_batch(zone, 0, 0, 1); __drain_all_pages(zone, true); }逐步解析1. 获取全局互斥锁mutex_lock(&pcp_batch_high_lock);前面讲过的pcp_batch_high_lock:保护 PCP 的high/batch字段更新。获取它确保在禁用期间,没有其他更新者并发修改 PCP 配置。注意:这把锁在zone_pcp_enable()中才释放,形成disable/enable 的配对临界区。2. 把 PCP 配置设为“禁用”状态__zone_set_pageset_high_and_batch(zone, 0, 0, 1);前面讲过的__zone_set_pageset_high_and_batch():遍历所有 CPU,更新 PCP 的high_min、high_max、batch。参数(0, 0, 1)的含义:high_min = 0、high_max = 0:高水位为 0,意味着 PCP 缓存不能保留任何页——一旦有页放入,立即触发批量归还。batch = 1:批量大小为 1,每次只处理一页。效果:PCP 缓存形同虚设,所有分配/释放几乎直接走伙伴系统。3. 强制排空所有 CPU 的 PCP 缓存__drain_all_pages(zone, true);前面讲过的__drain_all_pages(zone, force_all_cpus):排空指定 zone 在所有 CPU 上的 PCP 缓存。force_all_cpus = true:强制在所有 CPU 上执行排空,跳过“非空检查”的竞态优化。这是为了确保彻底清空——因为内存热插拔要求该 zone 的 PCP 缓存中不能有任何页残留,否则隔离/下线会失败。为什么需要force_all_cpus = true?前面讲__drain_all_pages()时提到过:普通的非空检查存在竞态——一个 CPU 的 PCP 看起来是空的,但另一个 CPU 正好在释放页到该 PCP 且尚未增加count。内存热插拔对“彻底清空”有强要求,所以必须强制排空所有 CPU。二、zone_pcp_enable()—— 重新启用 zone 的 PCP 缓存void zone_pcp_enable(struct zone *zone) { __zone_set_pageset_high_and_batch(zone, zone->pageset_high_min, zone->pageset_high_max, zone->pageset_batch); mutex_unlock(&pcp_batch_high_lock); }逐步解析1. 恢复 zone 的正常 PCP 配置__zone_set_pageset_high_and_batch(zone, zone->pageset_high_min, zone->pageset_high_max, zone->pageset_batch);从 zone 的pageset_*字段中取回禁用前的正常配置:zone->pageset_high_min/pageset_high_max:该 zone 的高水位范围;zone->pageset_batch:批量大小。这些值是在zone_set_pageset_high_and_batch()中计算并保存的。通过__zone_set_pageset_high_and_batch()重新分发到所有 CPU 的 PCP 结构。2. 释放全局互斥锁mutex_unlock(&pcp_batch_high_lock);释放pcp_batch_high_lock,结束 disable/enable 的临界区。之后其他更新者可以正常修改 PCP 配置。三、disable/enable 的配对使用这两个函数必须成对调用:zone_pcp_disable(zone); // 加锁,禁用 PCP,排空 /* ... 执行需要 PCP 无残留的操作,如内存隔离、下线 ... */ zone_pcp_enable(zone); // 恢复 PCP,解锁典型使用场景1. 内存热插拔下线offline_pages()在隔离 pageblock 之前,需要确保 PCP 缓存中没有该 zone 的页,否则这些页可能“躲过”隔离。所以先zone_pcp_disable(),下线完成后再zone_pcp_enable()。2. CMA 分配alloc_contig_range()在需要大块连续内存时,可能通过zone_pcp_disable()确保 PCP 缓存不干扰隔离。3. 内存规整某些极端场景下,规整需要 PCP 缓存无残留。四、与前面内容的呼应前面出现过的这里的关联pcp_batch_high_lockdisable 时获取,enable 时释放__zone_set_pageset_high_and_batch()两函数都调用它更新 PCP 配置__drain_all_pages(zone, true)disable 时强制排空zone->pageset_high_min/pageset_high_max/pageset_batchenable 时恢复这些值page_alloc_cpu_online()/page_alloc_cpu_dead()CPU 热插拔也涉及 PCP 配置内存热插拔offline_pages()典型调用场景五、小结表函数操作锁zone_pcp_disable(zone)设置 PCP 为high=0, batch=1(禁用)+ 强制排空所有 CPU获取pcp_batch_high_lockzone_pcp_enable(zone)恢复 zone 的正常high_min/high_max/batch释放pcp_batch_high_lock一句话总结zone_pcp_disable()和zone_pcp_enable()是 PCP 缓存的禁用/启用配对函数:disable 时获取pcp_batch_high_lock,把 zone 的 PCP 配置设为high=0, batch=1使其形同虚设,并强制排空所有 CPU 的 PCP 缓存以确保无页残留;enable 时从 zone 保存的pageset_*字段恢复正常配置并释放锁。它们用于内存热插拔、CMA 分配等需要 PCP 缓存彻底清空的场景,确保隔离和迁移操作不受 PCP 中滞留页的干扰。