History

tick-sched: Fix idle and iowait sleeptime accounting vs CPU hotplug

When offlining and onlining CPUs the overall reported idle and iowait times as reported by /proc/stat jump backward and forward: cpu 132 0 176 225249 47 6 6 21 0 0 cpu0 80 0 115 112575 33 3 4 18 0 0 cpu1 52 0 60 112673 13 3 1 2 0 0 cpu 133 0 177 226681 47 6 6 21 0 0 cpu0 80 0 116 113387 33 3 4 18 0 0 cpu 133 0 178 114431 33 6 6 21 0 0 <---- jump backward cpu0 80 0 116 114247 33 3 4 18 0 0 cpu1 52 0 61 183 0 3 1 2 0 0 <---- idle + iowait start with 0 cpu 133 0 178 228956 47 6 6 21 0 0 <---- jump forward cpu0 81 0 117 114929 33 3 4 18 0 0 Reason for this is that get_idle_time() in fs/proc/stat.c has different sources for both values depending on if a CPU is online or offline: - if a CPU is online the values may be taken from its per cpu tick_cpu_sched structure - if a CPU is offline the values are taken from its per cpu cpustat structure The problem is that the per cpu tick_cpu_sched structure is set to zero on CPU offline. See tick_cancel_sched_timer() in kernel/time/tick-sched.c. Therefore when a CPU is brought offline and online afterwards both its idle and iowait sleeptime will be zero, causing a jump backward in total system idle and iowait sleeptime. In a similar way if a CPU is then brought offline again the total idle and iowait sleeptimes will jump forward. It looks like this behavior was introduced with commit 4b0c0f294f60 ("tick: Cleanup NOHZ per cpu data on cpu down"). This was only noticed now on s390, since we switched to generic idle time reporting with commit be76ea614460 ("s390/idle: remove arch_cpu_idle_time() and corresponding code"). Fix this by preserving the values of idle_sleeptime and iowait_sleeptime members of the per-cpu tick_sched structure on CPU hotplug.

Heiko Carstens committed 2 years ago

Commit 71fee481 parent 80fe58c1 file · +5 −0Browse files

Showing the changes to kernel (1 of 1 file). Show all

Unified · Split

kernel/time/tick-sched.c+5−0
@@ -1576,13 +1576,18 @@ void tick_setup_sched_timer(void) · 1575 unmodified lines
1576void tick_cancel_sched_timer(int cpu)1576void tick_cancel_sched_timer(int cpu)
1577{1577{
1578 struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);1578 struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
1579 ktime_t idle_sleeptime, iowait_sleeptime;
1579 1580
1580# ifdef CONFIG_HIGH_RES_TIMERS1581# ifdef CONFIG_HIGH_RES_TIMERS
1581 if (ts->sched_timer.base)1582 if (ts->sched_timer.base)
1582 hrtimer_cancel(&ts->sched_timer);1583 hrtimer_cancel(&ts->sched_timer);
1583# endif1584# endif
1584 1585
1586 idle_sleeptime = ts->idle_sleeptime;
1587 iowait_sleeptime = ts->iowait_sleeptime;
1585 memset(ts, 0, sizeof(*ts));1588 memset(ts, 0, sizeof(*ts));
1589 ts->idle_sleeptime = idle_sleeptime;
1590 ts->iowait_sleeptime = iowait_sleeptime;
1586}1591}
1587#endif1592#endif
1588 1593