sched discussion
[PATCH] sched/topology: Add a cpus_read_lock to partition_sched_domains()
LLM 分析
sched/topology: 给 partition_sched_domains() 加上 cpus_read_lock
系列概况
- 标题: [PATCH] sched/topology: Add a cpus_read_lock to partition_sched_domains()
- 作者: Sebastian Andrzej Siewior bigeasy@linutronix.de
- 版本: 单补丁 v1(无版本号,无 cover letter,单 patch)
- 规模:
kernel/sched/topology.c1 个文件,1 行新增 - 修改文件: kernel/sched/topology.c
- 代码统计: +1 / -0
- Message-ID: 20260812095800.gl06ANul@linutronix.de
- 完整性: 完整,包含 commit message、Fixes tag、Signed-off-by 和 diff
补丁目的
该 patch 修复一个真实的锁缺失问题:从 sysctl 接口读取 /proc/sys/kernel/sched_rt_runtime_us 时,会触发 sched_rt_handler() -> partition_sched_domains() -> sched_cache_set() -> static_key_enable_cpuslocked() 的调用链。
其中 _cpuslocked() 接口要求 CPU 热插拔锁已被持有,但 partition_sched_domains() 只拿了 sched_domains_mutex,没拿 cpus_read_lock(),因此会触发"missing cpu_hotplug_lock"的回溯告警。
修复方法是在 partition_sched_domains() 入口处加一个 guard(cpus_read_lock)(),与 sched_cache_active_set() 的锁获取顺序保持一致(hotplug lock 在 sched_domains_mutex 之前获取)。
旧流程的问题
userspace
|
v read /proc/sys/kernel/sched_rt_runtime_us
sched_rt_handler()
|
v
partition_sched_domains() <-- only sched_domains_mutex held
|
v
sched_cache_set()
|
v
static_key_enable_cpuslocked(&sched_cache_present) <-- needs cpus_read_lock!
|
v
WARN_ON(!cpus_read_lock_held()) -> backtrace printed
调用链中至少存在两路进入 sched_cache_set():
sched_init_domains()(boot 早期)—— 此时 user space 没起来、不会有 hotplug,所以没有告警。sched_rt_handler()(sysctl 写路径)—— 正常运行时会触发 hotplug 并发的告警。
新流程
userspace
|
v read /proc/sys/kernel/sched_rt_runtime_us
sched_rt_handler()
|
v
partition_sched_domains()
| + guard(cpus_read_lock)(); <-- NEW: acquire hotplug lock first
| sched_domains_mutex_lock(); <-- then take domain mutex
v
partition_sched_domains_locked()
|
v
sched_cache_set() -> static_key_enable_cpuslocked() <-- lock held, OK
锁顺序变成 cpus_read_lock -> sched_domains_mutex -> ... -> _cpuslocked(),与 sched_cache_active_set() 一致。
Patch 概览
- 单 patch,仅修改
kernel/sched/topology.c,1 行新增 - 引用 Fixes tag:
a7660ce1590fc ("sched/cache: Fix has_multi_llcs iff at least one partition has multiple LLCs"),指明引入该问题的 commit
关键实现
修改位于 kernel/sched/topology.c 的 partition_sched_domains() 函数(行 3498 附近):
void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[],
struct sched_domain_attr *dattr_new)
{
+ guard(cpus_read_lock)();
sched_domains_mutex_lock();
partition_sched_domains_locked(ndoms_new, doms_new, dattr_new);
sched_domains_mutex_unlock();
}
要点:
- 使用
guard()宏做作用域清理,函数返回时自动释放cpus_read_lock,无需手写cpus_read_unlock()。 - 放在
sched_domains_mutex_lock()之前,对齐"先 hotplug、再 domain mutex"的现有约定。
enter partition_sched_domains()
|
v guard(cpus_read_lock)() (RAII: acquire on enter)
partition_sched_domains_locked()
|
v
static_key_enable_cpuslocked() / static_key_disable_cpuslocked()
|
v WARN disappears
function returns -> cpus_read_unlock() auto via guard
类比
把 CPU hotplug 锁想成"商场大门的门禁":系统启动后大门只允许单向通行(CPU 进入/离开),任何对 CPU 拓扑的"改装"都必须先在大门刷一下卡。sched_domains_mutex 则是店内的"仓库钥匙",用来保护货架上的调度域数据。
旧实现:店员(sched_rt_handler)一进门直接去仓库取货架(调 partition_sched_domains),结果走到 sched_cache_set 这个货架时,店里规定必须先刷门禁卡,否则报警。
新实现:店员进仓库前先在大门刷一下卡(guard(cpus_read_lock)),再到仓库开锁取货(sched_domains_mutex),这样走到 _cpuslocked 货架时门禁卡已经在身上,警报消失。
Highlight:风险与注意点
- 放置位置的争议: Chen Yu 与 Tim Chen 都注意到
partition_sched_domains()函数上方有注释写 "Call with hotplug lock held"。这意味着历史上调用方应自行持有 hotplug 锁。Tim 指出CONFIG_CPUSETS=n时,rebuild_sched_domains()和cpuset_reset_sched_domains()都被 stub 为partition_sched_domains(1, NULL, NULL),不会主动拿cpus_read_lock。Sebastian 选择在partition_sched_domains()内补锁,虽然技术正确,但与既有"调用方负责"约定有偏离,后续维护者需要警惕"既是调用方负责又是 callee 保护"的双重语义。 - 锁嵌套顺序:
cpus_read_lock与sched_domains_mutex的相对顺序一旦确立,全栈都必须遵守,否则会有 AB-BA 死锁风险。当前 patch 选在sched_domains_mutex之前获取,需要检查所有调用partition_sched_domains()的栈是否仍持有更外层的锁。 - boot 早期路径:
sched_init_domains()也会调到partition_sched_domains_locked(),但走的是_locked()版本(已被外层函数保护),不会重复上锁,安全。 - reviewer 一致性建议: Tim 提议为
CONFIG_CPUSETS=n下的cpuset_reset_sched_domains()等 stub 同步加锁以保持一致;如果接受,会演变为多 patch 修复集。 - 后续观察: 是否会引发 v2,引入
rebuild_sched_domains()/cpuset_reset_sched_domains()锁补充;是否需要把 hotplug 锁下推到 callee 而不是调用方。
版本变化
- v1(当前):在
partition_sched_domains()入口直接guard(cpus_read_lock)(),最小改动修复告警。 - 后续潜在改动(讨论中):在
CONFIG_CPUSETS=n的 stubrebuild_sched_domains()/cpuset_reset_sched_domains()加锁;或在 callee 加锁的同时考虑移除"Call with hotplug lock held"的注释。
一句话总结
在 partition_sched_domains() 入口加 cpus_read_lock() 守卫,修复 sysctl 写路径触发"missing cpu_hotplug_lock"告警;reviewers 提示在 CONFIG_CPUSETS=n 的 stub 调用方也需保持锁顺序一致。
与其他相关 patch 系列的关联
本 patch 引用 Fixes: a7660ce1590fc —— sched/cache 的 multi-LLC 修复 commit,正是它让 sched_cache_set() 在 sysctl 路径上暴露了锁缺失;本 patch 是对该引入问题的针对性补丁。