0/2 已展开

LLM 分析

sched/cache:在 ALB 中保留 migrate_llc_task 语义

系列概况

  • 标题[PATCH v3] sched/cache: honor migrate_llc_task semantics in active load balance(v4 标题大小写改为 Honor
  • 作者:Lu Wang <wanglu.priv@gmail.com>
  • 版本:v3 → v4(链接中可见 v1、v2 前期版本)
  • 规模:单文件改动 kernel/sched/fair.c,约 51 处新增、6 处删除
  • 修改文件kernel/sched/fair.c
  • 代码统计:57 行 hunk 总量,+51/-6
  • Message-ID
    • 20260813045241.3039862-1-wanglu.priv@gmail.com(v3)
    • 20260824023324.3663800-1-wanglu.priv@gmail.com(v3 之后的 ping 回复)
    • 20260903020656.3793626-1-wanglu.priv@gmail.com(v4 rebase)
  • 完整性:3 封邮件,覆盖 v3 patch、作者对 maintainer 的提醒 ping、v4 rebase patch;v4 仅 rebase,无功能改动

补丁目的

Cache-Aware Scheduling(CAS)新增的 migrate_llc_task 迁移类型,本意是把任务推向其 preferred_llc。但当被动负载均衡(passive LB)将任务 migrate_llc_task 作为目标时,若进一步回退到 Active Load Balance(ALB),ALB 内部会构造一份新的 lb_env,其中的 migration_type 不再携带 migrate_llc_task,于是 can_migrate_task() 可能放过一个“留在原 LLC 更合适”的候选任务,把它反向赶出 preferred LLC。补丁的目标是把“任务应当迁往哪个 LLC”这层语义穿过 ALB 的 cpu_stopper 边界保留下来。

旧流程的问题

源 runqueue 上有两个可运行任务 p1p2

  • p1preferred_llc = dst_llc(想去 dst_rq 所在的 LLC);
  • p2preferred_llc = src_llc(愿意留在 src_rq)。
+----------------------+        +----------------------+
| src_rq (src_llc)     |        | dst_rq (dst_llc)     |
|  p1 (wants dst_llc)  |  --->  |                      |
|  p2 (prefers src_llc)|        |                      |
+----------------------+        +----------------------+

被动 LB 检测到 src_rq 上至少有一个任务(p1)想去 dst_llc,于是把 migration_type 设为 migrate_llc_task 发起均衡,触发 ALB。ALB 的 stopper 在被踢起时重新构造 lb_env没有migrate_llc_task 传过来——can_migrate_task() 顺着 src_rq 扫描,看到 p2 也满足可迁移条件,便把 p2 搬走;这恰恰违背了 p2 的 LLC 偏好。

新流程

新增 LBF_ACTIVE_LB_LLC = 0x40 标志位。在 ALB 触发点 sched_balance_rq() 中通过 alb_stop_fn(env) 选择 cpu stopper 回调:

  • migration_type == migrate_llc_taskactive_load_balance_llc_cpu_stop(携带 LBF_ACTIVE_LB_LLC 标志);
  • 其它情况 → 旧的 active_load_balance_cpu_stop

两个 stopper 都委托给新拆出的 __active_load_balance_cpu_stop(data, lb_flags),后者把 lb_flags 合并到 lb_env.flagsmigrate_llc_task_wrong_dst() 这个共用 helper 同时识别 env->migration_type == migrate_llc_task(被动 LB 路径)和 env->flags & LBF_ACTIVE_LB_LLC(ALB 路径),从而在 migrate_degrades_llc()can_migrate_task() 中恢复“目标 LLC 不匹配就拒绝迁移”的行为。

                    passive LB (sets migrate_llc_task)
                                 |
                                 v
                  need_active_balance() -> sched_balance_rq()
                                 |
                  alb_stop_fn(env) 选择 stopper
                                 |
        +------------------------+------------------------+
        |                                                 |
migrate_llc_task == true                       其余情况
        |                                                 |
        v                                                 v
active_load_balance_llc_cpu_stop       active_load_balance_cpu_stop
        |                                                 |
        +----------> __active_load_balance_cpu_stop(data, lb_flags)
                                |
                  lb_env.flags |= LBF_ACTIVE_LB_LLC ?
                                |
                                v
        can_migrate_task() 看到 LBF_ACTIVE_LB_LLC
        拒绝“preferred_llc 与 dst_llc 不一致”的候选

Patch 概览

  • 新增 LBF_ACTIVE_LB_LLC = 0x40 标志;
  • 新增共用 helper migrate_llc_task_wrong_dst(),兼容被动 LB(看 migration_type)与 ALB(看 flags);
  • active_load_balance_cpu_stop 拆为两层:薄壳 + __active_load_balance_cpu_stop(data, lb_flags)
  • 新增 active_load_balance_llc_cpu_stop,调用 __active_load_balance_cpu_stop(data, LBF_ACTIVE_LB_LLC)
  • sched_balance_rq() 中通过 alb_stop_fn(&env) 选择 stopper 回调;
  • alb_break_llc() 不命中的 #else 分支保留 return false 的 stub,避免破坏 !CONFIG_CFS 编译。

关键实现

#define LBF_ACTIVE_LB_LLC 0x40

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 ||
            env->flags & LBF_ACTIVE_LB_LLC) &&
           READ_ONCE(p->preferred_llc) != llc_id(env->dst_cpu);
}
static inline cpu_stop_fn_t alb_stop_fn(struct lb_env *env)
{
    if (env->migration_type == migrate_llc_task)
        return active_load_balance_llc_cpu_stop;
    return active_load_balance_cpu_stop;
}
static int __active_load_balance_cpu_stop(void *data, unsigned int lb_flags)
{
    ...
    .flags = LBF_ACTIVE_LB | lb_flags,
    ...
}

static int active_load_balance_cpu_stop(void *data)
{
    return __active_load_balance_cpu_stop(data, 0);
}

static int active_load_balance_llc_cpu_stop(void *data)
{
    return __active_load_balance_cpu_stop(data, LBF_ACTIVE_LB_LLC);
}

设计要点是“选 callback 而非带 migration_type 穿过 stopper”:补丁作者明确解释过,stopper 是异步边界,如果直接把 migrate_llc_task 透传到 ALB,会与 delayed-dequeue 任务对 migration_type 的语义检查相冲突,因此选 (b) 方案:用一个 flag 把意图钉在 lb_env.flags 上。

类比

把 LLC 想象成“图书馆分馆”:每个读者(任务)有自己习惯去的分馆(preferred_llc)。前台(passive LB)看到读者甲想换去总馆借书,于是派人去把他请过去。可如果前台人手不够,又请了一位临时工(ALB stopper)来搬运——临时工没有拿到“只搬运甲”的小纸条,结果把同样站在门口的读者乙也一起顺走了。补丁相当于给临时工发了一张“只搬去总馆偏好者”的纸条:要么选带小纸条的搬运工 active_load_balance_llc_cpu_stop,要么派普通搬运工 active_load_balance_cpu_stop,从源头分单,就不会误搬乙。

Highlight:风险与注意点

  • LBF_ACTIVE_LB_LLC 与已有 LBF_SOME_PINNED/LBF_ACTIVE_LB/LBF_LLC_PINNED 共用位图(0x08/0x10/0x20),新值 0x40 不冲突,但继续增加位时要防止溢出。
  • alb_stop_fn()sched_balance_rq() 中基于调用现场的 env 决策,避免在 stopper 内部再次判断 migration_type,从而隔离异步边界——但这也意味着所有未来可能新增的“带 LLC 语义”的 ALB 入口都要在这里登记。
  • migrate_degrades_llc() 已替换为共用 helper;若后续再拆出第三种 migration_type,需要同步扩展 migrate_llc_task_wrong_dst() 的判定。
  • v4 是单纯 rebase(基于 ef9293b3b7,含 Tim Chen f0d243a96f26 “避免 cache-aware balancing 制造 misfit”),没有功能改动;review 关注点仍是 v3 时确认的语义正确性。
  • 仅有作者发起的 ping,未见 maintainer 回复;Fixes: tag 指向 e4c9a4cb244a(CAS 引入 commit),说明这是上游引入的回归。

版本变化

  • v1 → v2:在 kick 时选 stopper 回调,避免 migration_type 穿过 stopper 影响 delayed-dequeue 任务的语义(即方案 b)。
  • v2 → v3:仅改写 commit message 与 helper 注释,无代码改动。
  • v3 → v4:rebase 到 ef9293b3b7(包含 Tim Chen 的 misfit 修复 f0d243a96f26),无功能改动。

一句话总结

通过新增 LBF_ACTIVE_LB_LLC 标志位与两个 cpu stopper 回调(普通版与 LLC 版),让 ALB 在被动 LB 触发后仍能识别“该按 preferred LLC 选任务”的意图,避免把不该搬的任务赶出其 preferred LLC。