From nobody Sun Feb 8 05:27:46 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8334B274FC1; Fri, 16 Jan 2026 14:53:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768575228; cv=none; b=DiUGYfEkE7m+tiLC+iBrLNm97BmpCpY1rR/23xw3VzUZ/8+sDcWZ8tG5+FOUh0ILJIrLxd7ajeT+DQtH7RohnhM9lWRavl/AaI0WC9oFvrh3ChvuaV5bpqTWqSD0QopLLBMp0Lv82G0w9ML99LSTua8FI7AaFyqXLVBDO6tzU4s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768575228; c=relaxed/simple; bh=6rMuEpbQZXvwjKA1HjWhJVfT/gG1/xfSb0ElvkTOWjQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ofVqpmgH1ExCiWdNm1j/WveUa9b6NdqfjKpFZDnDKvf3a7aF4b5AYCVRCLaz3et7NAxtg6pB01NjV7w2RbsQIwrea0zlw8Kz09WusTazw8qTO4ujOgv4RkmFy9wNTvX3WJG3/SD/Vpnn4nFxeRHjg43aWHVDi69GgUdAnibmi/Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=CNHyGy9x; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="CNHyGy9x" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4FE2AC16AAE; Fri, 16 Jan 2026 14:53:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768575228; bh=6rMuEpbQZXvwjKA1HjWhJVfT/gG1/xfSb0ElvkTOWjQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=CNHyGy9x0ZCCX3hcPWuzKQoZdn2unDE3tiZ3OHWATurUeRfzrcyer0Bizt3PhEHNs VeXOLM91VS3T7jU6c+Oomdzra6apJuw5lvulqLk2RNJM8W2atJwfXU3bmOQrRhcMGK dhLpJHmLnLCrzmHDvtyJNh5lG2FqU6eGSaVbvu6dpyoc99Q46h6YEkbnB8NxttweMt 4+T3RxEeQPIACQs50+Rr0IclsAjLeekt5UsMdBaVhxEwjWRULX/BeE9qy/w1yrQ3ET 3arl4YDBDpRDM6J9kZ5pl1ddZb/l311wmJrjTSkyvPPaYoij16nZ7tekNqE3TrLhNP zte6143jEN7qg== From: Frederic Weisbecker To: LKML Cc: Frederic Weisbecker , "Christophe Leroy (CS GROUP)" , "Rafael J. Wysocki" , Alexander Gordeev , Anna-Maria Behnsen , Ben Segall , Boqun Feng , Christian Borntraeger , Dietmar Eggemann , Heiko Carstens , Ingo Molnar , Jan Kiszka , Joel Fernandes , Juri Lelli , Kieran Bingham , Madhavan Srinivasan , Mel Gorman , Michael Ellerman , Neeraj Upadhyay , Nicholas Piggin , "Paul E . McKenney" , Peter Zijlstra , Steven Rostedt , Sven Schnelle , Thomas Gleixner , Uladzislau Rezki , Valentin Schneider , Vasily Gorbik , Vincent Guittot , Viresh Kumar , Xin Zhao , linux-pm@vger.kernel.org, linux-s390@vger.kernel.org, linuxppc-dev@lists.ozlabs.org Subject: [PATCH 12/15] tick/sched: Consolidate idle time fetching APIs Date: Fri, 16 Jan 2026 15:52:05 +0100 Message-ID: <20260116145208.87445-13-frederic@kernel.org> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20260116145208.87445-1-frederic@kernel.org> References: <20260116145208.87445-1-frederic@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Fetching the idle cputime is available through a variety of accessors all over the place depending on the different accounting flavours and needs: - idle vtime generic accounting can be accessed by kcpustat_field(), kcpustat_cpu_fetch(), or get_idle/iowait_time() but not by get_cpu_idle/iowait_time_us() - dynticks-idle accounting can only be accessed by get_idle/iowait_time() or get_cpu_idle/iowait_time_us() - CONFIG_NO_HZ_COMMON=3Dn idle accounting can be accessed by kcpustat_field= () kcpustat_cpu_fetch(), or get_idle/iowait_time() but not by get_cpu_idle/iowait_time_us() Moreover get_idle/iowait_time() relies on get_cpu_idle/iowait_time_us() with a non-sensical conversion to microseconds and back to nanoseconds on the way. Start consolidating the APIs with removing get_idle/iowait_time() and make kcpustat_field() and kcpustat_cpu_fetch() work for all cases. Signed-off-by: Frederic Weisbecker --- fs/proc/stat.c | 40 +++----------------------- fs/proc/uptime.c | 8 ++---- include/linux/kernel_stat.h | 34 +++++++++++++++++++--- kernel/sched/cputime.c | 57 ++++++++++++++++++++++++++----------- 4 files changed, 76 insertions(+), 63 deletions(-) diff --git a/fs/proc/stat.c b/fs/proc/stat.c index 6ac2a13b8be5..c00468a83f64 100644 --- a/fs/proc/stat.c +++ b/fs/proc/stat.c @@ -22,38 +22,6 @@ #define arch_irq_stat() 0 #endif =20 -u64 get_idle_time(struct kernel_cpustat *kcs, int cpu) -{ - u64 idle, idle_usecs =3D -1ULL; - - if (cpu_online(cpu)) - idle_usecs =3D get_cpu_idle_time_us(cpu, NULL); - - if (idle_usecs =3D=3D -1ULL) - /* !NO_HZ or cpu offline or vtime so we can rely on cpustat.idle */ - idle =3D kcpustat_field(CPUTIME_IDLE, cpu); - else - idle =3D idle_usecs * NSEC_PER_USEC; - - return idle; -} - -static u64 get_iowait_time(struct kernel_cpustat *kcs, int cpu) -{ - u64 iowait, iowait_usecs =3D -1ULL; - - if (cpu_online(cpu)) - iowait_usecs =3D get_cpu_iowait_time_us(cpu, NULL); - - if (iowait_usecs =3D=3D -1ULL) - /* !NO_HZ or cpu offline or vtime so we can rely on cpustat.iowait */ - iowait =3D kcpustat_field(CPUTIME_IOWAIT, cpu); - else - iowait =3D iowait_usecs * NSEC_PER_USEC; - - return iowait; -} - static void show_irq_gap(struct seq_file *p, unsigned int gap) { static const char zeros[] =3D " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"; @@ -105,8 +73,8 @@ static int show_stat(struct seq_file *p, void *v) user +=3D cpustat[CPUTIME_USER]; nice +=3D cpustat[CPUTIME_NICE]; system +=3D cpustat[CPUTIME_SYSTEM]; - idle +=3D get_idle_time(&kcpustat, i); - iowait +=3D get_iowait_time(&kcpustat, i); + idle +=3D cpustat[CPUTIME_IDLE]; + iowait +=3D cpustat[CPUTIME_IOWAIT]; irq +=3D cpustat[CPUTIME_IRQ]; softirq +=3D cpustat[CPUTIME_SOFTIRQ]; steal +=3D cpustat[CPUTIME_STEAL]; @@ -146,8 +114,8 @@ static int show_stat(struct seq_file *p, void *v) user =3D cpustat[CPUTIME_USER]; nice =3D cpustat[CPUTIME_NICE]; system =3D cpustat[CPUTIME_SYSTEM]; - idle =3D get_idle_time(&kcpustat, i); - iowait =3D get_iowait_time(&kcpustat, i); + idle =3D cpustat[CPUTIME_IDLE]; + iowait =3D cpustat[CPUTIME_IOWAIT]; irq =3D cpustat[CPUTIME_IRQ]; softirq =3D cpustat[CPUTIME_SOFTIRQ]; steal =3D cpustat[CPUTIME_STEAL]; diff --git a/fs/proc/uptime.c b/fs/proc/uptime.c index b5343d209381..433aa947cd57 100644 --- a/fs/proc/uptime.c +++ b/fs/proc/uptime.c @@ -18,12 +18,8 @@ static int uptime_proc_show(struct seq_file *m, void *v) int i; =20 idle_nsec =3D 0; - for_each_possible_cpu(i) { - struct kernel_cpustat kcs; - - kcpustat_cpu_fetch(&kcs, i); - idle_nsec +=3D get_idle_time(&kcs, i); - } + for_each_possible_cpu(i) + idle_nsec +=3D kcpustat_field(CPUTIME_IDLE, i); =20 ktime_get_boottime_ts64(&uptime); timens_add_boottime(&uptime); diff --git a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h index a906492eb680..e1efd26e56f0 100644 --- a/include/linux/kernel_stat.h +++ b/include/linux/kernel_stat.h @@ -110,32 +110,59 @@ extern void kcpustat_dyntick_start(ktime_t now); extern void kcpustat_dyntick_stop(ktime_t now); extern void kcpustat_irq_enter(ktime_t now); extern void kcpustat_irq_exit(ktime_t now); +extern u64 kcpustat_field_idle(int cpu); +extern u64 kcpustat_field_iowait(int cpu); =20 static inline bool kcpustat_idle_dyntick(void) { return __this_cpu_read(kernel_cpustat.idle_dyntick); } #else +static inline u64 kcpustat_field_idle(int cpu) +{ + return kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE]; +} +static inline u64 kcpustat_field_iowait(int cpu) +{ + return kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT]; +} + static inline bool kcpustat_idle_dyntick(void) { return false; } #endif /* CONFIG_NO_HZ_COMMON */ =20 +/* Fetch cputime values when vtime is disabled on a CPU */ +static inline u64 kcpustat_field_default(enum cpu_usage_stat usage, int cp= u) +{ + if (usage =3D=3D CPUTIME_IDLE) + return kcpustat_field_idle(cpu); + if (usage =3D=3D CPUTIME_IOWAIT) + return kcpustat_field_iowait(cpu); + return kcpustat_cpu(cpu).cpustat[usage]; +} + +static inline void kcpustat_cpu_fetch_default(struct kernel_cpustat *dst, = int cpu) +{ + *dst =3D kcpustat_cpu(cpu); + dst->cpustat[CPUTIME_IDLE] =3D kcpustat_field_idle(cpu); + dst->cpustat[CPUTIME_IOWAIT] =3D kcpustat_field_iowait(cpu); +} + #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN extern u64 kcpustat_field(enum cpu_usage_stat usage, int cpu); extern void kcpustat_cpu_fetch(struct kernel_cpustat *dst, int cpu); #else static inline u64 kcpustat_field(enum cpu_usage_stat usage, int cpu) { - return kcpustat_cpu(cpu).cpustat[usage]; + return kcpustat_field_default(usage, cpu); } =20 static inline void kcpustat_cpu_fetch(struct kernel_cpustat *dst, int cpu) { - *dst =3D kcpustat_cpu(cpu); + kcpustat_cpu_fetch_default(dst, cpu); } - #endif /* !CONFIG_VIRT_CPU_ACCOUNTING_GEN */ =20 extern void account_user_time(struct task_struct *, u64); @@ -145,7 +172,6 @@ extern void account_system_index_time(struct task_struc= t *, u64, enum cpu_usage_stat); extern void account_steal_time(u64); extern void account_idle_time(u64); -extern u64 get_idle_time(struct kernel_cpustat *kcs, int cpu); =20 #ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE static inline void account_process_tick(struct task_struct *tsk, int user) diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c index 16d6730efe6d..9906abe5d7bc 100644 --- a/kernel/sched/cputime.c +++ b/kernel/sched/cputime.c @@ -475,21 +475,14 @@ void kcpustat_irq_exit(ktime_t now) kcpustat_idle_start(kc, now); } =20 -static u64 get_cpu_sleep_time_us(int cpu, enum cpu_usage_stat idx, - bool compute_delta, u64 *last_update_time) +static u64 kcpustat_field_dyntick(int cpu, enum cpu_usage_stat idx, + bool compute_delta, ktime_t now) { struct kernel_cpustat *kc =3D &kcpustat_cpu(cpu); u64 *cpustat =3D kc->cpustat; - ktime_t now, idle; + ktime_t idle; unsigned int seq; =20 - if (vtime_generic_enabled_cpu(cpu)) - return -1; - - now =3D ktime_get(); - if (last_update_time) - *last_update_time =3D ktime_to_us(now); - do { seq =3D read_seqcount_begin(&kc->idle_sleeptime_seq); =20 @@ -502,7 +495,38 @@ static u64 get_cpu_sleep_time_us(int cpu, enum cpu_usa= ge_stat idx, } } while (read_seqcount_retry(&kc->idle_sleeptime_seq, seq)); =20 - return ktime_to_us(idle); + return idle; +} + +u64 kcpustat_field_idle(int cpu) +{ + return kcpustat_field_dyntick(cpu, CPUTIME_IDLE, + !nr_iowait_cpu(cpu), ktime_get()); +} +EXPORT_SYMBOL_GPL(kcpustat_field_idle); + +u64 kcpustat_field_iowait(int cpu) +{ + return kcpustat_field_dyntick(cpu, CPUTIME_IOWAIT, + nr_iowait_cpu(cpu), ktime_get()); +} +EXPORT_SYMBOL_GPL(kcpustat_field_iowait); + +static u64 get_cpu_sleep_time_us(int cpu, enum cpu_usage_stat idx, + bool compute_delta, u64 *last_update_time) +{ + ktime_t now =3D ktime_get(); + u64 res; + + if (vtime_generic_enabled_cpu(cpu)) + return -1; + else + res =3D kcpustat_field_dyntick(cpu, idx, compute_delta, now); + + if (last_update_time) + *last_update_time =3D ktime_to_us(now); + + return ktime_to_us(res); } =20 /** @@ -552,7 +576,6 @@ u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_ti= me) nr_iowait_cpu(cpu), last_update_time); } EXPORT_SYMBOL_GPL(get_cpu_iowait_time_us); - #endif /* CONFIG_NO_HZ_COMMON */ =20 /* @@ -1110,8 +1133,8 @@ u64 kcpustat_field(enum cpu_usage_stat usage, int cpu) struct rq *rq; int err; =20 - if (!vtime_accounting_enabled_cpu(cpu)) - return val; + if (!vtime_generic_enabled_cpu(cpu)) + return kcpustat_field_default(usage, cpu); =20 rq =3D cpu_rq(cpu); =20 @@ -1206,8 +1229,8 @@ void kcpustat_cpu_fetch(struct kernel_cpustat *dst, i= nt cpu) struct rq *rq; int err; =20 - if (!vtime_accounting_enabled_cpu(cpu)) { - *dst =3D *src; + if (!vtime_generic_enabled_cpu(cpu)) { + kcpustat_cpu_fetch_default(dst, cpu); return; } =20 @@ -1220,7 +1243,7 @@ void kcpustat_cpu_fetch(struct kernel_cpustat *dst, i= nt cpu) curr =3D rcu_dereference(rq->curr); if (WARN_ON_ONCE(!curr)) { rcu_read_unlock(); - *dst =3D *src; + kcpustat_cpu_fetch_default(dst, cpu); return; } =20 --=20 2.51.1