sched discussion
[PATCH] sched/fair: Only apply cpufreq pressure where frequency is invariant
LLM 分析
sched/fair: Only apply cpufreq pressure where frequency is invariant
系列概况
- 标题:
[PATCH] sched/fair: Only apply cpufreq pressure where frequency is invariant - 作者: Jianyong Wu(
wujianyong@hygon.cn,后续通过jianyong.wu@outlook.com与jong wu继续回复) - 版本: v1(单 patch;thread 中作者重写了 commit message 的解释,未发布 v2 文本)
- 规模: 1 个 patch,13 封邮件(1 patch + 12 review/reply)
- 修改文件:
kernel/sched/fair.c(get_actual_cpu_capacity()) - 代码统计: 1 file changed, 9 insertions(+), 2 deletions(-)
- Message-ID:
20260821073927.455475-1-wujianyong@hygon.cn - 完整性: 完整(patch → Vincent/Hongyan 质疑 → 作者承认 v1 解释错误、改写根因 → 多人追问 boost 与
policy->max关系 → K Prateek Nayak 给出 cpufreq 层替代修复方案)
补丁目的
get_actual_cpu_capacity() 一直无条件把 cpufreq pressure 折进 capacity:
capacity -= max(hw_load_avg(cpu_rq(cpu)), cpufreq_get_pressure(cpu));
这种扣减只在架构声明 frequency invariant(utilization 已按当前频率同步缩放)时才正确。
在非 invariant 平台上,commit d2d5c129d07e("cpufreq: Make cpufreq_update_pressure() fall back to cpuinfo.max_freq")让 cpufreq_update_pressure() 即使没有真正硬件限频也输出正压力。结果 capacity 被持续压低,utilization 却仍按满频累计,繁忙 CPU 的 util 反超 capacity,调度器误判。
旧流程的问题
static inline unsigned long get_actual_cpu_capacity(int cpu)
{
unsigned long capacity = arch_scale_cpu_capacity(cpu);
capacity -= max(hw_load_avg(cpu_rq(cpu)), cpufreq_get_pressure(cpu));
return capacity;
}
- 默认语义"capacity 与 utilization 同基准缩放"只在
arch_scale_freq_invariant()为真时成立。 d2d5c129d07e把cpuinfo.max_freq作为arch_scale_freq_ref()的回退,让非 invariant 系统也开始出现正压力。- 作者在第 4 封回复里给出真正根因:在 x86 + acpi-cpufreq 上,
cpuinfo.max_freq含 autonomous boost 频率,policy->max解析为_PSS中最高可选项;开启 boost 时实测频率可超policy->max,因此policy->max < cpuinfo.max_freq并不代表存在有效硬件限频,回退假设失效。
新流程
static inline unsigned long get_actual_cpu_capacity(int cpu)
{
unsigned long capacity = arch_scale_cpu_capacity(cpu);
unsigned long pressure = hw_load_avg(cpu_rq(cpu));
/*
* Utilization only follows frequency where the architecture is
* frequency invariant. Elsewhere, lowering the capacity would
* scale one side of the comparison and not the other.
*/
if (arch_scale_freq_invariant())
pressure = max(pressure, cpufreq_get_pressure(cpu));
return capacity - pressure;
}
只在架构声明 frequency invariant 时才把 cpufreq pressure 合并进来;否则只用 hw_load_avg。
Patch 概览
| 文件 | 函数 | 关键改动 |
|---|---|---|
kernel/sched/fair.c | get_actual_cpu_capacity() | 引入 pressure 局部变量,用 arch_scale_freq_invariant() 守卫 cpufreq_get_pressure(cpu) 的合并 |
关键实现
- 入口点:
get_actual_cpu_capacity(int cpu),被select_task_rq_fair()、find_energy_efficient_cpu()、util_fits_cpu()等选核/能量路径调用。 - 关键判断:
arch_scale_freq_invariant()(arm64 默认 true,多数 x86 也 true,用于"util 已按当前 freq 缩放"语义)。 - 物理含义:util 与 capacity 必须用同一缩放基准;util 若始终以
SCHED_CAPACITY_SCALE表示满频工作量,就不能拿"当前 freq / max freq"反推 capacity。
ASCII 流程图
+---------------------------------------------------------------+
| OLD FLOW (non-invariant, e.g. x86 + acpi-cpufreq) |
+---------------------------------------------------------------+
| policy->max <- _PSS P-state cap |
| cpuinfo.max_freq <- includes boost frequency |
| | |
| v |
| cpufreq_update_pressure() |
| max_freq = cpuinfo.max_freq [after d2d5c129d07e] |
| capped_freq = policy->max |
| if (max_freq <= capped_freq) -> skip |
| -> else positive pressure |
| | |
| v |
| positive pressure (NO real HW cap, yet non-zero) |
| | |
| v |
| get_actual_cpu_capacity(): |
| capacity -= max(hw_load_avg, cpufreq_pressure) |
| | |
| v |
| util NOT scaled, capacity SHRUNK -> util > capacity -> BUG |
+---------------------------------------------------------------+
+---------------------------------------------------------------+
| NEW FLOW (this patch) |
+---------------------------------------------------------------+
| if (arch_scale_freq_invariant()) |
| true -> pressure = max(hw_load_avg, cpufreq_pressure) |
| [both sides scaled by freq -> OK] |
| false -> pressure = hw_load_avg |
| [drop cpufreq_pressure -> no asymmetric shrink] |
+---------------------------------------------------------------+
+---------------------------------------------------------------+
| REVIEW TIMELINE |
+---------------------------------------------------------------+
| m1 Jianyong : v1 patch (frequency-invariant explanation) |
| m2 Vincent : question / ask for clarification |
| m3 Hongyan : question / ask for clarification |
| m4 Jianyong : admits v1 message wrong; |
| root cause = cpuinfo.max_freq includes |
| boost frequency, != effective HW cap |
| m5-m11 reviews : discussion on cpuinfo.max_freq vs |
| policy->max semantics |
| m12-m13 Prateek : propose cpufreq-layer fix using |
| __resolve_freq() instead of |
| cpuinfo.max_freq fallback |
+---------------------------------------------------------------+
类比
调度器记两本账:util = 这辆车实际跑了多远;capacity = 这辆车油箱里的油。
旧逻辑说:"如果限速 70(cpufreq pressure),就把油量按 100/70 打折。"这只有当里程本身也是按"实际能跑的上限 70"来记的时候才自洽。
但 d2d5c129d07e 把"理论最大速度"(含 boost)当成了"限速 70"塞了进来——其实根本没限速,却被扣了油。结果里程 > 油量,明明跑了那么多路油却不够,调度器以为 CPU 已跑满。
补丁的做法:只有里程本来就是按当前频率记的(frequency invariant),才同步扣油;否则不要被这个假压力扣掉。
Highlight:风险与注意点
- 修在 sched 层只是"止血":真正不变量是
cpuinfo.max_freq的语义。Prateek 提议在cpufreq_update_pressure()把 fallback 从cpuinfo.max_freq换成__resolve_freq(policy, policy->cpuinfo.max_freq, policy->max, policy->min, ...),从源头消除伪压力;sched 层与 cpufreq 层两种思路需要 maintainer 取舍。 - commit message 误导:作者在第 4 封承认 v1 的"frequency invariant 不对称"叙述错误,真正根因是 boost 让
cpuinfo.max_freq不等于"硬件有效上限",存在 merging 时被要求重写提交说明的风险。 arch_scale_freq_invariant()覆盖面:arm64 默认 true、多数 x86 也 true,所以这个改动只在真正非 invariant 的少量平台上生效;非 invariant 平台把 cpufreq pressure 完全丢掉,会不会反过来低估 capacity,仍需实测。- 可能回归:
d2d5c129d07e的回退本意是给没有arch_scale_freq_ref()的平台一个合理估计;本 patch 在 sched 侧关闭它,相当于把问题推到 cpufreq 侧解决,需要 cpufreq 维护者(Viresh/Rafael)意见。 - 验证点:x86 + acpi-cpufreq、intel_pstate、arm64 + schedutil 上
util_fits_cpu()与 EAS 选核行为是否出现可见差异。
版本变化
- v1 提交说明(被作者自我否定):根因为 frequency invariant 不对称。代码仅 9 行新增。
- v1 修订说明(thread 内改写):根因改为
cpuinfo.max_freq含 boost 频率、与policy->max不可比。代码未变。 - 截至 thread 末尾未见 v2 patch 文本,但 Prateek 的
__resolve_freq(...)提议暗示 maintainer 倾向在 cpufreq 层修,作者下一步可能发 v2 把 fallback 替换。
与其他相关 patch 系列的关联
d2d5c129d07e("cpufreq: Make cpufreq_update_pressure() fall back to cpuinfo.max_freq"):本 patchFixes:指向的引入 commit。cpufreq_update_pressure()与arch_scale_freq_ref()、arch_scale_freq_invariant()的语义需要 cpufreq + sched 双方共同维护;Prateek 提议的__resolve_freq(...)改动落在drivers/cpufreq/cpufreq.c,跨子系统讨论。find_energy_efficient_cpu()、select_task_rq_fair()、util_fits_cpu()都间接消费get_actual_cpu_capacity(),是回归测试要覆盖的入口。
一句话总结
cpufreq 回退到 cpuinfo.max_freq 后在非 frequency-invariant 平台上引入虚假压力,让 capacity 被扣而 util 未缩放;本 patch 用 arch_scale_freq_invariant() 守卫 cpufreq_get_pressure() 的合并,恢复 capacity/utilization 的对称缩放。