sched discussion
[PATCH] sched/cache: honor migrate_llc_task semantics in active load balance
LLM 分析
sched/cache:让 active load balance 真正遵守 migrate_llc_task 的语义
系列概况
- 标题:[PATCH] sched/cache: honor migrate_llc_task semantics in active load balance
- 作者:Lu Wang wanglu.priv@gmail.com
- 版本:单封 patch(无版本号)
- 规模:1 个 patch,2 文件改动,23 行新增 / 3 行删除
- 修改文件:
kernel/sched/fair.c、kernel/sched/sched.h - 代码统计:fair.c +22 / -3,sched.h +1 / -0
- Message-ID:
20260801121729.2468102-1-wanglu.priv@gmail.com - 完整性:commit message(含 Fixes / Signed-off-by)、diff hunk、上下文片段均齐全,可独立分析;本系列无后续回复、无 v2。
补丁目的
这个补丁修复 migrate_llc_task 在 active load balance 路径上的两个 bug:
- migration type 跨异步边界丢失:被动均衡(passive load balance)选定 migrate_llc_task 并触发 active balance 后,CPU stopper 回调里重新构造的 lb_env 是一个 fresh 结构,.migration_type 没有从发起端继承下来。后续在 active_load_balance_cpu_stop() 中挑选迁移候选时,原本想做的 LLC 感知迁移变成无差别迁移。
- CAS 触发的 active balance 漏判 LLC 不匹配:active balance 由 sched_balance_rq() 的 CAS 路径发起时,没有复用 migrate_degrades_llc() 里已经存在的「任务 preferred LLC 与目的 LLC 不一致就拒绝」逻辑,导致 can_migrate_task() 在 active balance 阶段放过本应被留下的任务。
修完后,被动均衡承诺的「只把任务搬到它喜欢的 LLC」语义在异步 active balance 路径上也成立。
旧流程的问题
passive LB active LB (CPU stopper)
------------ -----------------------
env.migration_type = active_load_balance_cpu_stop()
migrate_llc_task env = { .idle = CPU_IDLE,
alb_break_llc() -> enqueue .flags = LBF_ACTIVE_LB, ... }
stop work .migration_type = ??? <-- default 0 / migrate_task
|
v
can_migrate_task(p, env)
return 1; <-- any task allowed
|
v
task moved to any dst_cpu,
even if preferred_llc != llc_id(dst_cpu)
旧流程里 migrate_degrades_llc() 只在 sched_cache_enabled() 编译打开时生效;即使开了 cache 感知,这条逻辑也从未被 active balance 路径上的 can_migrate_task() 调用——后者一直 return 1,于是 active balance 阶段完全无视 LLC 语义。同时 active_balance_type 没有保存到 rq 上,CPU stopper 唤醒时根本不知道发起端想做什么类型的迁移。
新流程
补丁引入共用 helper migrate_llc_task_wrong_dst(),把 LLC 不匹配的判定从 migrate_degrades_llc() 中抽出,并在两条路径上复用:
passive LB active LB (CPU stopper)
------------ -----------------------
env.migration_type = busiest->active_balance_type
migrate_llc_task = env.migration_type <-- newly saved
alb_break_llc() -> enqueue |
stop work v
active_load_balance_cpu_stop()
env.migration_type =
busiest_rq->active_balance_type
|
v
can_migrate_task(p, env)
return !migrate_llc_task_wrong_dst(p, env);
|
v
preferred_llc != llc_id(dst_cpu)
=> reject and skip
struct rq 上新增的 active_balance_type 字段就是这条「异步接力棒」,保证被动均衡的意图完整传到 CPU stopper。
Patch 概览
按文件列出本 patch 的关键变更:
kernel/sched/sched.h:struct rq 中新增int active_balance_type; /* enum migration_type */,作为发起端到 stopper 的传递通道。kernel/sched/fair.c:- 新增 migrate_llc_task_wrong_dst(p, env) helper(sched_cache_enabled() 时返回 p->preferred_llc != llc_id(env->dst_cpu),否则 stub 返回 false)。
- migrate_degrades_llc() 改用 helper 替代原 inline 判断(保持语义不变,只是去重)。
- can_migrate_task() 在 LBF_ACTIVE_LB 路径返回 !migrate_llc_task_wrong_dst(p, env),覆盖 active balance 阶段的过滤。
- sched_balance_rq() 在发起 active balance 前把 env.migration_type 写入 busiest->active_balance_type。
- active_load_balance_cpu_stop() 构造 lb_env 时从 busiest_rq->active_balance_type 回填 .migration_type。
关键实现
helper 在两条编译路径上的双胞胎写法:
#ifdef CONFIG_SCHED_CACHE
static inline bool
migrate_llc_task_wrong_dst(struct task_struct *p, struct lb_env *env)
{
return sched_cache_enabled() &&
env->migration_type == migrate_llc_task &&
READ_ONCE(p->preferred_llc) != llc_id(env->dst_cpu);
}
#else
static inline bool
migrate_llc_task_wrong_dst(struct task_struct *p, struct lb_env *env)
{
return false;
}
#endif
can_migrate_task() 在 active balance 入口新增的过滤:
if (env->flags & LBF_ACTIVE_LB)
return !migrate_llc_task_wrong_dst(p, env);
发起端写入、stopper 端读取的接力:
/* sched_balance_rq() -- 发起 active balance 前 */
busiest->active_balance_type = env.migration_type;
/* active_load_balance_cpu_stop() -- 真正挑选候选前 */
.migration_type = (enum migration_type)busiest_rq->active_balance_type,
类比
想象一家咖啡连锁店的调度中心:
- 被动均衡 是前台店员,看到本店咖啡机忙不过来,记下「这位客人想喝热的、不是冰的」(migrate_llc_task + preferred_llc),然后打电话叫隔壁店来帮忙。
- CPU stopper 是隔壁店派来的临时工。他只看得到一张交接单(lb_env),如果单子上「客人想喝什么」那一栏没写,他就会随便给一杯——热的、冰的都行,根本记不得原始客人的偏好。
- bug 就是:原来那张交接单根本没人填写「客人偏好」这一格;can_migrate_task() 见到人就说「可以搬」,不管客人到底想去哪家店。
- 补丁做的事:在前台店员那一侧把「客人偏好」抄到工单背面(busiest->active_balance_type),临时工拿单子时再读出来填进自己的表(env.migration_type),同时给「客人要去的店」和「实际搬去的那家店」之间加一个比对闸口——不一致就拒绝搬人。
Highlight:风险与注意点
- 类型宽度注意:active_balance_type 用 int 存 enum migration_type,读出时做了 (enum migration_type) 强转;如果未来枚举扩展到负值或更大集合,需要重新审视字段类型。
- READ_ONCE 与内存屏障假设:preferred_llc 在 migrate_degrades_llc() 中就用了 READ_ONCE,patch 沿用同一语义;但 active_balance_type 写入和读取跨 CPU stopper,没有显式屏障,仅依赖 CPU stopper 回调本身隐含的 happens-before(stop work 排队 -> 唤醒 -> 执行)。在弱内存模型上是否还需 smp_wmb/smp_rmb 值得后续检视。
- 行为变化对基准的影响:can_migrate_task() 在 active balance 阶段第一次拒绝候选,意味着某些原本会被搬走的任务会被留下,可能让热点 LLC 上的负载更「粘」。建议关注 sched/numa 与 cache_hot 基准(如 hackbench、schbench、stream)变化。
- stub 返回 false 是有意为之:!CONFIG_SCHED_CACHE 编译路径永远返回 false,相当于「关闭 cache 感知」——与 sched_cache_enabled() 为 false 时整体语义一致,没有引入新的拒绝分支。
- 与 Fixes 提交的关系:本 patch 修的是 e4c9a4cb244a(引入 migrate_llc_task 的提交)当时遗漏的主动均衡覆盖,属于原始设计的「半成品」,提交流程上应同时核对 Fixes: 是否被维护者接受。
版本变化
本 thread 只有 v1(单封 patch),没有 v2 / v3,因此没有「版本演进」维度可写。
一句话总结
把 migrate_llc_task 的「按 LLC 偏好迁移」语义从被动均衡一路修到 CPU stopper 触发的 active balance:保存 migration_type、抽出 LLC 错配判定、在 can_migrate_task() 上加拒绝闸口。