Skip to content

Commit 07b65a8

Browse files
Frederic WeisbeckerKAGA-KOKO
authored andcommitted
timers/nohz: Only ever update sleeptime from idle exit
The idle and IO sleeptime statistics appearing in /proc/stat can be currently updated from two sites: locally on idle exit and remotely by cpufreq. However there is no synchronization mechanism protecting concurrent updates. It is therefore possible to account the sleeptime twice, among all the other possible broken scenarios. To prevent from breaking the sleeptime accounting source, restrict the sleeptime updates to the local idle exit site. If there is a delta to add since the last update, IO/Idle sleep time readers will now only compute the delta without actually writing it back to the internal idle statistic fields. This fixes a writer VS writer race. Note there are still two known reader VS writer races to handle. A subsequent patch will fix one. Reported-by: Yu Liao <liaoyu15@huawei.com> Signed-off-by: Frederic Weisbecker <frederic@kernel.org> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://lore.kernel.org/r/20230222144649.624380-3-frederic@kernel.org
1 parent 605da84 commit 07b65a8

1 file changed

Lines changed: 37 additions & 58 deletions

File tree

kernel/time/tick-sched.c

Lines changed: 37 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -637,31 +637,21 @@ static void tick_nohz_update_jiffies(ktime_t now)
637637
touch_softlockup_watchdog_sched();
638638
}
639639

640-
/*
641-
* Updates the per-CPU time idle statistics counters
642-
*/
643-
static void
644-
update_ts_time_stats(int cpu, struct tick_sched *ts, ktime_t now, u64 *last_update_time)
640+
static void tick_nohz_stop_idle(struct tick_sched *ts, ktime_t now)
645641
{
646642
ktime_t delta;
647643

648-
if (ts->idle_active) {
649-
delta = ktime_sub(now, ts->idle_entrytime);
650-
if (nr_iowait_cpu(cpu) > 0)
651-
ts->iowait_sleeptime = ktime_add(ts->iowait_sleeptime, delta);
652-
else
653-
ts->idle_sleeptime = ktime_add(ts->idle_sleeptime, delta);
654-
ts->idle_entrytime = now;
655-
}
644+
if (WARN_ON_ONCE(!ts->idle_active))
645+
return;
656646

657-
if (last_update_time)
658-
*last_update_time = ktime_to_us(now);
647+
delta = ktime_sub(now, ts->idle_entrytime);
659648

660-
}
649+
if (nr_iowait_cpu(smp_processor_id()) > 0)
650+
ts->iowait_sleeptime = ktime_add(ts->iowait_sleeptime, delta);
651+
else
652+
ts->idle_sleeptime = ktime_add(ts->idle_sleeptime, delta);
661653

662-
static void tick_nohz_stop_idle(struct tick_sched *ts, ktime_t now)
663-
{
664-
update_ts_time_stats(smp_processor_id(), ts, now, NULL);
654+
ts->idle_entrytime = now;
665655
ts->idle_active = 0;
666656

667657
sched_clock_idle_wakeup_event();
@@ -674,6 +664,30 @@ static void tick_nohz_start_idle(struct tick_sched *ts)
674664
sched_clock_idle_sleep_event();
675665
}
676666

667+
static u64 get_cpu_sleep_time_us(struct tick_sched *ts, ktime_t *sleeptime,
668+
bool compute_delta, u64 *last_update_time)
669+
{
670+
ktime_t now, idle;
671+
672+
if (!tick_nohz_active)
673+
return -1;
674+
675+
now = ktime_get();
676+
if (last_update_time)
677+
*last_update_time = ktime_to_us(now);
678+
679+
if (ts->idle_active && compute_delta) {
680+
ktime_t delta = ktime_sub(now, ts->idle_entrytime);
681+
682+
idle = ktime_add(*sleeptime, delta);
683+
} else {
684+
idle = *sleeptime;
685+
}
686+
687+
return ktime_to_us(idle);
688+
689+
}
690+
677691
/**
678692
* get_cpu_idle_time_us - get the total idle time of a CPU
679693
* @cpu: CPU number to query
@@ -691,27 +705,9 @@ static void tick_nohz_start_idle(struct tick_sched *ts)
691705
u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time)
692706
{
693707
struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
694-
ktime_t now, idle;
695-
696-
if (!tick_nohz_active)
697-
return -1;
698-
699-
now = ktime_get();
700-
if (last_update_time) {
701-
update_ts_time_stats(cpu, ts, now, last_update_time);
702-
idle = ts->idle_sleeptime;
703-
} else {
704-
if (ts->idle_active && !nr_iowait_cpu(cpu)) {
705-
ktime_t delta = ktime_sub(now, ts->idle_entrytime);
706-
707-
idle = ktime_add(ts->idle_sleeptime, delta);
708-
} else {
709-
idle = ts->idle_sleeptime;
710-
}
711-
}
712-
713-
return ktime_to_us(idle);
714708

709+
return get_cpu_sleep_time_us(ts, &ts->idle_sleeptime,
710+
!nr_iowait_cpu(cpu), last_update_time);
715711
}
716712
EXPORT_SYMBOL_GPL(get_cpu_idle_time_us);
717713

@@ -732,26 +728,9 @@ EXPORT_SYMBOL_GPL(get_cpu_idle_time_us);
732728
u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time)
733729
{
734730
struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
735-
ktime_t now, iowait;
736-
737-
if (!tick_nohz_active)
738-
return -1;
739-
740-
now = ktime_get();
741-
if (last_update_time) {
742-
update_ts_time_stats(cpu, ts, now, last_update_time);
743-
iowait = ts->iowait_sleeptime;
744-
} else {
745-
if (ts->idle_active && nr_iowait_cpu(cpu) > 0) {
746-
ktime_t delta = ktime_sub(now, ts->idle_entrytime);
747-
748-
iowait = ktime_add(ts->iowait_sleeptime, delta);
749-
} else {
750-
iowait = ts->iowait_sleeptime;
751-
}
752-
}
753731

754-
return ktime_to_us(iowait);
732+
return get_cpu_sleep_time_us(ts, &ts->iowait_sleeptime,
733+
nr_iowait_cpu(cpu), last_update_time);
755734
}
756735
EXPORT_SYMBOL_GPL(get_cpu_iowait_time_us);
757736

0 commit comments

Comments
 (0)