Skip to content

Commit 6199f99

Browse files
Frederic WeisbeckerThomas Gleixner
authored andcommitted
sched/cputime: Handle dyntick-idle steal time correctly
The dyntick-idle steal time is currently accounted when the tick restarts but the stolen idle time is not subtracted from the idle time that was already accounted. This is to avoid observing the idle time going backward as the dyntick-idle cputime accessors can't reliably know in advance the stolen idle time. In order to maintain a forward progressing idle cputime while subtracting idle steal time from it, keep track of the previously accounted idle stolen time and substract it from _later_ idle cputime accounting. Signed-off-by: Frederic Weisbecker <frederic@kernel.org> Signed-off-by: Thomas Gleixner <tglx@kernel.org> Tested-by: Shrikanth Hegde <sshegde@linux.ibm.com> Link: https://patch.msgid.link/20260508131647.43868-16-frederic@kernel.org
1 parent 7198e39 commit 6199f99

2 files changed

Lines changed: 24 additions & 5 deletions

File tree

include/linux/kernel_stat.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ struct kernel_cpustat {
3939
bool idle_elapse;
4040
seqcount_t idle_sleeptime_seq;
4141
u64 idle_entrytime;
42+
u64 idle_stealtime[2];
4243
#endif
4344
u64 cpustat[NR_STATS];
4445
};

kernel/sched/cputime.c

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -425,19 +425,32 @@ static inline void irqtime_account_process_tick(struct task_struct *p, int user_
425425
static void kcpustat_idle_stop(struct kernel_cpustat *kc, u64 now)
426426
{
427427
u64 *cpustat = kc->cpustat;
428-
u64 delta;
428+
u64 delta, steal, steal_delta;
429+
int iowait;
429430

430431
if (!kc->idle_elapse)
431432
return;
432433

434+
iowait = nr_iowait_cpu(smp_processor_id()) > 0;
433435
delta = now - kc->idle_entrytime;
436+
steal = steal_account_process_time(delta);
434437

438+
/*
439+
* Record the idle time after substracting the steal time from
440+
* previous update sequence. Don't substract the steal time from
441+
* the current update sequence to avoid readers moving backward.
442+
*/
435443
write_seqcount_begin(&kc->idle_sleeptime_seq);
436-
if (nr_iowait_cpu(smp_processor_id()) > 0)
444+
steal_delta = min_t(u64, kc->idle_stealtime[iowait], delta);
445+
delta -= steal_delta;
446+
kc->idle_stealtime[iowait] -= steal_delta;
447+
448+
if (iowait)
437449
cpustat[CPUTIME_IOWAIT] += delta;
438450
else
439451
cpustat[CPUTIME_IDLE] += delta;
440452

453+
kc->idle_stealtime[iowait] += steal;
441454
kc->idle_entrytime = now;
442455
kc->idle_elapse = false;
443456
write_seqcount_end(&kc->idle_sleeptime_seq);
@@ -464,7 +477,6 @@ void kcpustat_dyntick_stop(u64 now)
464477
kcpustat_idle_stop(kc, now);
465478
kc->idle_dyntick = false;
466479
vtime_dyntick_stop();
467-
steal_account_process_time(ULONG_MAX);
468480
}
469481
}
470482

@@ -508,6 +520,7 @@ static u64 kcpustat_field_dyntick(int cpu, enum cpu_usage_stat idx,
508520
bool compute_delta, u64 now)
509521
{
510522
struct kernel_cpustat *kc = &kcpustat_cpu(cpu);
523+
int iowait = idx == CPUTIME_IOWAIT;
511524
u64 *cpustat = kc->cpustat;
512525
unsigned int seq;
513526
u64 idle;
@@ -516,8 +529,13 @@ static u64 kcpustat_field_dyntick(int cpu, enum cpu_usage_stat idx,
516529
seq = read_seqcount_begin(&kc->idle_sleeptime_seq);
517530

518531
idle = cpustat[idx];
519-
if (kc->idle_elapse && compute_delta && now > kc->idle_entrytime)
520-
idle += (now - kc->idle_entrytime);
532+
533+
if (kc->idle_elapse && compute_delta && now > kc->idle_entrytime) {
534+
u64 delta = now - kc->idle_entrytime;
535+
536+
delta -= min_t(u64, kc->idle_stealtime[iowait], delta);
537+
idle += delta;
538+
}
521539
} while (read_seqcount_retry(&kc->idle_sleeptime_seq, seq));
522540

523541
return idle;

0 commit comments

Comments
 (0)