sched discussion
[PATCH] sched/core: Fix overflow in resched latency warning threshold
LLM 分析
sched/core:修复 resched latency 警告阈值的整数溢出
系列概况
- 标题: [PATCH] sched/core: Fix overflow in resched latency warning threshold
- 作者: Hui Su sh_def@163.com
- 版本: 无(单 patch,v1)
- 规模: 2 files changed, 8 insertions(+), 6 deletions(-)
- 修改文件:
kernel/sched/core.c、kernel/sched/sched.h - 代码统计: +8 / -6
- Message-ID: 20260827080152.2544683-1-sh_def@163.com
- 完整性: 单封 patch,body 含完整 diff + commit message + Fixes + Signed-off-by;本线程无后续回复
补丁目的
修复 sysctl_resched_latency_warn_ms 在与 NSEC_PER_MSEC 相乘时,因 32 位平台上 NSEC_PER_MSEC 是 long,乘法按 32 位有符号算术求值。设置值 > 2147 ms 时结果会溢出,再与 u64 的 resched_latency 比较时阈值失准——warning 会被吞掉或过早触发。
此外 debugfs 通过 debugfs_create_u32() 暴露该 knob,但底层变量是 int,> INT_MAX 的值会按有符号解析成负数;boot 参数解析函数也用了 kstrtol() / long。本 patch 把变量类型、解析函数、乘法全链路统一为无符号 + u64。
Fixes: c006fac556e4 ("sched: Warn on long periods of pending need_resched")
旧流程的问题
旧实现代码:
int latency_warn_ms = READ_ONCE(sysctl_resched_latency_warn_ms);
...
if (resched_latency <= latency_warn_ms * NSEC_PER_MSEC)
latency_warn_ms是int;NSEC_PER_MSEC在 32-bit 内核上是long。int * long隐式转换后,编译器可能先用 32 位计算再扩展,超过约 21 亿 ns(2147 ms)后 wrap 成负数。- debugfs 暴露 u32,但变量是
int,写入> INT_MAX的值会被解释为负数。 - boot 参数用
kstrtol()解析为long val,再赋给int,大值被截断或溢出。
新流程
新实现代码:
unsigned int latency_warn_ms = READ_ONCE(sysctl_resched_latency_warn_ms);
u64 latency_warn_ns;
latency_warn_ns = (u64)latency_warn_ms * NSEC_PER_MSEC;
if (resched_latency <= latency_warn_ns)
- 全局变量变
unsigned int,写入路径无负值风险。 - 显式
(u64)cast,保证乘法在 64 位空间完成。 - boot 参数改
kstrtouint(),与 debugfs 接口语义一致。
关键实现
kernel/sched/sched.h
-extern int sysctl_resched_latency_warn_ms;
+extern unsigned int sysctl_resched_latency_warn_ms;
把全局变量声明改成无符号,与 core.c 中的定义对齐。
kernel/sched/core.c
变量定义同步改类型:
-__read_mostly int sysctl_resched_latency_warn_ms = 100;
+__read_mostly unsigned int sysctl_resched_latency_warn_ms = 100;
cpu_resched_latency() 里把读出的值转成 u64 ns 再比较:
- int latency_warn_ms = READ_ONCE(sysctl_resched_latency_warn_ms);
+ unsigned int latency_warn_ms = READ_ONCE(sysctl_resched_latency_warn_ms);
+ u64 latency_warn_ns;
...
- if (resched_latency <= latency_warn_ms * NSEC_PER_MSEC)
+ latency_warn_ns = (u64)latency_warn_ms * NSEC_PER_MSEC;
+ if (resched_latency <= latency_warn_ns)
boot 参数解析也换成无符号版本:
- long val;
+ unsigned int val;
- if ((kstrtol(str, 0, &val))) {
+ if (kstrtouint(str, 0, &val)) {
类比
把 latency_warn_ms * NSEC_PER_MSEC 想成水表上的刻度盘:
- 刻度盘最多显示约 21 亿(32-bit signed,对应约 2147 ms)。
- 实际水量超过上限后,刻度盘会「倒转」回到负数区,表面上像没用水——warning 因此被吞掉或提前触发。
- 修复办法是换一块能显示更长里程的「大表盘」(u64),并且把单位换算先做完再比较,避免在中间步骤溢出。
debugfs 暴露 u32 但变量用 int 也类似:商店标价是「最多 4294 元」(u32),但仓库记账用「借/贷」(int),允许负库存——两边规则不一致,账目必然对不上。
Highlight:风险与注意点
- 行为兼容性:本 patch 只影响 latency warning 诊断,不影响调度决策。但若之前在 boot cmdline 写
resched_latency_warn_ms=999999999,旧路径会默默溢出,新路径会真正生效——需要 release notes 提示。 - 32-bit 影响最大:64-bit 内核
NSEC_PER_MSEC通常是 64 位字面量,乘法天然 64 位,问题不易察觉;但 debugfs 接口与底层变量类型不一致的问题在 64-bit 上同样存在。 - 回归建议:在 32-bit 内核上跑
c006fac556e4引入的 resched latency 警告路径,喂入 3000 ms / 5000 ms,确认 warning 按预期触发。 - C 类型陷阱:
int * long在 32-bit 上的隐式转换是经典坑;类似函数可加BUILD_BUG_ON(sizeof(int) < sizeof(long))静态检查,但本 patch 选择直接修运行时逻辑,更稳。
Patch 概览
| 文件 | 变更摘要 |
|---|---|
kernel/sched/core.c | 变量定义改 unsigned int;局部加 u64 latency_warn_ns;比较前 (u64) cast;boot 参数解析改 kstrtouint |
kernel/sched/sched.h | extern 声明改 unsigned int |
版本变化
本 patch 为 v1 单次提交,无前序版本可比较。
一句话总结
把 sysctl_resched_latency_warn_ms 整条链路(变量声明、读出、乘法 cast、boot 解析)统一为 unsigned int + u64,避免 32 位上 2147 ms 以上阈值计算溢出导致 warning 误触或漏报。
+-----------------+ +----------------------------+
| debugfs | write | sysctl_resched_latency_warn_ms
| (u32) |<------>| int -> unsigned int
+-----------------+ +-------------+--------------+
| READ_ONCE
v
+-------------+--------------+
| latency_warn_ms |
| int -> unsigned int |
+-------------+--------------+
|
| (u64) * NSEC_PER_MSEC
v
+-------------+--------------+
| latency_warn_ns (u64) |
+-------------+--------------+
| compare
v
+---------------------------+
| resched_latency (u64) |
+---------------------------+
old: int * long -> 32-bit signed mul -> 2147ms+ overflow wrap
new: (u64) * long -> 64-bit mul -> full range correct