sched discussion
[PATCH v2 2/2] sched/cache: Drive cache task tick from execution context
LLM 分析
sched:proxy execution 下把 task tick 从 fair 路径上移到调度核心
系列概况
- 标题:[PATCH v2 0/2] sched: Fix execution-context tick handling under proxy execution
- 作者:Hui Su sh_def@163.com;Suggested-by Tim Chen tim.c.chen@linux.intel.com;参与讨论 K Prateek Nayak (AMD)、Chen Yu (Intel)
- 版本:v2,cover letter 给出 v1→v2 变更概要;尾部 Hui 在第 5/9 封预告 v3
- 规模:2 个 patch,3 个文件,合计 +19/-9
- 修改文件:
kernel/sched/core.c:在sched_tick()/sched_tick_remote()增加 fair 执行上下文 tickkernel/sched/fair.c:task_tick_numa/task_tick_cache由static改全局,并从task_tick_fair()移除调用kernel/sched/sched.h:导出两个函数声明
- 代码统计:core.c +13/-3,fair.c +6/-7,sched.h +2,合计 +21/-10(cover letter 给出 +19/-9,覆盖的是 v2 在 fair.c 中
task_tick_fair()上下文若干保留行差异) - Message-ID:
- cover:
20260903041154.2479761-1-sh_def@163.com - 1/2:
20260903041154.2479761-2-sh_def@163.com - 2/2:
20260903041154.2479761-3-sh_def@163.com
- cover:
- 完整性:cover + 2 个 patch + Prateek/Hui/Tim/Chen Yu 的 9 封来回完整;第 15 封 v3 预告被截断,但设计方向(新增
core_sched_start字段、改写__entity_slice_used())已清楚
补丁目的
Proxy execution 把"调度上下文"(rq->donor,决定 CFS/RT/DL 策略与切片)和"执行上下文"(rq->curr,真正占用 CPU 的 task_struct)分开。
sched_tick() 通过 donor->sched_class->task_tick() 派发调度类钩子,但 task_tick_fair() 同时承载 NUMA scan、cache scan 这些属于执行上下文的逻辑。当 donor 是 RT/DL、curr 是 fair 时,task_tick_fair() 不被调用,task_tick_numa/cache 漏跑;同时 update_se() 仍按 rq->curr 推进 account_mm_sched(),mm 的 scan epoch 不前进,最终 cache-aware 调度的 preferred LLC 会被错误作废。
本系列把执行上下文 tick 上提到 sched_tick() / sched_tick_remote(),按 rq->curr 是否 fair 触发,修掉回归并保留 full-dynticks 行为。
旧流程的问题
+-----------------------+
| sched_tick() |
| uses rq->donor |
+----------+------------+
|
v
+-----------------------+
| task_tick_fair() | only when donor is fair
| task_tick_numa |
| task_tick_cache |
+-----------------------+
当 donor 是 RT/DL、curr 是 fair:donor 的 task_tick 不会进入 fair 路径,NUMA/cache tick 都被跳过;update_se() 仍按 curr 记账 runtime,于是出现"runtime 涨、scan epoch 不涨"的错位,mm 的 preferred LLC 会失效。
新流程
+--------------------------------+
| sched_tick() / remote tick |
| 1) donor->class->task_tick |
| 2) if rq->curr is fair: |
| task_tick_numa() |
| task_tick_cache() |
+--------------------------------+
|
v
fair path keeps
misfit/overutilized/
core sched with donor
sched_tick() / sched_tick_remote() 现在统一调用 sched_tick_exec_ctx()(v3 抽出的 helper)来执行 fair 执行上下文 NUMA/cache tick;fair 路径只保留 misfit/overutilized/core sched 等调度上下文语义。
Patch 概览
- 1/2 sched/numa:
task_tick_numa由static改全局;sched_tick()/sched_tick_remote()在 curr 是 fair 时调用;从task_tick_fair()删除原调用 - 2/2 sched/cache:同样手法处理
task_tick_cache,覆盖 NUMA 之外的 cache scan epoch - 共同点:两处都同时修改
sched_tick_remote(),保证 nohz_full CPU 仍能驱动执行上下文 tick
关键实现
void sched_tick(void)
{
...
resched_curr(rq);
donor->sched_class->task_tick(rq, donor, 0);
if (sched_feat(LATENCY_WARN))
resched_latency = cpu_resched_latency(rq);
calc_global_load_tick(rq);
/* sched_tick_exec_ctx(): fair execution-context hook */
if (rq->curr->sched_class == &fair_sched_class) {
if (static_branch_unlikely(&sched_numa_balancing))
task_tick_numa(rq, rq->curr);
task_tick_cache(rq, rq->curr);
}
...
}
sched_tick_remote() 走相同的 fair-execution-context 钩子,但走 work->func,没有 resched_curr / global load。task_tick_numa / task_tick_cache 在 fair.c 中保留实现,声明导出到 sched.h。
/* task_tick_fair() 末尾,保留给调度上下文语义 */
if (queued) return;
task_tick_cache(rq, curr); /* removed in v2 */
update_misfit_status(curr, rq);
check_update_overutilized_status(task_rq(curr));
task_tick_fair() 删掉 cache/numa 调用之后,剩下的 misfit/overutilized/core sched 都属于"被 load balancer 搬运的实体",依然跟随 donor;Tim 在第 7 封建议加注释明确这一点,Hui 在 v3 中会补。
类比
把 CPU 想象成出租车交接班:
- donor(调度上下文) = 持有运营资质的"当班司机",决定接谁、跑多久、什么时候交班
- rq->curr(执行上下文) = 实际坐在驾驶座上开车的"代驾",只是借车练手
- fair 路径的 misfit/overutilized/core sched = 调度方该关心的事,跟着 donor 走
- NUMA scan / cache scan = 乘客(mm)和实际驾驶产生的里程与定位数据,应该跟着真正在跑的车走,否则里程表会失真
旧流程让"代驾"走了调度员,结果里程表没人记;新流程把"乘客数据更新"独立出来,无论谁开车都能记录。
Highlight:风险与注意点
- 执行上下文 vs 调度上下文混用风险:
task_tick_core()必须留在 fair.c、传 donor,因为 SMT force-idle 关心的是 donor 的调度切片;讨论中 Tim 明确警告不能"全统一" - 隐藏的
task_tick_core()bug:proxy 下 donor 的se->sum_exec_runtime不前进,__entity_slice_used()算的 delta 接近 0,force-idle 可能永不触发;Hui 在第 9 封确认会单独修,第 13-15 封讨论"选中时刻 vruntime 快照"或新增core_sched_start字段的方案 - 重复代码:
sched_tick()/sched_tick_remote()都做 fair-execution-context tick,Prateek 建议抽 helper,Hui 在 v3 中承诺合并为sched_tick_exec_ctx() - 可读性:Tim 提议在
task_tick_fair()加注释,说明 misfit/overutilized/core sched 仍属 donor,避免后人再次误迁移 - full-dynticks 行为对齐:迁移时必须同时更新
sched_tick_remote(),否则 nohz_full CPU 会丢 tick - 修复目标 commit:NUMA patch
Fixes: 7de9d4f94638(proxy execution 引入),cache patchFixes: df0d98475954(cache-aware scheduling 引入)
版本变化
- v1 → v2(cover letter)
- 把执行上下文 tick 从
task_tick_fair()上移到sched_tick()/sched_tick_remote() - 触发条件由"donor 是 fair"改为"curr 是 fair"
- 显式为
sched_tick_remote()添加对应调用,保留 full-dynticks
- 把执行上下文 tick 从
- v2 → v3(由 Hui 在第 5、9 封回复中预告)
- 抽
sched_tick_exec_ctx()公共函数,集中 fair 执行上下文 tick - 在
task_tick_fair()加注释解释 misfit/overutilized/core sched 仍属于调度上下文 task_tick_core()在 proxy 下sum_exec_runtime不前进的问题作为独立补丁跟进(v3 见第 15 封:在sched_entity加core_sched_start,由set_next_entity()设置)
- 抽
一句话总结
本系列修复 proxy execution 下 task_tick_numa/cache 被 donor 是 RT/DL 时漏调用的回归,把执行上下文 tick 上提到 sched_tick() / sched_tick_remote(),并在讨论中确立"调度上下文 vs 执行上下文"的清晰分层。