diff options
| author | Stian Halseth <stian@itx.no> | 2026-10-07 16:29:50 +0200 |
|---|---|---|
| committer | Peter Zijlstra <peterz@infradead.org> | 2026-10-16 09:49:03 +0200 |
| commit | 96a494c9cfd8f4bb7b1a7a70275426db71cf98aa (patch) | |
| tree | a4bcb7d5a6850ee0bf4f64a52a45f01800fb3500 /kernel/time | |
| parent | 6f88f6cbb0472a8481ba560578a1d7c8d802eba7 (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.c | 22 |
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); } |
