sched discussion
[PATCH] sched/numa: clear locality statistics on early return
LLM 分析
sched/numa: NUMA 扫描周期慢路径 locality 统计清零缺失
系列概况
- 标题:
[PATCH] sched/numa: clear locality statistics on early return - 作者: Hongling Zeng
<zenghongling@kylinos.cn>(麒麟软件) - 版本: 单封 v1,无系列号、无索引、无总数
- 规模: 1 file changed, 2 insertions(+), 0 deletions(-)
- 修改文件:
kernel/sched/fair.c - 代码统计:
update_task_scan_period()内部新增 1 处memset - Message-ID:
20260804090205.712192-1-zenghongling@kylinos.cn - 完整性: 邮件正文中 diff 上下界完整可见,commit message 与
Signed-off-by/Fixes/Cc签名齐全,hunk 末尾上下文可读
补丁目的
commit f307cd1a32fa("sched/numa: Slow scan rate if no NUMA hinting faults are being recorded")引入了"无 NUMA 提示错误时放慢扫描"的策略:当 p->numa_faults_locality[0](migration 失败档)非零时,update_task_scan_period() 直接走 slow-scan 分支并提前返回。问题在于,这条 slow-scan 提前返回路径漏掉了对 numa_faults_locality 的清零——正常路径里有清理,这里没有。
后果:一次迁移失败会让 locality 统计永久残留非零,后续每次 update_task_scan_period() 都看到旧的迁移失败记录,反复走 slow-scan,直到 numa_scan_period 顶到上限。补丁在 slow-scan 路径返回前补上 memset,让后续扫描周期评估拿到一份干净的 locality。
旧流程的问题
update_task_scan_period(task)
|
v
read p->numa_faults_locality[0]
|
nonzero? ---Y---> slow-scan branch
| |
| +--> early return <-- MISSED memset!
| |
| v
| one migration failure is remembered
| |
| v
| next call sees nonzero -> slow-scan again
| |
| v
| numa_scan_period keeps growing to MAX
|
N
v
normal path (already has memset)
老逻辑相当于"一次扣分、终身禁赛":即便 workload 早就恢复正常,扫描速率也回不到正常档。
新流程
update_task_scan_period(task)
|
v
read p->numa_faults_locality[0]
|
nonzero? ---Y---> memset numa_faults_locality
| |
| +--> set p->mm->numa_next_scan
| |
| +--> return
| |
| v
| next call sees clean stats, re-evaluate
|
N
v
normal path (existing memset logic)
Patch 概览
| 文件 | 函数 | 行号附近 | 改动 |
|---|---|---|---|
kernel/sched/fair.c | update_task_scan_period | @@-3518 | slow-scan 分支 return 前新增 memset |
关键实现
static void update_task_scan_period(struct task_struct *p,
unsigned long next_scan,
unsigned long period)
{
/* ...省略若干 locality 统计累计与慢路径触发条件... */
if (p->numa_faults_locality[0]) {
/* slow-scan: 把 locality 一起清零后再返回 */
memset(p->numa_faults_locality, 0,
sizeof(p->numa_faults_locality));
p->mm->numa_next_scan = jiffies +
msecs_to_jiffies(p->numa_scan_period);
return;
}
/* normal path: 此前的旧代码里已经把 locality 清零 */
/* ...省略 scan_period 收敛与下一次扫描时间计算... */
}
只有 2 行新增,关键点:
memset用&p->numa_faults_locality取址,sizeof(p->numa_faults_locality)拿到的是数组整体大小,避免退化为指针。- 清零发生在 return 之前,保证即便下次不再走 slow-scan,也不会被旧值污染。
numa_next_scan的赋值保持不变,仅多了一步清理。
类比
把 numa_faults_locality 想成"上季度的考勤扣分表"。
- 旧逻辑:发现有人迟到过一次就把整张表锁进柜子,从今往后每次考核都看到"曾迟到",无限扣分。一次意外永久背锅。
- 新逻辑:发现一次迟到就当场把本季度的扣分清零,下一季度从零开始评估。一次意外只影响本季度。
再换一个更贴合 scheduler 的类比:就像交叉口的红绿灯原本设计是"夜间车少时自动切黄闪",但控制器的"夜间模式"标志位一旦被置上就一直保持。补丁相当于在退出"夜间模式"前清掉这个标志,让下一个白天的循环能正常进入红绿灯循环。
Highlight:风险与注意点
- 其它慢路径:本次只补了
update_task_scan_period()。fair.c中其它处理numa_faults_locality的位置(统计累计、task_scan_max/task_scan_min决策)若也存在"提前 return 不清零"的同类问题,需要后续通盘审视。 - 清零粒度:
memset把 locality 的全部档位一起清零(包括非 migration 那一档),可能丢掉刚刚积累的局部性信息;更激进的做法是只清numa_faults_locality[0],但当前数组语义下做统一清零改动最小、最稳。 - 真实根因:若系统确实存在持续 NUMA 迁移失败(拓扑拥塞、绑定策略冲突),清零只是"延后症状",用户空间的
numactl绑定或硬件拓扑本身需要排查;补丁只解决"统计残留导致调度长期错档"。 Cc stable标注:作者把补丁 CC 给stable@vger.kernel.org,说明这是一个会影响已发布内核的回归问题,下游发行版(麒麟等)可能需要 backport。- 回归来源可追溯:commit message 里
Fixes: f307cd1a32fa明确指向引入该 bug 的上游 commit,方便git blame与 stable 树回溯。
一句话总结
补丁在 update_task_scan_period() 的 slow-scan 提前返回路径上补上 memset(p->numa_faults_locality, 0, ...),让一次 NUMA 迁移失败不再让 task 永远困在慢扫描档位。