sched discussion
[PATCH] sched/topology: Do not override sd->shared allocation
LLM 分析
sched/topology:避免覆盖 sd->shared 分配
系列概况
- 标题:
[PATCH] sched/topology: Do not override sd->shared allocation - 作者:K Prateek Nayak kprateek.nayak@amd.com
- 版本:v1(单封 patch,无 series index/total)
- 规模:1 个文件,+9 / -1
- 修改文件:
kernel/sched/topology.c - 代码统计:9 行新增,1 行删除
- Message-ID:
20260713065625.3572-1-kprateek.nayak@amd.com - 完整性:完整(包含 commit message、diff hunk、签名行、base-commit
04998aa54848f15332202d0bea008d2ca1ed1713);线程中第二封为同一作者对原 patch 的回复,仅引用前文,无新内容
补丁目的
修复 Breno Leitao 在一台「非对称拓扑 + 单一 LLC」平台上用 kmemleak 抓到的 sd->shared 内存泄漏。
根因是上游 commit 9e005ed21152d("sched/topology: Allow multiple domains to claim sched_domain_shared")。该 commit 让 init_sched_domain_shared() 在 sd_llc 路径上无条件分配并赋值 sd->shared。当 SD_ASYM_CPUCAPACITY 与 SD_SHARE_LLC 这两个 flag 落到 同一个 sched_domain 上时,第二次进入函数会把第一次分配好的指针直接覆盖掉,导致第一份 sched_domain_shared 结构丢失唯一引用,kmemleak 在系统关闭/拓扑退化阶段找不到配对的释放。
顺手修一处注释拼写错误:"avaialable" -> "available"。
旧流程的问题
init_sched_domain_shared() 在同一 sched_domain 上被两个不同 flag 调用时,第二次调用会改写 sd->shared:
[BEFORE PATCH - LEAK SCENARIO]
flag = SD_ASYM_CPUCAPACITY --> enters init_sched_domain_shared()
|
v
sds_A = alloc_sched_domain_shared()
sd->shared = sds_A (refcount=1)
flag = SD_SHARE_LLC --> enters init_sched_domain_shared() again (same sched_domain) |
v
sds_B = alloc_sched_domain_shared()
sd->shared = sds_B (refcount=1)
degenerate path:
refcount_dec(&sds_A->ref) --> only -1, but sd->shared now points to sds_B
refcount_dec(&sds_B->ref) --> reaches0, freed sds_A NEVER FREED --> kmemleak hits
关键不对称点:degenerate(退化)路径只对 当前 sd->shared 指向的那一份 refcount 减 1,前面被覆盖掉的那一份既无任何引用计数能配对,也不在退化列表里。系统越复杂、flag 越多,重叠命中的概率越高,泄漏规模按 num_overlaps - 1 累加。
新流程
入口加一段 short-circuit:若 sd->shared 已经被本 domain level 记过账(无论来自哪个 flag),直接 return,不再分配也不动 refcount。
[AFTER PATCH - FIXED FLOW]
init_sched_domain_shared(d, sd, flags)
|
v
+----+----+
| | sd->shared != NULL ? |
+---+------+
| \
yes no
| \
v v
return compute sds (llc / asym path)
|
v
if (WARN_ON_ONCE(!sd->shared))
sd->shared = sds; (defensive, normal path won't hit)
|
v
refcount_set(&sd->shared->ref, refs)
degenerate path:
refcount_dec(&sd->shared->ref) --> -1, paired with the single inc no leak
语义约束:每个 sched_domain level 只允许一次 sched_domain_shared 分配 + 一次 refcount inc。即使多个 flag 都命中同一 domain 也只记一次账,degenerate 路径的 -1 即可平衡。
关键实现
补丁只动 kernel/sched/topology.c 中 init_sched_domain_shared() 入口与一处注释:
@@ -2942,6 +2942,14 @@ init_sched_domain_shared(struct s_data *d,
struct sched_domain *sd, int flags)
+ /*
+ * sd->shared is already assigned for this domain level for a
+ * different flag. Nothing to do since this allocation is
+ * already accounted for.
+ */
+ if (sd->shared)
+ return;
+
@@ -2970,7 +2978,7 @@ init_sched_domain_shared(struct s_data *d,
struct sched_domain *sd, int flags)
- * CPU in the span if none are avaialable.
+ * CPU in the span if none are available.
注意点:
- 早返回放在函数最前面,不影响后续
__find_idlest_group、build_sched_domain等无关路径。 - 不分配
sds也不动 refcount,与「每个 domain level 记一次账」语义一致。 - 保留了原有的
if (WARN_ON_ONCE(!sd->shared)) sd->shared = sds;兜底,作为 defensive 检查,正常路径不会再触发。
类比
把 sched_domain_shared 想象成公寓楼里的 电梯控制箱钥匙。每层楼(sched_domain level)共用一个控制箱,但可能有多种「住户类型」(SD_ASYM_CPUCAPACITY、SD_SHARE_LLC …)都要登记使用权。
-第一次登记(SD_ASYM_CPUCAPACITY):物业去仓库领一把钥匙 k1,登记到该层台账 sd->shared = k1,refcount 记 1。
- 第二次登记(SD_SHARE_LLC):物业 没查台账 又跑去仓库领了
k2,把台账覆盖成sd->shared = k2,k1没人领走也无人归还。退房时物业只对台账上的钥匙减一次借用次数,k1永远压在仓库角落 —— kmemleak 就是巡逻保安捡到的那把没人领的钥匙。
加 patch 之后,物业在领钥匙之前先扫一眼台账:发现已经有登记,直接把新住户绑定到同一条锁名下,不再去仓库多领一把。这样退房时只需还一次,台账和仓库始终对得上。
Highlight:风险与注意点
- 触发条件罕见。常规对称 SMP 服务器上
SD_ASYM_CPUCAPACITY不会与SD_SHARE_LLC重叠,复现链路需要 ARM big.LITTLE / AMD CCD-asym 等非对称 + 单 LLC 拓扑,普通 CI 容易漏覆盖,建议在 LKP/QA 上挂长期回归。 - 不变式收紧。本 patch 维持「每 domain level 一次 alloc、一次 ref inc」语义。若后续
init_sched_domain_shared之外(如 CPU hotplug 重新构建拓扑)出现新的sd->shared赋值点,必须保证仍走 early-return 路径或显式复用,否则泄漏会以更隐蔽的形式回归。 - 冗余 defensive 检查
WARN_ON_ONCE(!sd->shared)仍保留,但正常路径已经被早返回吃掉,仅作为代码重构时的安全网;review 时可以讨论是否需要进一步删减。 - 顺手修了拼写,与 bugfix 无关。常规 maintainer 会一并接受;若偏好严格 1-改动-1-目的,可以拆成两个 commit。
- 后续可观察点:复跑 Breno 的拓扑用例,确认 kmemleak 报告归零;并检查
sched_domain_shared的 refcount 在 degenerate 前后始终匹配。
版本变化
仅 v1,无 v2→vN 演进。线程第二封邮件是同一作者的回复,仅引用原 commit message 与 diff 开头,不含新评审意见或代码改动。
与其他相关 patch 系列的关联
- 根因 patch:
9e005ed21152d("sched/topology: Allow multiple domains to claim sched_domain_shared")放宽了init_sched_domain_shared的复用规则,本次 patch 是该特性落地后的 correctness 二次手术,典型「先放开、再补护栏」的两段式落地。 - 相关结构:
sched_domain_shared共享模型同时影响 SDTL/SDSZ 对齐计算与sched_group的退化逻辑。后续如发现refcount在sched_domain_build_siblings/build_sched_domain中失衡,可对照本 patch 的「一次 inc、一次 dec」语义排查。 - 与 Reviewed-by标签协作:本 patch 已带
Tested-by: Breno Leitao与Tested-by: Dietmar Eggemann,maintainer(如 Ingo / Peter / Vincent)通常会要求至少一个 sched维护者的 Reviewed-by 才合入 v6.x。
一句话总结
在 init_sched_domain_shared() 入口加 sd->shared 非空判断早返回,避免 SD_ASYM_CPUCAPACITY 与 SD_SHARE_LLC 落到同一 sched_domain 时后一次覆盖前一次的 sched_domain_shared 造成 kmemleak,并顺手修一处注释拼写。