0/5 已展开

LLM 分析

sched_ext: Rename the cid-form cgroup ops to cpuctl_*

系列基线信息

字段内容
标题sched_ext: Rename the cid-form cgroup ops to cpuctl_*
作者Tejun Heo (tj@kernel.org)
版本无版本号标记,单次提交
规模2 patches,6 files,+66 −53
Message-ID20260718101029.725350-1-tj@kernel.org
来源sched-ext 频道
基线分支sched_ext/for-7.3 (7c2cd767705d) + cgroup migration patchset

明确目的

sched_ext 的 cid 形式(struct sched_ext_ops_cid)中,"cgroup" 这个名字承载了两层含义:

  1. 子调度器(sub-scheduler)挂载到 cgroup——这是 cgroup2 层级的概念;
  2. cgroup_*() 回调 ops 传递的是 cpu controller 事件——这是 cgroup cpu 控制器的概念。

两个概念并不相同,但 ops 名字 cgroup_* 暗示的是 cgroup2 层级,实际操作的是 cpu controller。

cid 形式目前只有 scx_qmap 一个用户,ABI 还能改。因此趁此窗口期把 cgroup_* 重命名为 cpuctl_*,让名字准确反映实际语义。

cpu 形式(struct sched_ext_ops)已是 deployed ABI,保持旧名不变。


遍历代码

Patch 1/2:添加 SCX_OPS_CID_OPEN

问题背景SCX_OPS_OPEN() 在打开 skeleton 时会对缺失的 ops 字段做 load-time compat fix-up(清零)。这些 fix-up 通过硬编码引用 sched_ext_ops(cpu 形式)的成员名来操作。cid 形式的 skeleton 之所以能编译通过,仅仅是因为两种 ops struct 当前把 cgroup ops 取了相同名字——而即将到来的重命名会让这个名字一致性消失,编译将失败。

此外,cid 形式晚于所有 compat-gated ops 的引入时间,根本不需要任何 fix-up。

改动逻辑

  1. SCX_OPS_OPEN() 里的 skeleton 打开公共路径提取为 __SCX_OPS_OPEN(__ops_name, __scx_name, __ops_struct) 内联宏:

    • 检查 dump 字段是否存在(版本兼容性检查);
    • 打开 skeleton;
    • 设置 hotplug_seq
    • 执行 SCX_ENUM_INIT
  2. SCX_OPS_OPEN() 调用 __SCX_OPS_OPEN(..., "sched_ext_ops"),然后继续做 compat 清零。

  3. 新增 SCX_OPS_CID_OPEN() 调用 __SCX_OPS_OPEN(..., "sched_ext_ops_cid"),跳过所有 compat 清零。

  4. scx_qmap 从 SCX_OPS_OPEN 切换到 SCX_OPS_CID_OPEN

// 新增的公共路径
#define __SCX_OPS_OPEN(__ops_name, __scx_name, __ops_struct) ({ \
    struct __scx_name *__oskel;                                 \
    SCX_BUG_ON(!__COMPAT_struct_has_field(__ops_struct, "dump"),\
        __ops_struct ".dump() missing, kernel too old?");        \
    __oskel = __scx_name##__open();                              \
    SCX_BUG_ON(!__oskel, "Could not open " #__scx_name);        \
    __oskel->struct_ops.__ops_name->hotplug_seq = scx_hotplug_seq(); \
    SCX_ENUM_INIT(__oskel);                                      \
    __oskel;                                                     \
})

// cid 形式只走公共路径,不做 compat fix-up
#define SCX_OPS_CID_OPEN(__ops_name, __scx_name) \
    __SCX_OPS_OPEN(__ops_name, __scx_name, "sched_ext_ops_cid")

Patch 2/2:重命名 cgroup_* → cpuctl_*

改动范围

  1. internal.hstruct sched_ext_ops_cid:8 个回调字段名全部改名:

    • cgroup_initcpuctl_init
    • cgroup_exitcpuctl_exit
    • cgroup_prep_movecpuctl_prep_move
    • cgroup_movecpuctl_move
    • cgroup_cancel_movecpuctl_cancel_move
    • cgroup_set_weightcpuctl_set_weight
    • cgroup_set_bandwidthcpuctl_set_bandwidth
    • cgroup_set_idlecpuctl_set_idle
  2. ext.c__bpf_ops_sched_ext_ops_cid:同名映射,实现函数名不变(如 sched_ext_ops__cgroup_init),只是 ops 字段名改了。

  3. ext.cCID_OFFSET_MATCH:确保重命名的 cid 字段与对应的 cpu 字段占据同一 layout slot:

    CID_OFFSET_MATCH(cgroup_init,    cpuctl_init);
    CID_OFFSET_MATCH(cgroup_set_bandwidth, cpuctl_set_bandwidth);
    // ...共 8 对
    
  4. scx_qmap.bpf.c:BPF 回调函数名从 qmap_cgroup_* 改为 qmap_cpuctl_*;ops 注册表里同步改名。

  5. scx_qmap.c / scx_qmap.h:注释和帮助文本里的 cgroup_init 改为 cpuctl_init

关键约束:layout 不变,内核通过 cpu 形式的 union 视图调用,实现函数名不变。


ASCII 流程图

┌──────────────────────────────────────────────────────┐
│            sched_ext ops 形式对比                     │
├─────────────────────┬────────────────────────────────┤
│  cpu 形式 (deployed │  cid 形式 (可变 ABI)           │
│  ABI, 不可改)       │                                │
│                     │                                │
│  cgroup_init        │  cpuctl_init    ← 重命名后     │
│  cgroup_exit        │  cpuctl_exit                   │
│  cgroup_prep_move   │  cpuctl_prep_move              │
│  cgroup_move        │  cpuctl_move                   │
│  cgroup_cancel_move │  cpuctl_cancel_move            │
│  cgroup_set_weight  │  cpuctl_set_weight             │
│  cgroup_set_bandwd  │  cpuctl_set_bandwidth          │
│  cgroup_set_idle    │  cpuctl_set_idle               │
├─────────────────────┴────────────────────────────────┤
│  CID_OFFSET_MATCH 保证两者在同一 slot                  │
│  内核通过 cpu-form union 视图调用 → 实现函数名不变      │
└──────────────────────────────────────────────────────┘

┌──── Skeleton 打开路径 ────────────────────────────┐
│                                                    │
│  SCX_OPS_OPEN()          SCX_OPS_CID_OPEN()        │
│  ├─ __SCX_OPS_OPEN()     ├─ __SCX_OPS_OPEN()       │
│  │  ├─ dump 检查         │  ├─ dump 检查           │
│  │  ├─ skeleton open     │  ├─ skeleton open       │
│  │  ├─ hotplug_seq       │  ├─ hotplug_seq         │
│  │  └─ SCX_ENUM_INIT     │  └─ SCX_ENUM_INIT       │
│  ├─ compat 清零 ← cpu名  │  └─ 直接返回 (无清零)   │
│  └─ 返回 __skel          │                          │
└────────────────────────────────────────────────────┘

概念类比

想象一座大楼有两套门牌系统——旧楼(已交付住户)和新楼(还在施工)。旧楼每层的会议室叫 "会议室A/B/C",新楼原想也叫同样的名字,但这些会议室其实是 "空调控制室",不是开会的。

现在新楼还没住户,趁这个机会把门牌从 "会议室" 换成 "空调控制室",名字终于和实际用途吻合了。旧楼的住户已经习惯了 "会议室A" 这个门牌,不能改——但两栋楼共享同一套管线(CID_OFFSET_MATCH),所以换门牌不影响管线走向。

SCX_OPS_CID_OPEN 就像是给新楼单独配了一把钥匙,不再需要走旧楼钥匙那一串"检查旧锁"的流程——因为新楼压根没有旧锁。


Highlight 突出问题

  1. ABI 窗口期有限:cid 形式目前只有 scx_qmap 一个用户,所以还能改。一旦更多调度器采用 cid 形式,重命名就不可逆了。此 patchset 是最后的机会。

  2. CID_OFFSET_MATCH 必须持续维护:重命名后,cid 字段名和 cpu 字段名不再一致,CID_OFFSET_MATCH 是保证两者在同一 layout slot 的唯一防线。未来新增字段或进一步重命名时,必须同步更新这些匹配声明,否则 union 视图调用会错位。

  3. SCX_OPS_CID_OPEN 不能混用:cid-form 调度器必须用 SCX_OPS_CID_OPEN,不能用 SCX_OPS_OPEN——否则 compat 清零会引用不存在的 cpu-form 成员名导致编译失败。这个区别在文档和代码注释里已说明,但未来新调度器作者容易误用。

  4. cpu 形式永久保留旧名struct sched_ext_ops 里的 cgroup_* 名字语义不准确但已成 deployed ABI,无法修正。这是历史包袱,后续代码阅读者需意识到 cpu 形式的 cgroup_* 实际操作的是 cpu controller。


版本演进

本系列无多版本迭代,首次提交即被 maintainer 应用。


与其他相关 patch 系列的关联

  • 依赖前置系列:cgroup migration patchset(cover letter 中引用了 20260718081727.582037-1-tj@kernel.org)。
  • 目标分支 sched_ext/for-7.3,属于 7.3 merge window 的准入改动。

一句话总结

趁 cid 形式 ABI 尚可变更,将 sched_ext_ops_cid 中语义不准确的 cgroup_* ops 重命名为 cpuctl_*,同时拆出 SCX_OPS_CID_OPEN 避免 cid skeleton 依赖 cpu-form 成员名的 compat 清零路径。