sched discussion
[PATCH RESEND] sched: Convert paravirt_steal to new static key APIs
LLM 分析
sched: paravirt_steal 静态键迁移到新 API:解读与跟进
系列概况
- 标题:[PATCH RESEND] sched: Convert paravirt_steal to new static key APIs
- 作者:Hongyan Xia hongyan.xia@transsion.com
- 版本:RESEND(先前 series 拆分后的单 patch 重投);已被 tip-bot2 合入 sched/core(commit 087b40fe5934e4b8a、a5576ebce9201e2a)
- 规模:10 文件,21 insertions(+),21 deletions(-),行级纯 API 重命名
- 修改文件:
- arch/arm64/kernel/paravirt.c
- arch/loongarch/kernel/paravirt.c
- arch/powerpc/platforms/pseries/setup.c
- arch/riscv/kernel/paravirt.c
- arch/x86/kernel/cpu/vmware.c
- arch/x86/kernel/kvm.c
- drivers/xen/time.c
- include/linux/sched/cputime.h
- kernel/sched/core.c
- kernel/sched/cputime.c
- 代码统计:21 +/21 -,全部为 1:1 API 替换
- Message-ID(首封):20260819081207.12150-1-hongyan.xia@transsion.com
- 完整性:thread 含 patch、IBM 回复(被截断)、两次 tip-bot2 通知;patch 主体完整,回复仅片段。
补丁目的
把两个全局 struct static_key —— paravirt_steal_enabled 与 paravirt_steal_rq_enabled —— 从内核早已废弃的 static_key_* 原语切换到新一代 DEFINE_STATIC_KEY_FALSE / static_branch_* API。
这是 Linux jump-label 体系长期清理工作的一部分:旧 static_key 类型只暴露 atomic_t,外部访问需手动 atomic_read,开关需 static_key_slow_inc/dec/enable/disable;新 API 通过 struct static_key_false 强化类型语义,把"未启用"作为编译期默认值,使 static_branch_unlikely() 在禁态下零成本分支,并在 enable 时由 jump label 路径直接重写指令。
旧流程的问题
旧 API 只剩"类型安全缺失、命名混淆"两项实际痛点:
static_key_slow_inc(¶virt_steal_enabled)是 generic 静态键,无_false/_true后缀,读者无法一眼看出默认状态。- 用户态读取
static_key_false()与新static_branch_unlikely()名字含义相同但类型不一致,混用容易在 hot path 引入误用。 - 维护者期望最终把所有
struct static_key一次性替换为带默认值的DEFINE_STATIC_KEY_*,这是 LWN 和内核邮件列表上发起多年的清理运动。
新流程
patch 中每个 hunk 都按"声明 -> 定义 -> 触发 -> 读取"四类同步替换:
declare: extern struct static_key xxx_enabled;
|
v DECLARE_STATIC_KEY_FALSE(xxx_enabled);
define: struct static_key xxx_enabled;
|
v
DEFINE_STATIC_KEY_FALSE(xxx_enabled);
toggle: static_key_slow_inc(&xxx_enabled);
|
v
static_branch_inc(&xxx_enabled);
read: if (static_key_false(&xxx_enabled))
|
v
if (static_branch_unlikely(&xxx_enabled))
Patch 概览
四个调用点(arm64、loongarch、powerpc/pseries、riscv 的 pv_time_init,以及 x86 vmware/kvm 和 xen time_setup_guest)把 static_key_slow_inc 换成 static_branch_inc;内核侧 core.c 与 cputime.c 把定义与 static_key_false 同步换成新 API;cputime.h 引入 <linux/jump_label.h> 改用 DECLARE_STATIC_KEY_FALSE。
关键实现
// include/linux/sched/cputime.h
- struct static_key;
- extern struct static_key paravirt_steal_enabled;
- extern struct static_key paravirt_steal_rq_enabled;
+ #include <linux/jump_label.h>
+ DECLARE_STATIC_KEY_FALSE(paravirt_steal_enabled);
+ DECLARE_STATIC_KEY_FALSE(paravirt_steal_rq_enabled);
// kernel/sched/core.c
- struct static_key paravirt_steal_rq_enabled;
+ DEFINE_STATIC_KEY_FALSE(paravirt_steal_rq_enabled);
// kernel/sched/cputime.c
- struct static_key paravirt_steal_enabled;
+ DEFINE_STATIC_KEY_FALSE(paravirt_steal_enabled);
- if (static_key_false(¶virt_steal_enabled))
+ if (static_branch_unlikely(¶virt_steal_enabled))
// 各 arch 的 pv_time_init / pSeries_setup_arch / vmware_cpu_down_prepare
- static_key_slow_inc(¶virt_steal_enabled);
+ static_branch_inc(¶virt_steal_enabled);
- static_key_slow_inc(¶virt_steal_rq_enabled);
+ static_branch_inc(¶virt_steal_rq_enabled);
DEFINE_STATIC_KEY_FALSE 在编译期把默认值设为 false,使 static_branch_unlikely() 在禁态下生成接近零开销的 fall-through;首次 static_branch_inc() 触发 jump label patching,从而原地改写为跳转。
调用栈(path)如下:
pv_time_init (arch/arm64|loongarch|riscv, x86 vmware/kvm, xen time)
|
+-- static_branch_inc(¶virt_steal_enabled)
|
+-- static_branch_inc(¶virt_steal_rq_enabled)
|
v
sched/core.c::update_rq_clock_task()
|
+-- static_branch_unlikely(¶virt_steal_rq_enabled) ?
paravirt_steal_clock(cpu_of(rq)) : native
类比
想象大楼电梯里"消防演习模式"的开关:旧 API 只有一枚通用钥匙,写代码的人要靠脑补记住"现在是关着的";新 API 则把开关明确分成"默认关(_FALSE)"和"默认开(_TRUE)"两种型号,箱体上印着大大的红绿字,谁来操作都不会按错。static_branch_unlikely 像在走廊里装的红外感应灯 —— 平时关着不耗电,启用后感应到人才亮;static_branch_inc 就是按下消防按钮的那一下,触发整层楼的线路切换。
更具体地,static_key_slow_inc 是手工扳道岔的老扳手,static_branch_inc 则是电动道岔控制器 —— 动作相同,但控制器会同步更新调度图和指示灯(jump label patching),让火车经过时自动走对轨道。
Highlight:风险与注意点
- patch 没有 functional change,但若漏改某个
static_key_false的读取点,仍会编译失败 —— 类型已经从struct static_key变成struct static_key_false,不匹配即报错。 cputime.h新引入<linux/jump_label.h>头依赖,需确认所有包含cputime.h的 TU 都能承受这个额外 include(jump_label.h 通常足够轻)。- 两个静态键默认都是
_FALSE:在 guest 真正启用 steal-time accounting 前,分支始终 fallback 到 native;这是正确语义,但要警惕未来若误用_TRUE会让 guest 路径提前执行,引发 #ifdef 死代码回归。 - tip-bot2 在两天内连发两条相同主题的 commit,可能是 maintainer 重打了同一 patch 两次或 topic branch rebase;如果后续 lore 上还出现第三个 tip 通知,需要确认是否合并分支冲突。
- 回复(IBM Shrikanth Hegde)body 被截断,无法判断是否给出新的 Tested-by / Reviewed-by,需要后续到 lore 全文核对。
版本变化
RESEND 这次只改动一件事:"把原 series 拆成多个独立 patch,不便于 review"。本次提交本身是单 patch,但被 tip-bot2 在不同 commit id(087b40fe、a5576ebce)下重发,提示 review 入口或 topic branch 重打。
与其他相关 patch 系列的关联
这是内核对 static_key_* 老 API 全面替换的子集,与 Ingo Molnar / Juergen Gross 多年来推动的 jump label 类型化工作同方向;同主题 series 通常按子系统拆分(cgroup、mm、sched、x86、arm64),本 patch 落在 sched + paravirt 子树。Juergen Gross 的 Acked-by 表示 Xen 维护侧已认可,说明这是跨 hypervisor(Xen、KVM、VMware、PowerVM、pseries)协同推进的清理。
一句话总结
把 paravirt steal-time 的两个全局静态键从废弃的 static_key_* API 一次性升级到类型化的 DEFINE_STATIC_KEY_FALSE / static_branch_*,跨 6 个 arch/xen 与 sched 核心无功能改动地完成 jump label 体系的类型统一。