0/18 已展开

LLM 分析

sched:非对称容量场景下的集群调度修复(v6)

系列概况

  • 标题:[PATCH v6 0/6] sched: Fix cluster scheduling in the presence of asymmetric capacity
  • 作者:Ricardo Neri ricardo.neri-calderon@linux.intel.com
  • 版本:v6(前置 v1~v5 链接齐全)
  • 规模:6 patches- 修改文件include/linux/sched/sd_flags.hkernel/sched/fair.ckernel/sched/topology.c
  • 代码统计:3 文件,+49 / -24
  • Message-ID20260720-rneri-fix-cas-clusters-v6-0-bb500bf4afd4@linux.intel.com
  • 完整性:完整;tip-bot2 已确认全部 6 个 patch 进入 sched/core tip 分支(commit f2c2ba727fd540b10fbd428d50b101f66060d61d180ff97c,committer Peter Zijlstra,2026-08-07)

补丁目的

在 big.LITTLE/hybrid CPU 拓扑(Alder Lake、Lunar Lake、Panther Lake 等)里,调度器需要保证 misfit 任务落在大核,把剩余任务均匀铺到小核 cluster 的多个 L2 组。本系列修复了阻止这一行为的五个相互关联的 bug(cover letter 中 a~e),让 SD_PREFER_SIBLINGCONFIG_SCHED_CLUSTER 的语义在小核集群之间真正生效。

旧流程的问题

  1. update_sd_pick_busiest()选 busiest 时做了容量判断,但顺序过晚,类型比较会把 equal-capacity fully_busy 组错误胜出。
  2. 小核 cluster 之间互相把对方识别成 misfit_task,阻挡跨 cluster 拉平。
  3. sched_balance_find_src_rq() 用 "~5% 容量差" 跳过 identical-capacity源 CPU,违背 CONFIG_SCHED_CLUSTER 设计。
  4. sg_overloadedSD_ASYM_CPUCAPACITY 路径里缺少 balancing_at_rd gating,浪费统计。
  5. SMT 兄弟忙时本应被选中的源 CPU 被错误跳过。
  6. SD_PREFER_SIBLINGSD_ASYM_CPUCAPACITY 子域里被强制清掉,兄弟 cluster 无法均衡。

新流程

按 patch 顺序组合后:

  • patch 1 用 smt_degraded_cap 在源 CPU 侧先判定 SMT 兄弟;
  • patch 2 把 sg_overloaded 的写入收敛到 balancing_at_rd
  • patch 3 把 capacity_greater 检查前置到 group_type 比较之前;
  • patch 4 仅在 dst_cpu 大于组内最大容量时累加 misfit,但保留 sg_overloaded=1
  • patch 5 引入 cluster_equal_cap,identical capacity 不再被 ~5% 规则跳过;
  • patch 6 恢复 SD_PREFER_SIBLING,让 newly-idle 平衡能在 cluster 之间迁移任务。

Patch 概览

#主题文件关键点
1SMT busy sibling 不跳过fair.csmt_degraded_cap 局部变量
2gating sg_overloadedfair.cbalancing_at_rd 检查
3容量比较前置fair.cupdate_sd_pick_busiest 顺序
4条件性 misfit 累加fair.cdst_cpu > max_capacity 才累加
5identical capacity 允许迁移fair.ccluster_equal_cap + sched_cluster_active
6恢复 SD_PREFER_SIBLINGtopology.c + sd_flags.h删清 flag 的逻辑

关键实现

patch 3(核心顺序调整):

if ((env->sd->flags & SD_ASYM_CPUCAPACITY) &&
    (sgs->group_type <= group_fully_busy) &&
    (capacity_greater(sg->sgc->min_capacity, capacity_of(env->dst_cpu))))
    return false;

patch 4(条件 misfit 累加):

if (rq->misfit_task_load) {
    if (balancing_at_rd)
        *sg_overloaded = 1;
    if (capacity_greater(capacity_of(env->dst_cpu),
                          group->sgc->max_capacity) &&
        (sgs->group_misfit_task_load < rq->misfit_task_load))
        sgs->group_misfit_task_load = rq->misfit_task_load;
}

patch 5(identical capacity 例外):

bool cluster_equal_cap = static_branch_unlikely(&sched_cluster_active) &&
    (get_actual_cpu_capacity(env->dst_cpu) ==
     get_actual_cpu_capacity(i));

if (!smt_degraded_cap && !cluster_equal_cap &&
    !capacity_greater(capacity_of(env->dst_cpu), capacity))
    continue;

patch 6(topology 改动):

// 删除 sd_init 里的清 flag 代码// if ((sd->flags & SD_ASYM_CPUCAPACITY) && sd->child)
//     sd->child->flags &= ~SD_PREFER_SIBLING;

ASCII流程图

+-------- Big CPU (capacity_high) ---------+
| B0 |
| B1                                      |
+-----------------------------------------++-------- Small cluster0 --------+ +-------- Small cluster 1 --------+
| s0  s1  s2  s3  (L2) | | s4  s5  s6  s7  (L2)            |
+--------------------------------+ +--------------------------------+

old flow:
  update_sd_pick_busiest  -->  pick fully_busy on cluster 0
  misfit load accounting -->  cluster 1 marked misfit_task
  sched_balance_find_src_rq -->  identical cap skipped
  SD_PREFER_SIBLING        -->  cleared under ASYM_CAPACITY
  => tasks pile up on cluster 0

new flow (v6):
1) SMT sibling busy? smt_degraded_cap (patch 1)
  2) gating overloaded?   balancing_at_rd only (patch 2)
  3) capacity_greater check FIRST (patch 3)
  4) skip misfit load if dst <= max_cap (patch 4)
  5) cluster_equal_cap -> allow migration  (patch 5)
  6) SD_PREFER_SIBLING restored (patch 6)
  => cluster 0 and cluster 1 share load
update_sd_pick_busiest() decision tree candidate sg |
              v SD_ASYM_CPUCAPACITY && type<=fully_busy
  && capacity_greater(sgc->min, dst) ? ----yes----> return false
              |
             no
              v compare group_type vs busiest
              |
              v
   pick busiest, then find src_rq
 (smt/cluster_equal_cap aware)

类比

把 CPU 想象成办公室的两类员工:资深员工(大核)处理大项目,初级员工(小核)按 4 人一组(cluster)共用复印机(L2)。bug a~e 就像经理挑人的规则出问题:要么不向闲人派活,要么把所有活都堆给资深,要么不让小组之间互相借调。本系列让经理按规则把活真正均匀分到各小组,复印机不再被某个小组累垮。

Highlight:风险与注意点

  • patch 3 把容量检查前置,可能影响纯大核/小核的非 cluster 系统,需配合 patch 6 一并回归。
  • patch 4 把 misfit 累加条件化,要确认 newly_idle 路径仍能挑出 misfit 并交给大核。
  • patch 5 用 get_actual_cpu_capacity() 取代 arch_scale_cpu_capacity(),对 cpufreq 压力更敏感,低频场景需观察抖动。
  • patch 6 修改 SD_PREFER_SIBLING 是行为变更,建议在 Raptor/Meteor Lake 等更多 hybrid 拓扑上验证。
  • v6 评审尚未完全落幕(Vincent/Christian 的 reply仅有引文),后续可能还有微调 patch 5/6 的讨论。

版本变化

  • v1 → v2:patch 2 修复 sched_reduced_capacity 互斥;patch 3 改用 arch_scale_cpu_capacity();patch 5 描述改写。
  • v2 → v3:patch 3 反转容量检查;patch 4 描述扩展 overloaded/low-capacity 行为。
  • v3 → v4:patch 1 修 SMT 多 busy 兄弟 bug;patch 2 修多余 sg_overloaded 更新;patch 5 用局部变量。
  • v4 → v5:patch 1 仅按需判断 SMT busy;patch 5 仅按需检查 architectural capacity。
  • v5 → v6:patch 5 改用 get_actual_cpu_capacity();patch 6 扩展为所有 asymmetric 域恢复 SD_PREFER_SIBLING;新增 Vincent/Andrea 的 Reviewed/Tested tag。

一句话总结

v6 通过修补 update_sd_pick_busiest 顺序、misfit 累加、identical-capacity 判定以及恢复 SD_PREFER_SIBLING,让 hybrid CPU 的小核 cluster 真正能跨 L3 平摊负载,并已被 Peter Zijlstra 收进 sched/core tip。