summaryrefslogtreecommitdiff
path: root/kernel/time
diff options
context:
space:
mode:
authorStian Halseth <stian@itx.no>2026-10-07 16:29:50 +0200
committerPeter Zijlstra <peterz@infradead.org>2026-10-16 09:49:03 +0200
commit96a494c9cfd8f4bb7b1a7a70275426db71cf98aa (patch)
treea4bcb7d5a6850ee0bf4f64a52a45f01800fb3500 /kernel/time
parent6f88f6cbb0472a8481ba560578a1d7c8d802eba7 (diff)
sched/cputime: Don't account idle time twice after dyntick-idle
On idle exit the dyntick-idle accounting accounts the time up to now, then the tick is restarted on its old period. The first tick accounts a whole TICK_NSEC to whatever runs, although the part of that period before the idle exit has just been accounted as idle time, or with IRQ_TIME_ACCOUNTING as IRQ time. That is up to a full tick per idle exit, and /proc/stat reports more idle time than wall time. Record how much of the current tick period dyntick-idle has accounted, and leave it out of the tick that ends the period. Fixes: cf6444c3e1bb7 ("tick/sched: Unify idle cputime accounting") Signed-off-by: Stian Halseth <stian@itx.no> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Reviewed-by: Frederic Weisbecker <frederic@kernel.org> Link: https://lore.kernel.org/all/20261004142724.3896396-1-stian@itx.no/ Link: https://patch.msgid.link/20261007142950.547604-1-stian@itx.no
Diffstat (limited to 'kernel/time')
-rw-r--r--kernel/time/tick-sched.c22
1 files changed, 17 insertions, 5 deletions
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index 6c3fea386713..4e293f81e4dc 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -763,12 +763,18 @@ static ktime_t tick_forward_now(ktime_t expires, ktime_t now)
return expires + TICK_NSEC;
}
+static void tick_nohz_forward_last_tick(struct tick_sched *ts, ktime_t now)
+{
+ if (now >= ts->last_tick)
+ ts->last_tick = tick_forward_now(ts->last_tick, now);
+}
+
static void tick_nohz_restart(struct tick_sched *ts, ktime_t now)
{
- ktime_t expires = ts->last_tick;
+ ktime_t expires;
- if (now >= expires)
- expires = tick_forward_now(expires, now);
+ tick_nohz_forward_last_tick(ts, now);
+ expires = ts->last_tick;
if (tick_sched_flag_test(ts, TS_FLAG_HIGHRES)) {
hrtimer_start(&ts->sched_timer, expires, HRTIMER_MODE_ABS_PINNED_HARD);
@@ -1329,6 +1335,12 @@ unsigned long tick_nohz_get_idle_calls_cpu(int cpu)
return ts->idle_calls;
}
+static void tick_nohz_dyntick_stop(struct tick_sched *ts, ktime_t now)
+{
+ tick_nohz_forward_last_tick(ts, now);
+ kcpustat_dyntick_stop(now, ts->last_tick - TICK_NSEC);
+}
+
void tick_nohz_idle_restart_tick(void)
{
struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
@@ -1341,7 +1353,7 @@ void tick_nohz_idle_restart_tick(void)
* no tiny amount of idle time is accounted twice.
*/
ts->idle_entrytime = ktime_get();
- kcpustat_dyntick_stop(ts->idle_entrytime);
+ tick_nohz_dyntick_stop(ts, ts->idle_entrytime);
tick_nohz_restart_sched_tick(ts, ts->idle_entrytime);
}
}
@@ -1385,7 +1397,7 @@ void tick_nohz_idle_exit(void)
if (tick_sched_flag_test(ts, TS_FLAG_STOPPED)) {
now = ktime_get();
- kcpustat_dyntick_stop(now);
+ tick_nohz_dyntick_stop(ts, now);
tick_nohz_idle_update_tick(ts, now);
}