migrate_pages_batch():逐 folio unmap 再搬移
批量 folio
一批一批迁移
逐个 unmap
每个 folio 一次
搬移
到新的位置
KVM 宿主上,每次 unmap 的 mmu_notifier 都不便宜
try_to_migrate()
kvm_mmu_notifier_invalidate_range_start()
tdp_mmu_zap_leafs()
大批量迁移时,CPU 被这个循环长时间占住
RCU-tasks 没有读侧加锁原语
grace period 的判定,靠任务自己走到静止状态
✓ 自愿上下文切换
✓ 执行用户态代码
✓ 进 idle
✓ 主动上报
做到任意一个
GP 就不必再等这个任务
✗ 被抢占
非自愿抢占不算静止状态
所以循环里的 cond_resched() 帮不上忙
unmap 循环中的 cond_resched()
PREEMPTION 内核上,它被实现成空操作
迁移任务既不自愿切换,也不上报
任务因此挂上 holdout 链表
迁移任务
如 kcompactd
holdout 链表
任务被挂上
grace period
被拖到 stall
Meta 舰队实测
kcompactd 触发 rcu_tasks stall,grace period 被拖至分钟级
cond_resched()
循环里本来用它让出
PREEMPTION 下空操作
cond_resched_tasks_rcu_qs()
每轮 unmap 间隙都上报一次
让出与上报合成一次调用
循环还是原来的循环
每次抵达让出点,都上报一次
上报不再以调度发生为前提
循环不必真的让出,也能报静止状态
rcu_tasks_qs()
上报
检查当前任务的 holdout 标记
若已被 RCU-tasks 的 GP kthread 置位,就清掉
cond_resched()
调度
保持原语义
清掉标记 = 声明 grace period 不必再等自己
前半段上报,后半段保留调度语义
上报与调度就此解耦
无论它是否空操作,holdout 标记都会被清
上报点在 unmap 循环的间隙
unmap folio
间隙
unmap folio
间隙
更多 folio
每个间隙都是原有 cond_resched() 的调用点
换成上报原语后,位置不动
间隙里未持有锁
本来就是合法的调度点
安全性与原有调度点一致
换的只是原语,调用位置没有移动
① GP 不再被拖住
大批量迁移不再
拖延 grace period
消除分钟级 stall 告警
② 回调得以及时执行
trampoline 等
依赖 RCU-tasks 的回调
不必再等被拖住的 GP
③ 开销可忽略
无 grace period 时
每轮循环仅多
一次标记读取
作者未提供性能数据,收益由机制推出
从代码逻辑看,上报补上后 GP 不再被迁移循环拖住
把静止状态上报从调度里解耦
循环里的让出点不再依赖「真的发生调度」
空格
播放/暂停
←
→
逐动作
R
重播
⚠
1x