0/1 已展开

LLM 分析

core-sched: 让 flip 等待 in-flight selection 完成

系列概况

  • 标题:FAILED: patch "[PATCH] sched/core: Make core-sched flips wait for in-flight" failed to apply to 6.6-stable tree
  • 作者:Greg Kroah-Hartman(stable 机器人回信);原始作者 Tejun Heo <tj@kernel.org>
  • 版本:单封 backport 失败通知;上游原始提交 f3629c63a4af3e491381780bc6c123cb498c4c40
  • 规模:1 封邮件,附带 1 个 4 文件的 diff(stable 6.6 上无法干净应用)
  • 修改文件kernel/sched/core.ckernel/sched/sched.h
  • 代码统计:core.c 约 +15 行,sched.h +1 行(新增字段 core_pick_in_flight
  • Message-ID2026090311-paramedic-jaybird-0ada@gregkh
  • 完整性:原始 patch 完整提供;stable 回放命令也给出,但因为 6.6 代码已偏移无法自动 cherry-pick

补丁目的

core scheduling 下,pick_next_task() 在共享的 core-wide rq lock 内为所有 SMT sibling 选下一个任务。但 ->pick_task() 调度类回调允许在选核过程中临时释放 rq lock。一旦 lock 被 drop,__sched_core_flip()(用于启用/关闭 core sched 的开关)就可能在 selection 中途完成,把 rq_lockp() 重新绑定到拆分后的 per-rq lock。

结果 selection 会在"分家后"的锁上继续操作兄弟 rq 的状态,而它已经不再持有对应的锁了;最后 __schedule() 释放一把根本没拿到的锁,并泄漏另一把真正拿到的锁。这是一个典型的锁序错乱导致的资源泄漏和状态损坏。本 patch 通过引入 core_pick_in_flight 计数器,让 flip 在仍有 in-flight 的 core-wide selection 时阻塞等待。

旧流程的问题

  1. Leader CPU 拿到 shared core lock,开始 pick_next_task()
  2. 某个 SMT sibling 的 ->pick_task() 因为 I/O、调度等原因 drop 了 rq lock。
  3. 此刻 __sched_core_flip(false) 在另一线程抢占成功,把 rq_lockp() 重新指向拆分后的 per-rq lock。
  4. 原来的 selection 在拆分锁上恢复执行,触摸兄弟状态却没有持有对应的核心锁。
  5. __schedule() 退出时还按"shared lock"释放——错位释放导致锁泄漏。

新流程

  • 在 leader rq 上维护 core_pick_in_flight
    • pick_next_task() 入口 ++,出口 --
    • __sched_core_flip() 在采样时若发现计数非零,drop shared lock、relax、重抢,直到 drain。
  • 因为计数器只在 shared core lock 下变化,flip 在持锁时采样,不需要额外的内存屏障或顺序规则。
  • flip 极少发生(cookie 生命周期事件),selection 也不会无限重叠,所以这种自旋等待代价可接受。
  • sched_core_cpu_deactivate() 在 leader 移交时移动而不是拷贝这个计数——否则将来该 CPU 重新成为自己 leader 时,旧计数会"永远偏置"。

Patch 概览

  1. kernel/sched/core.c__sched_core_flip():增加 while (core_pick_in_flight) 等待循环。
  2. kernel/sched/core.cpick_next_task() 头尾:core_pick_in_flight++ / --
  3. kernel/sched/core.csched_core_cpu_deactivate():转移计数到新 leader。
  4. kernel/sched/core.csched_init():初始化为 0。
  5. kernel/sched/sched.hstruct rq 新增 unsigned int core_pick_in_flight

关键实现

static void __sched_core_flip(bool enabled)
{
    ...
    while (cpu_rq(cpu)->core->core_pick_in_flight) {
        sched_core_unlock(cpu, &flags);
        cpu_relax();
        sched_core_lock(cpu, &flags);
    }
    ...
    for_each_cpu(t, smt_mask)
        cpu_rq(t)->core_enabled = enabled;
}
pick_next_task(struct rq *rq, struct rq_flags *rf)
{
    ...
    rq->core->core_pick_in_flight++;
    ...
    rq->core->core_pick_in_flight--;
}
sched_core_cpu_deactivate(unsigned int cpu)
{
    ...
    core_rq->core_pick_in_flight = rq->core->core_pick_in_flight;
    rq->core->core_pick_in_flight = 0;
}

类比

想象一栋写字楼的"全楼广播系统":保安主任(leader)要向所有楼层同时下达同一指令,必须等所有楼层经理(sibling)都签收完毕才算一次"完整广播"。

某天 3 楼的经理说"我去趟洗手间",暂时放下手中的签收单(drop rq lock)。这时物业总经理(flip)跑过来说"切换广播频道",于是 3 楼回来时听到的指令来自另一套系统——这位经理接下的指令和手里那份旧签收单对不上号,最后交回一把"根本没用过的钥匙"。

修法就是在大堂放一块白板,写着"当前正在广播 N",每次广播开始 +1、结束 -1;总经理想切频道时先看一眼白板,不为零就先去倒杯咖啡等广播结束。

Highlight:风险与注意点

  • stable 树冲突:6.6-stable 上的 kernel/sched/core.c 已经偏移,机器人无法自动 cherry-pick。需要 stable@vger.kernel.org 接收人工 backport,提交时附原始 commit id f3629c63a4af…
  • Fixes 标签:标记为 Fixes: 539f65125d20(v5.14+ 引入 core scheduling),所以理论上需要回灌到所有受影响的 stable/longterm 分支。
  • drop-lock 的合法窗口->pick_task() drop rq lock 是 EINTR/EAGAIN 友好的设计,本身没问题;问题是 flip 不能在这个窗口里把锁"换皮"。
  • hotplug 计数移动sched_core_cpu_deactivate() 用"move"语义而不是"copy",否则旧 leader 重新上线时计数偏差。
  • Acks:已被 Peter Zijlstra ack,社区认可度高,但仍需 maintainer 进一步合入。
+--------+      shared core lock      +-----------+
|  CPU A | <-------------------------> |  CPU A'   |
| leader |                             |  sibling  |
+--------+                             +-----------+
     |  pick_next_task begins              |
     |  core_pick_in_flight = 1            |
     |                                    v
     |  ->pick_task() drops rq_lock ---> temporarily unlocked
     |                                    ^
     |  __sched_core_flip() tries         |
     |  sees count != 0, backs off        |
     |  (unlock/relax/relock loop)        |
     v                                    |
     |  selection resumes under shared lock
     |  core_pick_in_flight = 0
     v
   flip proceeds, rebinds rq_lockp() safely

版本变化

本线程只是 stable backport 失败通知,没有 vN→vN+1 的多版本迭代。原始 patch f3629c63a4af 即最终形态,已并入 Linus 树。

一句话总结

通过 leader rq 上的 core_pick_in_flight 计数器,让 __sched_core_flip() 在仍有 core-wide selection 进行时阻塞等待,避免 rq_lockp() 在 drop-lock 窗口被错误地重绑定,从而修掉一个潜伏 5 年之久的 core-sched 锁泄漏与状态损坏隐患。