FOLL_LONGTERM 的 pin 要先清场
不允许长期 pin 的 folio 先迁走,再重试
check_and_migrate_movable_*()
区间里出现不允许的 folio
如可移动区或 CMA 页
collect_longterm_unpinnable_folios()
把它们收集起来
迁移走,再重新 pin
收集阶段要先把 folio 从 LRU 上摘下来
摘之前要确认 per-CPU cache 没有残留引用
兜底 · 引用不符才 drain
drain 本该只在真出事时触发
folio_isolate_lru()
从 LRU 上摘下来
per-CPU LRU cache 可能还持有引用
引用比对
ref_count == expected + 本次 pin 的引用
不符就说明有残留
不符:先 lru_add_drain() 排空本 CPU
仍不符再 lru_add_drain_all() 排空所有 CPU
drain 的定位是兜底,不是常规路径
问题 · 预期 +1,实际 +1024
FOLL_PIN 的计数规则有两条路径
large folio
有独立的 _pincount 字段
pin 一次记 1
small folio
没有 _pincount
refcount += GUP_PIN_COUNTING_BIAS(1024)
旧比对一律按 +1 预期
small folio 永远对不上
错的是比对基准
不是 per-CPU cache 真有残留
后果 · 每次必误判,两级 drain 全走
唯一能进比对的类型,恰恰必然误判
per-CPU LRU cache 只缓存 small folio
folio_may_be_lru_cached() 不认 large folio
进入比对的只有 small folio
同一个 folio 连续触发两级 drain
先 lru_add_drain()
再 lru_add_drain_all()
lru_add_drain_all() 在每个在线 CPU 调度执行
全局开销大
每次进入该路径都全量扫一遍
drain 从兜底变成了常态
方案 · 比对基准用 pin 的真实增量
large folio 记 1,small folio 记 1024
const int pin_refs =
folio_has_pincount(folio) ? 1 : GUP_PIN_COUNTING_BIAS;
if (folio_ref_count(folio) !=
folio_expected_ref_count(folio) + pin_refs)
有 _pincount 记 1,没有记 1024
语义回到设计初衷
无残留:ref_count 恰好相等
有残留:才继续往下 drain
drain 不再被误触发
不是改 drain 逻辑
是把比对基准改对
边界 · 回归按需兜底
drain 没有被删掉,是不再必然触发
没有残留
比对恰好相等
两级 drain 都跳过
真有残留
先排空本 CPU
仍不符再排空所有 CPU
drain 仍在那里
只在 per-CPU LRU cache 真残留引用时触发
兜底重新是兜底
预期收益 · 作者未提供性能数据
以下均为代码逻辑推断,不是实测
不再必然触发全 CPU 的 LRU cache drain
small folio 的引用比对不再误判
消除 lru_add_drain_all() 的全局开销
不再在每个在线 CPU 上调度执行、扰动缓存
drain 回归按需兜底
仅 per-CPU LRU cache 真残留引用时才触发
空格
播放/暂停
←
→
逐动作
R
重播
⚠
1x