sched discussion
[REGRESSION] sched/idle: Sysbench threads regression after f4c31b07b136
LLM 分析
sched/idle:修复 no-cpuidle-driver 路径的 Sysbench 回归
系列概况
- 标题:线程主题为
[REGRESSION] sched/idle: Sysbench threads regression after f4c31b07b136;修复补丁为[PATCH] sched/idle: Stop the tick when no cpuidle driver is available。 - 作者:Joseph Salisbury 发起报告;Christian Loehle 提交修复;Rafael J. Wysocki 和 Zhan Xusheng 参与分析。
- 版本:没有可靠的内核版本号、vN 编号或目标发布周期。
- 规模:共 11 封邮件,包含 1 个单行修复 patch;测试覆盖 x86 与 Arm/Ampere 两种 OCI VM shape。
- 修改文件:
kernel/sched/idle.c。 - 代码统计:1 file changed,1 insertion(+),1 deletion(-)。
- Message-ID:线程首封为
096b42fa-107f-450d-b3b1-03bcad3f1e04@oracle.com;修复 patch 为0b9b9c3d-8ba0-4329-9504-b9d33c627649@arm.com。 - 完整性:输入包含回归报告、bisect 结果、revert 验证、根因推演和修复 patch;但多封回复只是截断引用,未包含最终评审结论、修复后完整测试数据或合入状态。
补丁目的
Oracle 在两个 OCI VM shape 上发现 MySQL Sysbench threads 性能可复现下降,bisect 将首个坏提交定位为 f4c31b07b136:
VM.Standard2.1:333 降至 236,下降 29.1%。VM.Standard.A1.Flex.2:1286 降至 1152,下降 10.4%。
回退该提交后性能恢复。修复 patch 收窄改动范围:仅让没有 cpuidle driver 的路径恢复无条件停止 tick;single-state cpuidle 路径继续使用 got_tick 启发式。
旧流程的问题
f4c31b07b136 之前,两条特殊路径的语义不同:
- 没有 cpuidle driver:调用
tick_nohz_idle_stop_tick(),然后进入default_idle_call()。 - 只有一个 cpuidle state:调用
tick_nohz_idle_retain_tick(),保留周期 tick。
问题并非旧代码本身,而是 f4c31b07b136 将两条路径统一到 idle_call_stop_or_retain_tick(got_tick)。在新的 idle episode 开始时,got_tick 通常为 false,使 no-driver guest 暂时保留周期 tick。
按 Zhan Xusheng 的推演,guest 因仍有约 1/HZ 的 pending timer,更容易被 hypervisor 唤醒和调度。该解释与测试幅度相关,但不能视为已经完全证实的唯一根因。
新流程
修复 patch 将 no-driver 分支改为直接调用 tick_nohz_idle_stop_tick(),恢复每次进入 idle 时都立即停 tick 的语义。存在 cpuidle driver 的路径不受该单行修复影响,single-state 路径仍保留 previous-wakeup 启发式。
+----------------------+ +--------------------------+
| no cpuidle driver | | cpuidle driver available |
+----------+-----------+ +------------+-------------+
| |
v v
+----------------------+ +--------------------------+
| stop tick now | | use previous got_tick |
+----------+-----------+ +------------+-------------+
| |
v v
+----------------------+ +--------------------------+
| default_idle_call | | enter configured idle |
+----------------------+ +--------------------------+
| |
+----------------+---------------+
|
v
+----------------------+
| idle loop continues |
+----------------------+
Patch 概览
修复来自 Christian Loehle,是原始回归线程的直接后续补丁。它没有回退整个 f4c31b07b136,而是恢复其中 no-cpuidle-driver 路径的旧行为。
补丁使用 Fixes: f4c31b07b136 标记回归来源,并使用 Reported-by 标明 Joseph Salisbury。线程没有提供其他可确认的相关 patch 系列。
关键实现
逻辑变化可以概括为:
if (cpuidle_not_available(drv, dev)) {
tick_nohz_idle_stop_tick();
default_idle_call();
goto exit_idle;
}
/* The single-state cpuidle path retains the got_tick heuristic. */
idle_call_stop_or_retain_tick(stop_tick);
关键步骤如下:
do_idle()在新 idle episode 开始时将got_tick重置为false。- 统一后的 helper 以
false进入时选择 retain tick 分支。 - 只有 tick 实际触发并令
got_tick变为true后,后续循环才更可能停止 tick。 - 修复直接调用
tick_nohz_idle_stop_tick(),不再等待这次got_tick状态转换。 - single-state cpuidle 路径继续保留启发式,修复没有扩大为完整回退。
new idle episode
|
v
got_tick = false
|
v
no-driver + unified heuristic ----> retain tick
| |
| v
| host sees timer soon
| |
| v
| more vCPU wakeups
|
v
direct tick_nohz_idle_stop_tick()
|
v
old no-driver behavior restored
类比
可以把 vCPU 想成值班室里的员工,把周期 tick 想成固定响铃闹钟:
- 旧流程允许员工每次休息时马上关闭闹钟,宿主便能暂时离开,减少无意义查看。
- 新启发式会参考员工上次为何醒来;第一次进入休息时信息不足,闹钟仍保持启用。
- hypervisor 看见闹钟很快又要响,就会更频繁地唤醒和安排这个 vCPU。
- 修复相当于为没有完整 idle 状态管理的值班室恢复特殊规则:先关闹钟,再让员工休息。
Highlight:风险与注意点
got_tick解释是线程中的合理根因推演,尚缺 idle-state、hypervisor 调度或 trace 数据来最终确认。- 邮件曾要求提供 guest 与 host 的 idle-state 列表,但当前输入没有这些信息。
- HZ 差异与回归幅度一致:回复称 x86 为 HZ=1000、退化 29%;Arm 为 HZ=250、退化 10%。这支持频率相关假设,但不单独构成证明。
- 修复只覆盖 no-driver 路径;single-state cpuidle 场景仍需继续观察。
- 缺少修复后的完整复测数据,patch 正文也明确表示作者仍好奇精确机制。
版本变化
线程没有 vN 编号,但语义变化清晰:
f4c31b07b136:统一 no-driver 与 single-state 两条特殊路径,引入 previous-tick-wakeup 启发式。- 后续修复 patch:仅恢复 no-driver 路径的无条件停 tick 行为,保留 single-state 路径的启发式。
- 尚未提供 v2、最终测试结果或合入状态。
一句话总结
f4c31b07b136 将 previous-wakeup 启发式带入无 cpuidle driver 的 guest idle 路径,导致 tick 保留并可能增加 vCPU 唤醒开销;一行修复恢复了该路径原有的立即停 tick 行为。