sched discussion
[syzbot] [mm?] BUG: sleeping function called from invalid context in kvm_mmu_notifier_invalidate_range_start
LLM 分析
Linux Scheduler / MM:OOM reaper 在持有 mmap_lock 时命中 sleeping-in-invalid-context
系列概况
- 标题:
[syzbot] [mm?] BUG: sleeping function called from invalid context in kvm_mmu_notifier_invalidate_range_start - 作者:syzbot(首报与转报)、Sean Christopherson(KVM 维护者)、David Woodhouse(dwmw2,邮件归档/转发者)
- 版本:本线程没有正式的 patch series 版本号,但配套的代码改动在
kernel.h/sched.h里给__might_resched()增加了一个bool rt_sleeping_lock参数 - 规模:5 封邮件,1 份未签名的 diff 片段(围绕
__might_resched签名扩展);非典型 patch series - 修改文件:从邮件 4 的 diff 片段可见,主要是
include/linux/kernel.h和include/linux/sched.h,并影响所有cond_resched*()宏 - 代码统计:diff 中可见约 6 处函数/宏签名变更,每处只增减一两行,纯签名扩展,无新增逻辑分支
- Message-ID:首封
6936812a.a70a0220.38f243.0090.GAE@google.com;最后一封6a8d8cc9.a5a502ce.31d34.0067.GAE@google.com - 完整性:缺正式 patch cover letter、缺 Signed-off-by、缺 Fixes tag;最后一封只是
#syz test: seb-nonblock-rt-test的测试请求,不能算完整闭环
补丁目的
目的是修复 OOM reaper 在持有 mmap_lock(reader 锁,标 ++++-{4:4})时调用 cond_resched() / __might_resched() 被锁检测标记为"非法上下文休眠"的问题。dump 里的栈关键路径是:
oom_reaper/39
-> mmap_read_trylock
-> oom_reap_task_mm (mm/oom_kill.c)
-> ... -> cond_resched()
-> __might_resched() triggers BUG at spinlock_rt.c:48
症结不在 mmap_lock 本身,而在 oom_reaper 路径上同时使用 RT-mutex 化的 spinlock(rt_spin_lock),所以 sched/RT 锁检测把这次 might_sleep 当成了非法的"在原子上下文里 sleep"。补丁想给 __might_resched() 引入一个 rt_sleeping_lock 形参,让调用方明确告诉锁检测"这次睡眠发生在 RT sleeping lock 上,不是普通的 sleeping-in-atomic"。
旧流程的问题
- 在 PREEMPT_RT 配置下,
mmap_lock之类的 reader lock 被 rtmutex 化,进入临界区后依然可能触发cond_resched()。 - 旧的
__might_resched()只看preempt_count与 RCU 嵌套深度,无法区分"持普通 spinlock"和"持 RT sleeping lock",一看到preempt_count=0就直接 BUG。 - 这让 oom_reaper 在 RT 配置下不可用:它必须
cond_resched()让出 CPU,但又会被锁检测误判。
新流程
__might_resched() 增加一个 bool rt_sleeping_lock 形参:
extern void __might_resched(const char *file, int line,
unsigned int offsets,
bool rt_sleeping_lock);
所有 cond_resched*() 宏调用点都传入 false:
#define cond_resched() ({ \
__might_resched(__FILE__, __LINE__, 0, false); \
_cond_resched(); \
})
未来由 KVM / MM 路径在持 RT sleeping lock 的地方传入 true,让锁检测放过这一次 might_sleep,从而允许 oom_reap_task_mm() 在持有 reader mmap_lock 时正常触发调度。
Patch 概览
邮件 4 给出的核心 diff 形态:
include/linux/kernel.h
__might_resched() new 4th parameter rt_sleeping_lock
__might_resched_no_domains() same new parameter
include/linux/sched.h
cond_resched() pass false
cond_resched_lock(lock) pass false
cond_resched_rwlock_read() pass false
cond_resched_rwlock_write() pass false
... other cond_resched*() macros updated likewise
这个 patch 是签名扩展 + 默认 false 的"准备阶段",真正的修要靠后续由 KVM 路径在合适调用点传入 true 完成。
关键实现
调用链解读:
cond_resched_lock(lock) -- reader mmap_lock path -->
__might_resched(..., false)
--> lockdep / RT sleeping_lock detection
rt=false -> behavior equals the old implementation
rt=true -> skip the invalid-context warning
_cond_resched() -- real yielding point
OOM reaper 在 RT 下需要进入 oom_reap_task_mm() 时既可能 spin 也可能 schedule,所以必须让 cond_resched_lock(lock) 在 rt_sleeping_lock=true 的语义下被允许。
整体对象关系:
+----------------------+ +----------------------------+
| cond_resched*() | call | __might_resched() |
| macros in sched.h |-------->| + new param: |
| pass rt=false | | bool rt_sleeping_lock |
+----------------------+ +----------------------------+
|
| false
v
+-------------+
| warn / BUG | (legacy path)
+-------------+
^
| true
+-------------+
| skip BUG | (RT sleeping lock path)
+-------------+
类比
想象银行柜台:reader lock 相当于"取号等待",多个客户可以同时在大厅里坐着等叫号;普通 spinlock 相当于"柜台员工锁住工位不让换人",员工绝不能跑去洗手间。而 oom_reaper 是银行大堂经理,他要处理"长时间挂号的客户"必须让自己也能去趟洗手间。旧锁检测只要看到"员工还在工位上"就报警,根本不问这次是不是"大堂经理去洗手间"。新参数相当于给大堂经理配了一张特殊工牌,锁检测认到这张工牌就知道"这次睡眠是合理的"。
再换一个比喻:红绿灯原本只判断"有没有车在路口",现在新增一个"是不是应急车辆"的标志,应急车辆闯灯会被放行。rt_sleeping_lock=true 就是那张应急标志。
Highlight:风险与注意点
- 签名 ABI 风险:
__might_resched()是内核里被各模块广泛调用的内部 API,扩展参数会影响所有调用点。邮件 4 的 diff 已经把已知宏都同步成false,但 out-of-tree 模块会编译失败。 - 过度放行的可能:一旦
rt_sleeping_lock=true的调用面扩大,要确保传入true的位置真的只发生在 RT sleeping lock 临界区,否则又把原本应当告警的真实 bug 吞掉。 - oom_reaper 死锁风险:oom_reaper 在持
mmap_lock时让出调度,若持锁时间过长,可能拖慢其它需要 mmap_lock writer 的路径;补丁只解决"被误报 BUG",不解决"持锁太久"。 - 复现路径需要持续跟踪:邮件 2 给出
repro.syz,dwmw2 在邮件 5 发起#syz test: seb-nonblock-rt-test,说明问题需要在 RT 内核 + 非阻塞 mmap 路径上验证;后续是否合入 mainline 取决于该测试结果。
版本变化
本线程本身没有 patch series vN→vN+1 的演进,可观察到的版本变化是:
- 首报(2025-12-08):仅发现 BUG,无 reproducer,HEAD
37bb2e7217b0(6.19 合并窗口)。 - 邮件 2(2026-05-04):syzbot 在 linux-next(
b9303e6bff70,2026-04-30 snapshot)上找到 reproducer。 - 邮件 3(2026-05-06):Sean Christopherson 回复(KVM 角度介入)。
- 邮件 4(2026-08-21):转发了一份扩展
__might_resched签名的 diff,是修复的前置准备。 - 邮件 5(2026-08-25):dwmw2 转发
#syz test: seb-nonblock-rt-test请求,测试补丁后的行为。
一句话总结
OOM reaper 在 RT 配置下因为持有 rtmutex 化的 reader lock 又调用 cond_resched(),被 __might_resched() 误判为在非法上下文睡眠;补丁给 __might_resched() 增加 rt_sleeping_lock 形参,让合法路径显式声明自己处于 RT sleeping lock 上,从而避免误报并保持 oom_reaper 的让出调度能力。