0/1 已展开

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.ckernel/sched/sched.h
  • 代码统计:fair.c +22 / -3,sched.h +1 / -0
  • Message-ID20260801121729.2468102-1-wanglu.priv@gmail.com
  • 完整性:commit message(含 Fixes / Signed-off-by)、diff hunk、上下文片段均齐全,可独立分析;本系列无后续回复、无 v2。

补丁目的

这个补丁修复 migrate_llc_task 在 active load balance 路径上的两个 bug:

  1. migration type 跨异步边界丢失:被动均衡(passive load balance)选定 migrate_llc_task 并触发 active balance 后,CPU stopper 回调里重新构造的 lb_env 是一个 fresh 结构,.migration_type 没有从发起端继承下来。后续在 active_load_balance_cpu_stop() 中挑选迁移候选时,原本想做的 LLC 感知迁移变成无差别迁移。
  2. 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() 上加拒绝闸口。