0/1 已展开

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.ckernel/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_MSEClong,乘法按 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_msintNSEC_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.hextern 声明改 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