0/4 已展开

LLM 分析

sched/core:补回 idle_stamp 守卫,避免 rq->avg_idle 被推满 clamp

系列概况

  • 标题:[PATCH v2] sched/core: Skip rq->avg_idle update without a valid idle_stamp
  • 作者:Shubhang Kaushik (Ampere) sh@gentwo.org
  • 版本:v2,2026-08-06
  • 规模:1 file changed, 8 insertions(+), 2 deletions(-)
  • 修改文件kernel/sched/core.c
  • 代码统计update_rq_avg_idle() 入口加 idle_stamp 守卫,delta / max 仅在 stamp 非零时计算
  • Message-ID(首封)20260806-master-v2-1-e1f3a1a0c903@gentwo.org
  • 完整性:thread 共 4 封邮件;index 2(John Stultz)与 index 4(Shubhang)的正文在 lore 抓取中被截断,但 index 3(Zhan Xusheng)给出完整技术评论,thread 立场足以还原。

补丁目的

修复 commit 4b603f1551a73 ("sched: Update rq->avg_idle when a task is moved to an idle CPU") 把 rq->avg_idle 记账搬出 wakeup 路径后丢失的 rq->idle_stamp 合法性检查。

新 helper update_rq_avg_idle() 无条件执行 rq_clock(rq) - rq->idle_stamp;当 stamp 为 0 时,样本退化为 rq_clock(rq) 这种极大值,avg_idle 立刻被推到 2 * max_idle_balance_cost 的 clamp,让 load balancer 误判"这台 CPU 可以睡很久"。

旧流程的问题

  • ttwu_do_activate() 在更新前用 if (rq->idle_stamp) 守住:stamp 为 0 直接跳过更新,结尾的 rq->idle_stamp = 0 是 no-op。
  • 4b603f1551a73 改造后,put_prev_task_idle()update_rq_avg_idle(rq) 链路不再有 stamp 校验。
  • 多条路径会让 rq 进入 idle 时 idle_stamp 仍为 0:find_proxy_task()force-idlingsched_core_enabled(rq) 提前 return)、sched_balance_newidle()ttwu_pending 早返回。

新流程

update_rq_avg_idle() 入口先把 idle_stamp 读出来,为 0 直接返回,跳过 delta 计算、平均值更新与 clamp;末尾的 rq->idle_stamp = 0 自然也走不到。

Patch 概览

单文件 diff,集中在 ttwu_do_wakeup() 里对 update_rq_avg_idle(rq) 调用前的局部变量重排,无跨函数改动。

关键实现

@@ static inline void ttwu_do_wakeup(struct task_struct *p)
-       u64 delta = rq_clock(rq) - rq->idle_stamp;
-       u64 max = 2*rq->max_idle_balance_cost;
+       u64 idle_stamp = rq->idle_stamp;
+       u64 delta, max;
+
+       if (unlikely(!idle_stamp))
+               return;
+
+       delta = rq_clock(rq) - idle_stamp;
+       max = 2 * rq->max_idle_balance_cost;

调用关系与 stamp=0 入口:

put_prev_task_idle()
        |
        v
update_rq_avg_idle(rq)   <-- now has idle_stamp guard
        |
        v
update_avg(); clamp; rq->idle_stamp = 0

stamp=0 入口与真正高频触发点:

                +----------------------------+
                |   pick_next_task_fair()    |
                |  idle:                     |
                +-------------+--------------+
                              |
              +---------------+----------------+
              |                                |
   sched_core_enabled(rq)               sched_balance_newidle()
        return  (force-idle)                  |
              |                  +------------+------------+
              v                  |                         |
   rq goes idle,              ttwu_pending?              this_rq->idle_stamp
   idle_stamp not refreshed           |                         = rq_clock(this_rq)
              |              early return 0            (before ttwu_pending
              |                  |                     early return)
              v                  v
       put_prev_task_idle() -> update_rq_avg_idle(rq)
       needs guard for stamp==0

类比

rq->avg_idle 想成一块"待机时长统计表",它依赖上一次进入 idle 时写入的时间戳 idle_stamp

  • 正常路径:CPU 每次进 idle,管家 newidle_balance() 都会先在表上盖章,记下进入时刻;出 idle 时用"现在时刻 − 盖章"得到真实待机时长。
  • 异常路径(stamp=0):全新上电、或被 ttwu_pending 早返回这种"还没盖章就被叫醒"的回路跳过盖章;老电表原本有 if (timestamp) 这条"没戳就不算"的安全带,新电表把安全带摘了,于是第一次上电就会算出"今天 − 0"这种天文数字,骗负载均衡器说"这户可以睡很久"。
  • 本 patch 就是把那条 if (timestamp) 安全带重新系回 update_rq_avg_idle(),与原始 ttwu_do_activate() 里的 if (rq->idle_stamp) 是同一招。

Highlight:风险与注意点

  • 真正高频触发路径:Zhan 指出 sched_balance_newidle()if (this_rq->ttwu_pending) return 0; 早返回位于 this_rq->idle_stamp = rq_clock(this_rq); 之前,这才是 hackbench 这种 wakeup-heavy 负载必踩的入口;而 changelog 里举例的 find_proxy_task()force-idling 需要 proxy exec 或 CONFIG_SCHED_CORE,不应被当作 root cause。建议 v3 在 changelog 中点名 ttwu_pending,让"avg_idle clamp"问题描述变成与配置无关。
  • 单调用点影响面update_rq_avg_idle() 只有一个调用方 put_prev_task_idle(),补完守卫后行为空间收回到 4b603f1551a73 之前,副作用面小。
  • 回归覆盖:建议补 TRACE_EVENT(sched_update_rq_avg_idle_skip) 或 sched selftest 校验 stamp=0 时确实被跳过,避免再次被重构"优化"掉。
  • 审阅链:v2 已加 K Prateek Nayak 的 Reviewed-by,并 Cc John Stultz;v1 → v2 实质差异在 commit message 文案与 trigger 点名。

版本变化

  • v1 → v2:加入 Reviewed-by:K Prateek Nayak;commit message 显式举例 find_proxy_task()force-idling;Cc John Stultz;明示是 earlier proposal 的 narrower variant,避免触碰 set_next_task_idle() 的 stamp 行为。
  • review 反馈(Zhan Xusheng):把 stamp=0 真正高频触发点定位到 sched_balance_newidle()ttwu_pending 早返回,建议 v3 在 changelog 中补一句;并指出 fix 本身与 4b603f1551a7 移除的旧 if (rq->idle_stamp) 守卫等价,省略结尾 rq->idle_stamp = 0 是 no-op。

一句话总结

在 commit 4b603f1551a73rq->avg_idle 记账搬到 put_prev_task_idle() 之后,把丢失的 idle_stamp 合法性检查补回 update_rq_avg_idle(),避免 stamp=0 时把 avg_idle 一脚踩满 clamp。