[PATCH] sched/core: Fix overflow in resched latency warning threshold

From: Hui Su

Date: Thu Aug 27 2026 - 04:03:39 EST


On 32-bit kernels, latency_warn_ms * NSEC_PER_MSEC is evaluated
using 32-bit signed arithmetic because NSEC_PER_MSEC is a long. Values
above 2147 ms can therefore overflow before the result is compared with
the u64 resched latency, causing the warning threshold to be
miscomputed. Depending on the wrapped value, warnings can be
suppressed or triggered too early.

The debugfs latency_warn_ms knob is exposed through
debugfs_create_u32(), but the underlying variable is currently an int.
Values above INT_MAX can therefore be stored with a negative signed
interpretation. The boot parameter parser similarly uses a signed type
even though the debugfs interface exposes the setting as unsigned.

Use an unsigned int consistently, parse the boot parameter with
kstrtouint(), and convert the millisecond value to u64 nanoseconds
before multiplication.

This only affects the latency warning diagnostic and does not change
scheduling decisions.

Fixes: c006fac556e4 ("sched: Warn on long periods of pending need_resched")
Signed-off-by: Hui Su <sh_def@xxxxxxx>
---
kernel/sched/core.c | 12 +++++++-----
kernel/sched/sched.h | 2 +-
2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index f78275192036..0b1edbc13da1 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -180,7 +180,7 @@ __read_mostly unsigned int sysctl_sched_features =
* If sysctl_resched_latency_warn_once is set, only one warning will be shown
* per boot.
*/
-__read_mostly int sysctl_resched_latency_warn_ms = 100;
+__read_mostly unsigned int sysctl_resched_latency_warn_ms = 100;
__read_mostly int sysctl_resched_latency_warn_once = 1;

/*
@@ -5719,7 +5719,8 @@ unsigned long long task_sched_runtime(struct task_struct *p)

static u64 cpu_resched_latency(struct rq *rq)
{
- 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;
u64 resched_latency, now = rq_clock(rq);
static bool warned_once;

@@ -5740,7 +5741,8 @@ static u64 cpu_resched_latency(struct rq *rq)

rq->ticks_without_resched++;
resched_latency = now - rq->last_seen_need_resched_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)
return 0;

warned_once = true;
@@ -5750,9 +5752,9 @@ static u64 cpu_resched_latency(struct rq *rq)

static int __init setup_resched_latency_warn_ms(char *str)
{
- long val;
+ unsigned int val;

- if ((kstrtol(str, 0, &val))) {
+ if (kstrtouint(str, 0, &val)) {
pr_warn("Unable to set resched_latency_warn_ms\n");
return 1;
}
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index e656c7059bf8..0abea8f64148 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -3150,7 +3150,7 @@ extern __read_mostly unsigned int sysctl_sched_migration_cost;

extern unsigned int sysctl_sched_base_slice;

-extern int sysctl_resched_latency_warn_ms;
+extern unsigned int sysctl_resched_latency_warn_ms;
extern int sysctl_resched_latency_warn_once;

extern unsigned int sysctl_sched_tunable_scaling;
--
2.54.0