0/1 已展开

LLM 分析

sched/fair:修复注释中 util_avg_cap 公式

系列概况

  • 标题[PATCH] sched/fair: Fix the util_avg_cap formula in a comment
  • 作者:Zhan Xusheng zhanxusheng@xiaomi.com
  • 版本:单封 v1(无版本号)
  • 规模:1 个 patch,单文件改动
  • 修改文件kernel/sched/fair.c
  • 代码统计:4 insertions(+), 2 deletions(-)
  • Message-ID20260717092530.1495648-1-zhanxusheng@xiaomi.com
  • 完整性:完整 patch,包含 diff、commit message 主体、Fixes 标签、Sign-off

补丁目的

init_entity_runnable_average() 上方解释新任务 util_avg 上限的注释里写的是
(cpu_scale - cfs_rq->avg.util_avg) / 2^n,而真实代码实现的是除以 2。

注释本身给的工作示例(1024 容量、512 已用、第 2 个任务得到 256)和代码也是
一致的——只是文字公式少抄了"反复对剩余预算除以 2 才会得到 2^n 序列"这一层
逻辑。本补丁把注释公式改成与代码一致,并补一句说明 2^n 序列来自"每新建一个
任务就把剩余预算再砍一半",而不是每次新建任务都按 2^n 缩放。

旧流程的问题

注释里 (cpu_scale - cfs_rq->avg.util_avg) / 2^n 的写法有两个问题:

  1. 公式与代码不一致:代码做的是 (cpu_scale - cfs_rq->avg.util_avg) / 2
    不是 / 2^n。读者按注释公式去对照代码会一头雾水。
  2. 公式与注释自身的例子不一致:用第 2 个任务、util_avg = 512
    cpu_scale = 1024 代入,注释公式得到 (1024 - 512) / 2^2 = 128
    而注释表格里给出的值是 256,代码算出来也是 256。

这是个纯注释 bug,但阅读 fair.c 的开发者会拿到一个错误的"心智模型",
从而误以为单次上限的衰减系数随任务序号指数增长——这对后续调试或在该处
增加新逻辑(例如更细粒度的 cap)非常危险。

新流程

保留代码逻辑不动,只把注释公式改为
util_avg_cap = (cpu_scale - cfs_rq->avg.util_avg) / 2
并加一句解释:因为 cfs_rq->avg.util_avg 会累加之前已建任务的结果,
每次"对剩余预算再砍一半"叠加起来,对第 n 个任务来说最终上限才是
cpu_scale / 2^n。代码语义零变化,只修正文档。

Patch 概览

文件位置改动
kernel/sched/fair.cinit_entity_runnable_average 上方注释块公式 /2^n/2;新增 2 行解释 2^n 序列的来源

关键实现

被改动的核心是注释,不是 C 代码。改动前后的 hunk 含义如下:

 /*
- * util_avg_cap = (cpu_scale - cfs_rq->avg.util_avg) / 2^n
- * where n denotes the nth task and cpu_scale the CPU capacity.
+ * util_avg_cap = (cpu_scale - cfs_rq->avg.util_avg) / 2
+ * where cpu_scale is the CPU capacity. Since cfs_rq->avg.util_avg
+ * accumulates the previously created tasks, applying this cap to each new
+ * task gives the nth task a util_avg_cap of cpu_scale / 2^n.
  *
  * For example, for a CPU with 1024 of capacity, a simplest series from
  * the beginning would be like:
  * ...
  */

Fixes: 2b8c41daba32 ("sched/fair: Fix initial util_avg calculation") 指明
这条坏注释最初是被 commit 2b8c41daba32 引入的,对应的 stable 标记逻辑
会让这个修复回溯到所有包含该 commit 的 stable 分支。

下面用一张图把"注释里误导性的公式 / 实际代码 / 真实累加结果"三条线索并排
放在一起,方便对比:

  task#   cfs_rq util_avg  comment says /2^n   code does /2   actual cap
  -----   ---------------  ------------------   -------------  ----------
     1          0           1024 / 2^1 = 512    1024 / 2 =512    512
     2        512           (1024-512)/2^2=128  (1024-512)/2=256  256
     3        768           (1024-768)/2^3=32   (1024-768)/2=128  128
     4        896           (1024-896)/2^4=8    (1024-896)/2=64    64

  -> comment "column 3" never matches code "column 4"
  -> series in "column 4" is 512,256,128,64... which equals cpu_scale/2^n
     only because cfs_rq->avg.util_avg keeps accumulating the previous caps.

代码 init_entity_runnable_average() 中真正发生的事是简单的"对剩余预算
除以 2",2^n 序列是累加的副产物:

  init_entity_runnable_average(se)
        |
        v
  util_avg = 0
        |
        v
  remaining = cpu_scale - cfs_rq->avg.util_avg
        |
        v
  cap = remaining / 2          <-- 注释里错写成 "/ 2^n"
        |
        v
  util_avg = min(util_avg, cap)
        |
        v
  cfs_rq->avg.util_avg += util_avg   <-- 累加到 rq 总量,下次任务用

类比

cpu_scale 想成一个固定大小的蛋糕(容量 1024),每个新任务进入 cfs_rq
时只能分走剩余蛋糕的一半——第一个任务分走 512,剩下 512;
第二个任务分走 256,剩下 256;以此类推。

并不是每个任务一次性就把蛋糕切成 1/2、1/4、1/8……而是"分一次、剩一次、
下次再分剩下的"。注释里直接写"第 n 个任务拿到 1/2^n"是结果对、过程错——
就像直接告诉别人"第 n 个月存 1/2^n 元"听起来像是每个月按指数缩减,
但其实是把每月剩余金额再砍一半、累加起来才得到这个序列。

Highlight:风险与注意点

  • 公式误导而非行为 bug:代码本身没有问题,只是注释错误;不影响调度行为,
    但维护者在该区域附近做新特性(例如动态 cap、per-CPU 上限调整)时可能被
    错误的 /2^n 公式带偏。修注释比修代码更便宜,但是个非常合理的提交。
  • Fixes 标签会触发 stable 回溯:因为指向了被多个 stable 系列吸收的
    commit,maintainer 通常会要求带上 Cc: stable@vger.kernel.org 显式抄送,
    并确认 subject 前缀使用 sched/fair: 而非手写编号。
  • 可能的扩展讨论点:是否同时把那段示例表也补全(标注第 1、2、3…… 个
    任务的实际数值),或者把"/2^n"作为注释里唯一出现的位置统一替换;
    这只是 polish,不是必须。

一句话总结

这是一条仅修注释的 patch:把 fair.cutil_avg_cap 公式从错误的
/2^n 改成与代码一致的 /2,并补一句说明 2^n 序列来自逐次对剩余预算
减半的累积行为。