* [PATCH v3 0/2] Support BPF traversal of wakeup sources
From: Samuel Wu @ 2026-03-31 15:34 UTC (permalink / raw)
To: Rafael J. Wysocki, Pavel Machek, Len Brown, Greg Kroah-Hartman,
Danilo Krummrich, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman,
Kumar Kartikeya Dwivedi, Song Liu, Yonghong Song, Jiri Olsa,
Shuah Khan
Cc: Samuel Wu, kernel-team, linux-kernel, linux-pm, driver-core, bpf,
linux-kselftest
This patchset adds requisite kfuncs for BPF programs to safely traverse
wakeup_sources, and puts a config flag around the sysfs interface.
Currently, a traversal of wakeup sources require going through
/sys/class/wakeup/* or /d/wakeup_sources/*. The repeated syscalls to query
sysfs is inefficient, as there can be hundreds of wakeup_sources, with each
wakeup source also having multiple attributes. debugfs is unstable and
insecure.
Adding kfuncs to lock/unlock wakeup sources allows BPF program to safely
traverse the wakeup sources list. The head address of wakeup_sources can
safely be resolved through BPF helper functions or variable attributes.
On a quiescent Pixel 6 traversing 150 wakeup_sources, I am seeing ~34x
speedup (sampled 75 times in table below). For a device under load, the
speedup is greater.
+-------+----+----------+----------+
| | n | AVG (ms) | STD (ms) |
+-------+----+----------+----------+
| sysfs | 75 | 44.9 | 12.6 |
+-------+----+----------+----------+
| BPF | 75 | 1.3 | 0.7 |
+-------+----+----------+----------+
The initial attempts for BPF traversal of wakeup_sources was with BPF
iterators [1]. However, BPF already allows for traversing of a simple list
with bpf_for(), and this current patchset has the added benefit of being
~2-3x more performant than BPF iterators.
[1]: https://lore.kernel.org/all/20260225210820.177674-1-wusamuel@google.com/
Changes in v3:
- Changed return type of bpf_wakeup_sources_get_head() to `void *` per Alexei
- Added failure test for direct dereference of wakeup source head
- Use bpf_core_cast() instead of macros in BPF program per Kumar
- v2 link: https://lore.kernel.org/all/20260326112521.2827500-1-wusamuel@google.com/
Changes in v2:
- Dropped CONFIG_PM_WAKEUP_STATS_SYSFS patch for future patchset
- Added declarations for kfuncs to .h to fix sparse and checkpatch warnings
- Added kfunc to get address of wakeup_source's head
- Added example bpf prog selftest for traversal of wakeup sources per Kumar
- Added *_fail.c selftest per Kumar
- More concise commit message in patch 1/2
- v1 link: https://lore.kernel.org/all/20260320160055.4114055-1-wusamuel@google.com/
Samuel Wu (2):
PM: wakeup: Add kfuncs to traverse over wakeup_sources
selftests/bpf: Add tests for wakeup_sources kfuncs
drivers/base/power/power.h | 7 ++
drivers/base/power/wakeup.c | 72 ++++++++++++-
tools/testing/selftests/bpf/config | 3 +-
.../selftests/bpf/prog_tests/wakeup_source.c | 101 ++++++++++++++++++
.../selftests/bpf/progs/test_wakeup_source.c | 92 ++++++++++++++++
.../selftests/bpf/progs/wakeup_source.h | 22 ++++
.../selftests/bpf/progs/wakeup_source_fail.c | 76 +++++++++++++
7 files changed, 370 insertions(+), 3 deletions(-)
create mode 100644 tools/testing/selftests/bpf/prog_tests/wakeup_source.c
create mode 100644 tools/testing/selftests/bpf/progs/test_wakeup_source.c
create mode 100644 tools/testing/selftests/bpf/progs/wakeup_source.h
create mode 100644 tools/testing/selftests/bpf/progs/wakeup_source_fail.c
--
2.53.0.1018.g2bb0e51243-goog
^ permalink raw reply
* Re: [PATCH 01/14] sched/idle: Handle offlining first in idle loop
From: Rafael J. Wysocki @ 2026-03-31 13:59 UTC (permalink / raw)
To: Frederic Weisbecker
Cc: LKML, 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, Shrikanth Hegde, Steven Rostedt, Sven Schnelle,
Thomas Gleixner, Uladzislau Rezki, Valentin Schneider,
Vasily Gorbik, Vincent Guittot, Viresh Kumar, Xin Zhao, linux-pm,
linux-s390, linuxppc-dev
In-Reply-To: <20260331131622.30505-2-frederic@kernel.org>
On Tue, Mar 31, 2026 at 3:16 PM Frederic Weisbecker <frederic@kernel.org> wrote:
>
> Offline handling happens from within the inner idle loop,
> after the beginning of dyntick cputime accounting, nohz idle
> load balancing and TIF_NEED_RESCHED polling.
>
> This is not necessary and even buggy because:
>
> * There is no dyntick handling to do. And calling tick_nohz_idle_enter()
> messes up with the struct tick_sched reset that was performed on
> tick_sched_timer_dying().
>
> * There is no nohz idle balancing to do.
>
> * Polling on TIF_RESCHED is irrelevant at this stage, there are no more
> tasks allowed to run.
>
> * No need to check if need_resched() before offline handling since
> stop_machine is done and all per-cpu kthread should be done with
> their job.
>
> Therefore move the offline handling at the beginning of the idle loop.
> This will also ease the idle cputime unification later by not elapsing
> idle time while offline through the call to:
>
> tick_nohz_idle_enter() -> tick_nohz_start_idle()
>
> Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
> Reviewed-by: Shrikanth Hegde <sshegde@linux.ibm.com>
> Tested-by: Shrikanth Hegde <sshegde@linux.ibm.com>
No issues found, so
Reviewed-by: Rafael J. Wysocki (Intel) <rafael@kernel.org>
> ---
> kernel/sched/idle.c | 13 ++++++++-----
> 1 file changed, 8 insertions(+), 5 deletions(-)
>
> diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c
> index 3681b6ad9276..521a076d8845 100644
> --- a/kernel/sched/idle.c
> +++ b/kernel/sched/idle.c
> @@ -260,6 +260,14 @@ static void do_idle(void)
> {
> int cpu = smp_processor_id();
>
> + if (cpu_is_offline(cpu)) {
> + local_irq_disable();
> + /* All per-CPU kernel threads should be done by now. */
> + WARN_ON_ONCE(need_resched());
> + cpuhp_report_idle_dead();
> + arch_cpu_idle_dead();
> + }
> +
> /*
> * Check if we need to update blocked load
> */
> @@ -311,11 +319,6 @@ static void do_idle(void)
> */
> local_irq_disable();
>
> - if (cpu_is_offline(cpu)) {
> - cpuhp_report_idle_dead();
> - arch_cpu_idle_dead();
> - }
> -
> arch_cpu_idle_enter();
> rcu_nocb_flush_deferred_wakeup();
>
> --
> 2.53.0
>
^ permalink raw reply
* [PATCH 14/14] sched/cputime: Handle dyntick-idle steal time correctly
From: Frederic Weisbecker @ 2026-03-31 13:16 UTC (permalink / raw)
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, Shrikanth Hegde,
Steven Rostedt, Sven Schnelle, Thomas Gleixner, Uladzislau Rezki,
Valentin Schneider, Vasily Gorbik, Vincent Guittot, Viresh Kumar,
Xin Zhao, linux-pm, linux-s390, linuxppc-dev
In-Reply-To: <20260331131622.30505-1-frederic@kernel.org>
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>
Tested-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
include/linux/kernel_stat.h | 1 +
kernel/sched/cputime.c | 21 +++++++++++++++------
2 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h
index 512104b0ff49..24a54a6151ba 100644
--- a/include/linux/kernel_stat.h
+++ b/include/linux/kernel_stat.h
@@ -39,6 +39,7 @@ struct kernel_cpustat {
bool idle_elapse;
seqcount_t idle_sleeptime_seq;
u64 idle_entrytime;
+ u64 idle_stealtime;
#endif
u64 cpustat[NR_STATS];
};
diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c
index a77b6f1dbdca..4ae779b161b5 100644
--- a/kernel/sched/cputime.c
+++ b/kernel/sched/cputime.c
@@ -425,19 +425,25 @@ static inline void irqtime_account_process_tick(struct task_struct *p, int user_
static void kcpustat_idle_stop(struct kernel_cpustat *kc, u64 now)
{
u64 *cpustat = kc->cpustat;
- u64 delta;
+ u64 delta, steal, steal_delta;
if (!kc->idle_elapse)
return;
delta = now - kc->idle_entrytime;
+ steal = steal_account_process_time(delta);
write_seqcount_begin(&kc->idle_sleeptime_seq);
+ steal_delta = min_t(u64, kc->idle_stealtime, delta);
+ delta -= steal_delta;
+ kc->idle_stealtime -= steal_delta;
+
if (nr_iowait_cpu(smp_processor_id()) > 0)
cpustat[CPUTIME_IOWAIT] += delta;
else
cpustat[CPUTIME_IDLE] += delta;
+ kc->idle_stealtime += steal;
kc->idle_entrytime = now;
kc->idle_elapse = false;
write_seqcount_end(&kc->idle_sleeptime_seq);
@@ -460,7 +466,6 @@ void kcpustat_dyntick_stop(u64 now)
kcpustat_idle_stop(kc, now);
kc->idle_dyntick = false;
vtime_dyntick_stop();
- steal_account_process_time(ULONG_MAX);
}
}
@@ -504,10 +509,14 @@ static u64 kcpustat_field_dyntick(int cpu, enum cpu_usage_stat idx,
do {
seq = read_seqcount_begin(&kc->idle_sleeptime_seq);
- if (kc->idle_elapse && compute_delta)
- idle = cpustat[idx] + (now - kc->idle_entrytime);
- else
- idle = cpustat[idx];
+ idle = cpustat[idx];
+
+ if (kc->idle_elapse && compute_delta) {
+ u64 delta = now - kc->idle_entrytime;
+
+ delta -= min_t(u64, kc->idle_stealtime, delta);
+ idle += delta;
+ }
} while (read_seqcount_retry(&kc->idle_sleeptime_seq, seq));
return idle;
--
2.53.0
^ permalink raw reply related
* [PATCH 13/14] sched/cputime: Handle idle irqtime gracefully
From: Frederic Weisbecker @ 2026-03-31 13:16 UTC (permalink / raw)
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, Shrikanth Hegde,
Steven Rostedt, Sven Schnelle, Thomas Gleixner, Uladzislau Rezki,
Valentin Schneider, Vasily Gorbik, Vincent Guittot, Viresh Kumar,
Xin Zhao, linux-pm, linux-s390, linuxppc-dev
In-Reply-To: <20260331131622.30505-1-frederic@kernel.org>
The dyntick-idle cputime accounting always assumes that IRQ time
accounting is enabled and consequently stops elapsing the idle time
during dyntick-idle IRQs.
This doesn't mix up well with disabled IRQ time accounting because then
idle IRQs become a cputime blind-spot. Also this feature is disabled
on most configurations and the overhead of pausing dyntick-idle
accounting while in idle IRQs could then be avoided.
Fix the situation with conditionally pausing dyntick-idle accounting
during idle IRQs only if neither native vtime (which does IRQ time
accounting) nor generic IRQ time accounting are enabled.
Also make sure that the accumulated IRQ time is not accidentally
substracted from later accounting.
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Tested-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
kernel/sched/cputime.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c
index 137e6b099fa9..a77b6f1dbdca 100644
--- a/kernel/sched/cputime.c
+++ b/kernel/sched/cputime.c
@@ -47,7 +47,8 @@ static void irqtime_account_delta(struct irqtime *irqtime, u64 delta,
u64_stats_update_begin(&irqtime->sync);
cpustat[idx] += delta;
irqtime->total += delta;
- irqtime->tick_delta += delta;
+ if (!kcpustat_idle_dyntick())
+ irqtime->tick_delta += delta;
u64_stats_update_end(&irqtime->sync);
}
@@ -478,7 +479,8 @@ void kcpustat_irq_enter(u64 now)
{
struct kernel_cpustat *kc = kcpustat_this_cpu;
- if (!vtime_generic_enabled_this_cpu())
+ if (!vtime_generic_enabled_this_cpu() &&
+ (irqtime_enabled() || vtime_accounting_enabled_this_cpu()))
kcpustat_idle_stop(kc, now);
}
@@ -486,7 +488,8 @@ void kcpustat_irq_exit(u64 now)
{
struct kernel_cpustat *kc = kcpustat_this_cpu;
- if (!vtime_generic_enabled_this_cpu())
+ if (!vtime_generic_enabled_this_cpu() &&
+ (irqtime_enabled() || vtime_accounting_enabled_this_cpu()))
kcpustat_idle_start(kc, now);
}
--
2.53.0
^ permalink raw reply related
* [PATCH 12/14] sched/cputime: Provide get_cpu_[idle|iowait]_time_us() off-case
From: Frederic Weisbecker @ 2026-03-31 13:16 UTC (permalink / raw)
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, Shrikanth Hegde,
Steven Rostedt, Sven Schnelle, Thomas Gleixner, Uladzislau Rezki,
Valentin Schneider, Vasily Gorbik, Vincent Guittot, Viresh Kumar,
Xin Zhao, linux-pm, linux-s390, linuxppc-dev
In-Reply-To: <20260331131622.30505-1-frederic@kernel.org>
The last reason why get_cpu_idle/iowait_time_us() may return -1 now is
if the config doesn't support nohz.
The ad-hoc replacement solution by cpufreq is to compute jiffies minus
the whole busy cputime. Although the intention should provide a coherent
low resolution estimation of the idle and iowait time, the
implementation is buggy because jiffies don't start at 0.
Just provide instead a real get_cpu_[idle|iowait]_time_us() offcase.
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Tested-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
drivers/cpufreq/cpufreq.c | 29 +----------------------------
include/linux/kernel_stat.h | 3 +++
include/linux/tick.h | 4 ----
kernel/sched/cputime.c | 12 +++++++++---
4 files changed, 13 insertions(+), 35 deletions(-)
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 277884d91913..1adffb8ccefd 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -130,38 +130,11 @@ struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy)
}
EXPORT_SYMBOL_GPL(get_governor_parent_kobj);
-static inline u64 get_cpu_idle_time_jiffy(unsigned int cpu, u64 *wall)
-{
- struct kernel_cpustat kcpustat;
- u64 cur_wall_time;
- u64 idle_time;
- u64 busy_time;
-
- cur_wall_time = jiffies64_to_nsecs(get_jiffies_64());
-
- kcpustat_cpu_fetch(&kcpustat, cpu);
-
- busy_time = kcpustat.cpustat[CPUTIME_USER];
- busy_time += kcpustat.cpustat[CPUTIME_SYSTEM];
- busy_time += kcpustat.cpustat[CPUTIME_IRQ];
- busy_time += kcpustat.cpustat[CPUTIME_SOFTIRQ];
- busy_time += kcpustat.cpustat[CPUTIME_STEAL];
- busy_time += kcpustat.cpustat[CPUTIME_NICE];
-
- idle_time = cur_wall_time - busy_time;
- if (wall)
- *wall = div_u64(cur_wall_time, NSEC_PER_USEC);
-
- return div_u64(idle_time, NSEC_PER_USEC);
-}
-
u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy)
{
u64 idle_time = get_cpu_idle_time_us(cpu, io_busy ? wall : NULL);
- if (idle_time == -1ULL)
- return get_cpu_idle_time_jiffy(cpu, wall);
- else if (!io_busy)
+ if (!io_busy)
idle_time += get_cpu_iowait_time_us(cpu, wall);
return idle_time;
diff --git a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h
index 3680519d7b2c..512104b0ff49 100644
--- a/include/linux/kernel_stat.h
+++ b/include/linux/kernel_stat.h
@@ -133,6 +133,9 @@ static inline bool kcpustat_idle_dyntick(void)
}
#endif /* CONFIG_NO_HZ_COMMON */
+extern u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time);
+extern u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time);
+
/* Fetch cputime values when vtime is disabled on a CPU */
static inline u64 kcpustat_field_default(enum cpu_usage_stat usage, int cpu)
{
diff --git a/include/linux/tick.h b/include/linux/tick.h
index 738007d6f577..1cf4651f09ad 100644
--- a/include/linux/tick.h
+++ b/include/linux/tick.h
@@ -139,8 +139,6 @@ extern bool tick_nohz_idle_got_tick(void);
extern ktime_t tick_nohz_get_next_hrtimer(void);
extern ktime_t tick_nohz_get_sleep_length(ktime_t *delta_next);
extern unsigned long tick_nohz_get_idle_calls_cpu(int cpu);
-extern u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time);
-extern u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time);
#else /* !CONFIG_NO_HZ_COMMON */
#define tick_nohz_enabled (0)
static inline bool tick_nohz_is_active(void) { return false; }
@@ -162,8 +160,6 @@ static inline ktime_t tick_nohz_get_sleep_length(ktime_t *delta_next)
*delta_next = TICK_NSEC;
return *delta_next;
}
-static inline u64 get_cpu_idle_time_us(int cpu, u64 *unused) { return -1; }
-static inline u64 get_cpu_iowait_time_us(int cpu, u64 *unused) { return -1; }
#endif /* !CONFIG_NO_HZ_COMMON */
/*
diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c
index ebb9eee049e0..137e6b099fa9 100644
--- a/kernel/sched/cputime.c
+++ b/kernel/sched/cputime.c
@@ -523,6 +523,13 @@ u64 kcpustat_field_iowait(int cpu)
nr_iowait_cpu(cpu), ktime_get());
}
EXPORT_SYMBOL_GPL(kcpustat_field_iowait);
+#else
+static u64 kcpustat_field_dyntick(int cpu, enum cpu_usage_stat idx,
+ bool compute_delta, ktime_t now)
+{
+ return kcpustat_cpu(cpu).cpustat[idx];
+}
+#endif /* CONFIG_NO_HZ_COMMON */
static u64 get_cpu_sleep_time_us(int cpu, enum cpu_usage_stat idx,
bool compute_delta, u64 *last_update_time)
@@ -558,7 +565,7 @@ static u64 get_cpu_sleep_time_us(int cpu, enum cpu_usage_stat idx,
* This time is measured via accounting rather than sampling,
* and is as accurate as ktime_get() is.
*
- * Return: -1 if generic vtime is enabled, else total idle time of the @cpu
+ * Return: total idle time of the @cpu
*/
u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time)
{
@@ -582,7 +589,7 @@ EXPORT_SYMBOL_GPL(get_cpu_idle_time_us);
* This time is measured via accounting rather than sampling,
* and is as accurate as ktime_get() is.
*
- * Return: -1 if generic vtime is enabled, else total iowait time of @cpu
+ * Return: total iowait time of @cpu
*/
u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time)
{
@@ -590,7 +597,6 @@ u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time)
nr_iowait_cpu(cpu), last_update_time);
}
EXPORT_SYMBOL_GPL(get_cpu_iowait_time_us);
-#endif /* CONFIG_NO_HZ_COMMON */
/*
* Use precise platform statistics if available:
--
2.53.0
^ permalink raw reply related
* [PATCH 11/14] tick/sched: Consolidate idle time fetching APIs
From: Frederic Weisbecker @ 2026-03-31 13:16 UTC (permalink / raw)
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, Shrikanth Hegde,
Steven Rostedt, Sven Schnelle, Thomas Gleixner, Uladzislau Rezki,
Valentin Schneider, Vasily Gorbik, Vincent Guittot, Viresh Kumar,
Xin Zhao, linux-pm, linux-s390, linuxppc-dev
In-Reply-To: <20260331131622.30505-1-frederic@kernel.org>
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(), get_idle/iowait_time() and
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=n 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 <frederic@kernel.org>
Tested-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
fs/proc/stat.c | 40 +++---------------------
fs/proc/uptime.c | 8 ++---
include/linux/kernel_stat.h | 34 ++++++++++++++++++---
kernel/sched/cputime.c | 61 ++++++++++++++++++++++++-------------
4 files changed, 76 insertions(+), 67 deletions(-)
diff --git a/fs/proc/stat.c b/fs/proc/stat.c
index 8b444e862319..c00468a83f64 100644
--- a/fs/proc/stat.c
+++ b/fs/proc/stat.c
@@ -22,38 +22,6 @@
#define arch_irq_stat() 0
#endif
-u64 get_idle_time(struct kernel_cpustat *kcs, int cpu)
-{
- u64 idle, idle_usecs = -1ULL;
-
- if (cpu_online(cpu))
- idle_usecs = get_cpu_idle_time_us(cpu, NULL);
-
- if (idle_usecs == -1ULL)
- /* !NO_HZ or cpu offline so we can rely on cpustat.idle */
- idle = kcs->cpustat[CPUTIME_IDLE];
- else
- idle = idle_usecs * NSEC_PER_USEC;
-
- return idle;
-}
-
-static u64 get_iowait_time(struct kernel_cpustat *kcs, int cpu)
-{
- u64 iowait, iowait_usecs = -1ULL;
-
- if (cpu_online(cpu))
- iowait_usecs = get_cpu_iowait_time_us(cpu, NULL);
-
- if (iowait_usecs == -1ULL)
- /* !NO_HZ or cpu offline so we can rely on cpustat.iowait */
- iowait = kcs->cpustat[CPUTIME_IOWAIT];
- else
- iowait = iowait_usecs * NSEC_PER_USEC;
-
- return iowait;
-}
-
static void show_irq_gap(struct seq_file *p, unsigned int gap)
{
static const char zeros[] = " 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 += cpustat[CPUTIME_USER];
nice += cpustat[CPUTIME_NICE];
system += cpustat[CPUTIME_SYSTEM];
- idle += get_idle_time(&kcpustat, i);
- iowait += get_iowait_time(&kcpustat, i);
+ idle += cpustat[CPUTIME_IDLE];
+ iowait += cpustat[CPUTIME_IOWAIT];
irq += cpustat[CPUTIME_IRQ];
softirq += cpustat[CPUTIME_SOFTIRQ];
steal += cpustat[CPUTIME_STEAL];
@@ -146,8 +114,8 @@ static int show_stat(struct seq_file *p, void *v)
user = cpustat[CPUTIME_USER];
nice = cpustat[CPUTIME_NICE];
system = cpustat[CPUTIME_SYSTEM];
- idle = get_idle_time(&kcpustat, i);
- iowait = get_iowait_time(&kcpustat, i);
+ idle = cpustat[CPUTIME_IDLE];
+ iowait = cpustat[CPUTIME_IOWAIT];
irq = cpustat[CPUTIME_IRQ];
softirq = cpustat[CPUTIME_SOFTIRQ];
steal = 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;
idle_nsec = 0;
- for_each_possible_cpu(i) {
- struct kernel_cpustat kcs;
-
- kcpustat_cpu_fetch(&kcs, i);
- idle_nsec += get_idle_time(&kcs, i);
- }
+ for_each_possible_cpu(i)
+ idle_nsec += kcpustat_field(CPUTIME_IDLE, i);
ktime_get_boottime_ts64(&uptime);
timens_add_boottime(&uptime);
diff --git a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h
index 9343353ac7a3..3680519d7b2c 100644
--- a/include/linux/kernel_stat.h
+++ b/include/linux/kernel_stat.h
@@ -110,32 +110,59 @@ extern void kcpustat_dyntick_start(u64 now);
extern void kcpustat_dyntick_stop(u64 now);
extern void kcpustat_irq_enter(u64 now);
extern void kcpustat_irq_exit(u64 now);
+extern u64 kcpustat_field_idle(int cpu);
+extern u64 kcpustat_field_iowait(int cpu);
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 */
+/* Fetch cputime values when vtime is disabled on a CPU */
+static inline u64 kcpustat_field_default(enum cpu_usage_stat usage, int cpu)
+{
+ if (usage == CPUTIME_IDLE)
+ return kcpustat_field_idle(cpu);
+ if (usage == 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 = kcpustat_cpu(cpu);
+ dst->cpustat[CPUTIME_IDLE] = kcpustat_field_idle(cpu);
+ dst->cpustat[CPUTIME_IOWAIT] = 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);
}
static inline void kcpustat_cpu_fetch(struct kernel_cpustat *dst, int cpu)
{
- *dst = kcpustat_cpu(cpu);
+ kcpustat_cpu_fetch_default(dst, cpu);
}
-
#endif /* !CONFIG_VIRT_CPU_ACCOUNTING_GEN */
extern void account_user_time(struct task_struct *, u64);
@@ -145,7 +172,6 @@ extern void account_system_index_time(struct task_struct *, 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);
#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 4a259f2700a1..ebb9eee049e0 100644
--- a/kernel/sched/cputime.c
+++ b/kernel/sched/cputime.c
@@ -490,24 +490,14 @@ void kcpustat_irq_exit(u64 now)
kcpustat_idle_start(kc, now);
}
-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, u64 now)
{
struct kernel_cpustat *kc = &kcpustat_cpu(cpu);
u64 *cpustat = kc->cpustat;
unsigned int seq;
- ktime_t now;
u64 idle;
- now = ktime_get();
- if (last_update_time)
- *last_update_time = ktime_to_us(now);
-
- if (vtime_generic_enabled_cpu(cpu)) {
- idle = kcpustat_field(idx, cpu);
- goto to_us;
- }
-
do {
seq = read_seqcount_begin(&kc->idle_sleeptime_seq);
@@ -517,12 +507,42 @@ static u64 get_cpu_sleep_time_us(int cpu, enum cpu_usage_stat idx,
idle = cpustat[idx];
} while (read_seqcount_retry(&kc->idle_sleeptime_seq, seq));
-to_us:
- do_div(idle, NSEC_PER_USEC);
-
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 = ktime_get();
+ u64 res;
+
+ if (vtime_generic_enabled_cpu(cpu))
+ res = kcpustat_field(idx, cpu);
+ else
+ res = kcpustat_field_dyntick(cpu, idx, compute_delta, now);
+
+ do_div(res, NSEC_PER_USEC);
+
+ if (last_update_time)
+ *last_update_time = ktime_to_us(now);
+
+ return res;
+}
+
/**
* get_cpu_idle_time_us - get the total idle time of a CPU
* @cpu: CPU number to query
@@ -570,7 +590,6 @@ u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time)
nr_iowait_cpu(cpu), last_update_time);
}
EXPORT_SYMBOL_GPL(get_cpu_iowait_time_us);
-
#endif /* CONFIG_NO_HZ_COMMON */
/*
@@ -1124,8 +1143,8 @@ u64 kcpustat_field(enum cpu_usage_stat usage, int cpu)
struct rq *rq;
int err;
- if (!vtime_accounting_enabled_cpu(cpu))
- return val;
+ if (!vtime_generic_enabled_cpu(cpu))
+ return kcpustat_field_default(usage, cpu);
rq = cpu_rq(cpu);
@@ -1220,8 +1239,8 @@ void kcpustat_cpu_fetch(struct kernel_cpustat *dst, int cpu)
struct rq *rq;
int err;
- if (!vtime_accounting_enabled_cpu(cpu)) {
- *dst = *src;
+ if (!vtime_generic_enabled_cpu(cpu)) {
+ kcpustat_cpu_fetch_default(dst, cpu);
return;
}
@@ -1234,7 +1253,7 @@ void kcpustat_cpu_fetch(struct kernel_cpustat *dst, int cpu)
curr = rcu_dereference(rq->curr);
if (WARN_ON_ONCE(!curr)) {
rcu_read_unlock();
- *dst = *src;
+ kcpustat_cpu_fetch_default(dst, cpu);
return;
}
--
2.53.0
^ permalink raw reply related
* [PATCH 10/14] tick/sched: Account tickless idle cputime only when tick is stopped
From: Frederic Weisbecker @ 2026-03-31 13:16 UTC (permalink / raw)
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, Shrikanth Hegde,
Steven Rostedt, Sven Schnelle, Thomas Gleixner, Uladzislau Rezki,
Valentin Schneider, Vasily Gorbik, Vincent Guittot, Viresh Kumar,
Xin Zhao, linux-pm, linux-s390, linuxppc-dev
In-Reply-To: <20260331131622.30505-1-frederic@kernel.org>
There is no real point in switching to dyntick-idle cputime accounting
mode if the tick is not actually stopped. This just adds overhead,
notably fetching the GTOD, on each idle exit and each idle IRQ entry for
no reason during short idle trips.
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Tested-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
kernel/time/tick-sched.c | 44 ++++++++++++++++++----------------------
1 file changed, 20 insertions(+), 24 deletions(-)
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index 0da83d9b324a..923cae35827a 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -1144,8 +1144,10 @@ void tick_nohz_idle_stop_tick(void)
ts->idle_sleeps++;
ts->idle_expires = expires;
- if (!was_stopped && tick_sched_flag_test(ts, TS_FLAG_STOPPED))
+ if (!was_stopped && tick_sched_flag_test(ts, TS_FLAG_STOPPED)) {
+ kcpustat_dyntick_start(ts->idle_entrytime);
nohz_balance_enter_idle(cpu);
+ }
} else {
tick_nohz_retain_tick(ts);
}
@@ -1187,7 +1189,6 @@ void tick_nohz_idle_enter(void)
WARN_ON_ONCE(ts->timer_expires_base);
tick_sched_flag_set(ts, TS_FLAG_INIDLE);
ts->idle_entrytime = ktime_get();
- kcpustat_dyntick_start(ts->idle_entrytime);
tick_nohz_clock_sleep(ts);
local_irq_enable();
@@ -1217,9 +1218,10 @@ void tick_nohz_irq_exit(void)
struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
if (tick_sched_flag_test(ts, TS_FLAG_INIDLE)) {
- ts->idle_entrytime = ktime_get();
- kcpustat_irq_exit(ts->idle_entrytime);
tick_nohz_clock_sleep(ts);
+ ts->idle_entrytime = ktime_get();
+ if (tick_sched_flag_test(ts, TS_FLAG_STOPPED))
+ kcpustat_irq_exit(ts->idle_entrytime);
} else {
tick_nohz_full_update_tick(ts);
}
@@ -1320,8 +1322,11 @@ void tick_nohz_idle_restart_tick(void)
{
struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
- if (tick_sched_flag_test(ts, TS_FLAG_STOPPED))
- tick_nohz_restart_sched_tick(ts, ktime_get());
+ if (tick_sched_flag_test(ts, TS_FLAG_STOPPED)) {
+ ktime_t now = ktime_get();
+ kcpustat_dyntick_stop(now);
+ tick_nohz_restart_sched_tick(ts, now);
+ }
}
static void tick_nohz_idle_update_tick(struct tick_sched *ts, ktime_t now)
@@ -1351,7 +1356,6 @@ static void tick_nohz_idle_update_tick(struct tick_sched *ts, ktime_t now)
void tick_nohz_idle_exit(void)
{
struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
- bool idle_active, tick_stopped;
ktime_t now;
local_irq_disable();
@@ -1360,18 +1364,13 @@ void tick_nohz_idle_exit(void)
WARN_ON_ONCE(ts->timer_expires_base);
tick_sched_flag_clear(ts, TS_FLAG_INIDLE);
- idle_active = tick_sched_flag_test(ts, TS_FLAG_IDLE_ACTIVE);
- tick_stopped = tick_sched_flag_test(ts, TS_FLAG_STOPPED);
+ tick_nohz_clock_wakeup(ts);
- if (idle_active || tick_stopped)
+ if (tick_sched_flag_test(ts, TS_FLAG_STOPPED)) {
now = ktime_get();
-
- if (idle_active)
- tick_nohz_clock_wakeup(ts);
-
- if (tick_stopped)
+ kcpustat_dyntick_stop(now);
tick_nohz_idle_update_tick(ts, now);
- kcpustat_dyntick_stop(now);
+ }
local_irq_enable();
}
@@ -1425,15 +1424,13 @@ static inline void tick_nohz_irq_enter(void)
struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
ktime_t now;
- if (!tick_sched_flag_test(ts, TS_FLAG_STOPPED | TS_FLAG_IDLE_ACTIVE))
+ tick_nohz_clock_wakeup(ts);
+
+ if (!tick_sched_flag_test(ts, TS_FLAG_STOPPED))
return;
now = ktime_get();
-
- if (tick_sched_flag_test(ts, TS_FLAG_IDLE_ACTIVE)) {
- tick_nohz_clock_wakeup(ts);
- kcpustat_irq_enter(now);
- }
+ kcpustat_irq_enter(now);
/*
* If all CPUs are idle we may need to update a stale jiffies value.
@@ -1442,8 +1439,7 @@ static inline void tick_nohz_irq_enter(void)
* rare case (typically stop machine). So we must make sure we have a
* last resort.
*/
- if (tick_sched_flag_test(ts, TS_FLAG_STOPPED))
- tick_nohz_update_jiffies(now);
+ tick_nohz_update_jiffies(now);
}
#else
--
2.53.0
^ permalink raw reply related
* [PATCH 09/14] tick/sched: Remove unused fields
From: Frederic Weisbecker @ 2026-03-31 13:16 UTC (permalink / raw)
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, Shrikanth Hegde,
Steven Rostedt, Sven Schnelle, Thomas Gleixner, Uladzislau Rezki,
Valentin Schneider, Vasily Gorbik, Vincent Guittot, Viresh Kumar,
Xin Zhao, linux-pm, linux-s390, linuxppc-dev
In-Reply-To: <20260331131622.30505-1-frederic@kernel.org>
Remove fields after the dyntick-idle cputime migration to scheduler
code.
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Tested-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
kernel/time/tick-sched.h | 12 ------------
kernel/time/timer_list.c | 6 +-----
scripts/gdb/linux/timerlist.py | 4 ----
3 files changed, 1 insertion(+), 21 deletions(-)
diff --git a/kernel/time/tick-sched.h b/kernel/time/tick-sched.h
index b4a7822f495d..79b9252047b1 100644
--- a/kernel/time/tick-sched.h
+++ b/kernel/time/tick-sched.h
@@ -44,9 +44,7 @@ struct tick_device {
* to resume the tick timer operation in the timeline
* when the CPU returns from nohz sleep.
* @next_tick: Next tick to be fired when in dynticks mode.
- * @idle_jiffies: jiffies at the entry to idle for idle time accounting
* @idle_waketime: Time when the idle was interrupted
- * @idle_sleeptime_seq: sequence counter for data consistency
* @idle_entrytime: Time when the idle call was entered
* @last_jiffies: Base jiffies snapshot when next event was last computed
* @timer_expires_base: Base time clock monotonic for @timer_expires
@@ -55,9 +53,6 @@ struct tick_device {
* @idle_expires: Next tick in idle, for debugging purpose only
* @idle_calls: Total number of idle calls
* @idle_sleeps: Number of idle calls, where the sched tick was stopped
- * @idle_exittime: Time when the idle state was left
- * @idle_sleeptime: Sum of the time slept in idle with sched tick stopped
- * @iowait_sleeptime: Sum of the time slept in idle with sched tick stopped, with IO outstanding
* @tick_dep_mask: Tick dependency mask - is set, if someone needs the tick
* @check_clocks: Notification mechanism about clocksource changes
*/
@@ -73,12 +68,10 @@ struct tick_sched {
struct hrtimer sched_timer;
ktime_t last_tick;
ktime_t next_tick;
- unsigned long idle_jiffies;
ktime_t idle_waketime;
unsigned int got_idle_tick;
/* Idle entry */
- seqcount_t idle_sleeptime_seq;
ktime_t idle_entrytime;
/* Tick stop */
@@ -90,11 +83,6 @@ struct tick_sched {
unsigned long idle_calls;
unsigned long idle_sleeps;
- /* Idle exit */
- ktime_t idle_exittime;
- ktime_t idle_sleeptime;
- ktime_t iowait_sleeptime;
-
/* Full dynticks handling */
atomic_t tick_dep_mask;
diff --git a/kernel/time/timer_list.c b/kernel/time/timer_list.c
index 488e47e96e93..e77b512e8597 100644
--- a/kernel/time/timer_list.c
+++ b/kernel/time/timer_list.c
@@ -154,14 +154,10 @@ static void print_cpu(struct seq_file *m, int cpu, u64 now)
P_flag(highres, TS_FLAG_HIGHRES);
P_ns(last_tick);
P_flag(tick_stopped, TS_FLAG_STOPPED);
- P(idle_jiffies);
P(idle_calls);
P(idle_sleeps);
P_ns(idle_entrytime);
P_ns(idle_waketime);
- P_ns(idle_exittime);
- P_ns(idle_sleeptime);
- P_ns(iowait_sleeptime);
P(last_jiffies);
P(next_timer);
P_ns(idle_expires);
@@ -258,7 +254,7 @@ static void timer_list_show_tickdevices_header(struct seq_file *m)
static inline void timer_list_header(struct seq_file *m, u64 now)
{
- SEQ_printf(m, "Timer List Version: v0.10\n");
+ SEQ_printf(m, "Timer List Version: v0.11\n");
SEQ_printf(m, "HRTIMER_MAX_CLOCK_BASES: %d\n", HRTIMER_MAX_CLOCK_BASES);
SEQ_printf(m, "now at %Ld nsecs\n", (unsigned long long)now);
SEQ_printf(m, "\n");
diff --git a/scripts/gdb/linux/timerlist.py b/scripts/gdb/linux/timerlist.py
index ccc24d30de80..c14ce55674c9 100644
--- a/scripts/gdb/linux/timerlist.py
+++ b/scripts/gdb/linux/timerlist.py
@@ -90,14 +90,10 @@ def print_cpu(hrtimer_bases, cpu, max_clock_bases):
text += f" .{'nohz':15s}: {int(bool(ts['flags'] & TS_FLAG_NOHZ))}\n"
text += f" .{'last_tick':15s}: {ts['last_tick']}\n"
text += f" .{'tick_stopped':15s}: {int(bool(ts['flags'] & TS_FLAG_STOPPED))}\n"
- text += f" .{'idle_jiffies':15s}: {ts['idle_jiffies']}\n"
text += f" .{'idle_calls':15s}: {ts['idle_calls']}\n"
text += f" .{'idle_sleeps':15s}: {ts['idle_sleeps']}\n"
text += f" .{'idle_entrytime':15s}: {ts['idle_entrytime']} nsecs\n"
text += f" .{'idle_waketime':15s}: {ts['idle_waketime']} nsecs\n"
- text += f" .{'idle_exittime':15s}: {ts['idle_exittime']} nsecs\n"
- text += f" .{'idle_sleeptime':15s}: {ts['idle_sleeptime']} nsecs\n"
- text += f" .{'iowait_sleeptime':15s}: {ts['iowait_sleeptime']} nsecs\n"
text += f" .{'last_jiffies':15s}: {ts['last_jiffies']}\n"
text += f" .{'next_timer':15s}: {ts['next_timer']}\n"
text += f" .{'idle_expires':15s}: {ts['idle_expires']} nsecs\n"
--
2.53.0
^ permalink raw reply related
* [PATCH 08/14] tick/sched: Move dyntick-idle cputime accounting to cputime code
From: Frederic Weisbecker @ 2026-03-31 13:16 UTC (permalink / raw)
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, Shrikanth Hegde,
Steven Rostedt, Sven Schnelle, Thomas Gleixner, Uladzislau Rezki,
Valentin Schneider, Vasily Gorbik, Vincent Guittot, Viresh Kumar,
Xin Zhao, linux-pm, linux-s390, linuxppc-dev
In-Reply-To: <20260331131622.30505-1-frederic@kernel.org>
Although the dynticks-idle cputime accounting is necessarily tied to
the tick subsystem, the actual related accounting code has no business
residing there and should be part of the scheduler cputime code.
Move away the relevant pieces and state machine to where they belong.
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Tested-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
include/linux/kernel_stat.h | 14 +++-
kernel/sched/cputime.c | 149 +++++++++++++++++++++++++++++++--
kernel/time/tick-sched.c | 162 +++++++-----------------------------
3 files changed, 184 insertions(+), 141 deletions(-)
diff --git a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h
index ba65aad308a1..9343353ac7a3 100644
--- a/include/linux/kernel_stat.h
+++ b/include/linux/kernel_stat.h
@@ -35,9 +35,12 @@ enum cpu_usage_stat {
struct kernel_cpustat {
#ifdef CONFIG_NO_HZ_COMMON
- int idle_dyntick;
+ bool idle_dyntick;
+ bool idle_elapse;
+ seqcount_t idle_sleeptime_seq;
+ u64 idle_entrytime;
#endif
- u64 cpustat[NR_STATS];
+ u64 cpustat[NR_STATS];
};
struct kernel_stat {
@@ -103,8 +106,11 @@ static inline unsigned long kstat_cpu_irqs_sum(unsigned int cpu)
}
#ifdef CONFIG_NO_HZ_COMMON
-extern void kcpustat_dyntick_start(void);
-extern void kcpustat_dyntick_stop(void);
+extern void kcpustat_dyntick_start(u64 now);
+extern void kcpustat_dyntick_stop(u64 now);
+extern void kcpustat_irq_enter(u64 now);
+extern void kcpustat_irq_exit(u64 now);
+
static inline bool kcpustat_idle_dyntick(void)
{
return __this_cpu_read(kernel_cpustat.idle_dyntick);
diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c
index 4934c537f5e3..4a259f2700a1 100644
--- a/kernel/sched/cputime.c
+++ b/kernel/sched/cputime.c
@@ -2,6 +2,7 @@
/*
* Simple CPU accounting cgroup controller
*/
+#include <linux/sched/clock.h>
#include <linux/sched/cputime.h>
#include <linux/tsacct_kern.h>
#include "sched.h"
@@ -420,22 +421,156 @@ static inline void irqtime_account_process_tick(struct task_struct *p, int user_
#endif /* !CONFIG_IRQ_TIME_ACCOUNTING */
#ifdef CONFIG_NO_HZ_COMMON
-void kcpustat_dyntick_start(void)
+static void kcpustat_idle_stop(struct kernel_cpustat *kc, u64 now)
{
- if (!vtime_generic_enabled_this_cpu()) {
- vtime_dyntick_start();
- __this_cpu_write(kernel_cpustat.idle_dyntick, 1);
- }
+ u64 *cpustat = kc->cpustat;
+ u64 delta;
+
+ if (!kc->idle_elapse)
+ return;
+
+ delta = now - kc->idle_entrytime;
+
+ write_seqcount_begin(&kc->idle_sleeptime_seq);
+ if (nr_iowait_cpu(smp_processor_id()) > 0)
+ cpustat[CPUTIME_IOWAIT] += delta;
+ else
+ cpustat[CPUTIME_IDLE] += delta;
+
+ kc->idle_entrytime = now;
+ kc->idle_elapse = false;
+ write_seqcount_end(&kc->idle_sleeptime_seq);
}
-void kcpustat_dyntick_stop(void)
+static void kcpustat_idle_start(struct kernel_cpustat *kc, u64 now)
{
+ write_seqcount_begin(&kc->idle_sleeptime_seq);
+ kc->idle_entrytime = now;
+ kc->idle_elapse = true;
+ write_seqcount_end(&kc->idle_sleeptime_seq);
+}
+
+void kcpustat_dyntick_stop(u64 now)
+{
+ struct kernel_cpustat *kc = kcpustat_this_cpu;
+
if (!vtime_generic_enabled_this_cpu()) {
- __this_cpu_write(kernel_cpustat.idle_dyntick, 0);
+ WARN_ON_ONCE(!kc->idle_dyntick);
+ kcpustat_idle_stop(kc, now);
+ kc->idle_dyntick = false;
vtime_dyntick_stop();
steal_account_process_time(ULONG_MAX);
}
}
+
+void kcpustat_dyntick_start(u64 now)
+{
+ struct kernel_cpustat *kc = kcpustat_this_cpu;
+
+ if (!vtime_generic_enabled_this_cpu()) {
+ vtime_dyntick_start();
+ kc->idle_dyntick = true;
+ kcpustat_idle_start(kc, now);
+ }
+}
+
+void kcpustat_irq_enter(u64 now)
+{
+ struct kernel_cpustat *kc = kcpustat_this_cpu;
+
+ if (!vtime_generic_enabled_this_cpu())
+ kcpustat_idle_stop(kc, now);
+}
+
+void kcpustat_irq_exit(u64 now)
+{
+ struct kernel_cpustat *kc = kcpustat_this_cpu;
+
+ if (!vtime_generic_enabled_this_cpu())
+ kcpustat_idle_start(kc, now);
+}
+
+static u64 get_cpu_sleep_time_us(int cpu, enum cpu_usage_stat idx,
+ bool compute_delta, u64 *last_update_time)
+{
+ struct kernel_cpustat *kc = &kcpustat_cpu(cpu);
+ u64 *cpustat = kc->cpustat;
+ unsigned int seq;
+ ktime_t now;
+ u64 idle;
+
+ now = ktime_get();
+ if (last_update_time)
+ *last_update_time = ktime_to_us(now);
+
+ if (vtime_generic_enabled_cpu(cpu)) {
+ idle = kcpustat_field(idx, cpu);
+ goto to_us;
+ }
+
+ do {
+ seq = read_seqcount_begin(&kc->idle_sleeptime_seq);
+
+ if (kc->idle_elapse && compute_delta)
+ idle = cpustat[idx] + (now - kc->idle_entrytime);
+ else
+ idle = cpustat[idx];
+ } while (read_seqcount_retry(&kc->idle_sleeptime_seq, seq));
+
+to_us:
+ do_div(idle, NSEC_PER_USEC);
+
+ return idle;
+}
+
+/**
+ * get_cpu_idle_time_us - get the total idle time of a CPU
+ * @cpu: CPU number to query
+ * @last_update_time: variable to store update time in. Do not update
+ * counters if NULL.
+ *
+ * Return the cumulative idle time (since boot) for a given
+ * CPU, in microseconds. Note that this is partially broken due to
+ * the counter of iowait tasks that can be remotely updated without
+ * any synchronization. Therefore it is possible to observe backward
+ * values within two consecutive reads.
+ *
+ * This time is measured via accounting rather than sampling,
+ * and is as accurate as ktime_get() is.
+ *
+ * Return: -1 if generic vtime is enabled, else total idle time of the @cpu
+ */
+u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time)
+{
+ return get_cpu_sleep_time_us(cpu, CPUTIME_IDLE,
+ !nr_iowait_cpu(cpu), last_update_time);
+}
+EXPORT_SYMBOL_GPL(get_cpu_idle_time_us);
+
+/**
+ * get_cpu_iowait_time_us - get the total iowait time of a CPU
+ * @cpu: CPU number to query
+ * @last_update_time: variable to store update time in. Do not update
+ * counters if NULL.
+ *
+ * Return the cumulative iowait time (since boot) for a given
+ * CPU, in microseconds. Note this is partially broken due to
+ * the counter of iowait tasks that can be remotely updated without
+ * any synchronization. Therefore it is possible to observe backward
+ * values within two consecutive reads.
+ *
+ * This time is measured via accounting rather than sampling,
+ * and is as accurate as ktime_get() is.
+ *
+ * Return: -1 if generic vtime is enabled, else total iowait time of @cpu
+ */
+u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time)
+{
+ return get_cpu_sleep_time_us(cpu, CPUTIME_IOWAIT,
+ nr_iowait_cpu(cpu), last_update_time);
+}
+EXPORT_SYMBOL_GPL(get_cpu_iowait_time_us);
+
#endif /* CONFIG_NO_HZ_COMMON */
/*
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index 6cd7777755dd..0da83d9b324a 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -749,125 +749,6 @@ static void tick_nohz_update_jiffies(ktime_t now)
touch_softlockup_watchdog_sched();
}
-static void tick_nohz_stop_idle(struct tick_sched *ts, ktime_t now)
-{
- u64 *cpustat = kcpustat_this_cpu->cpustat;
- ktime_t delta;
-
- if (vtime_generic_enabled_this_cpu())
- return;
-
- if (WARN_ON_ONCE(!tick_sched_flag_test(ts, TS_FLAG_IDLE_ACTIVE)))
- return;
-
- delta = ktime_sub(now, ts->idle_entrytime);
-
- write_seqcount_begin(&ts->idle_sleeptime_seq);
- if (nr_iowait_cpu(smp_processor_id()) > 0)
- cpustat[CPUTIME_IOWAIT] = ktime_add(cpustat[CPUTIME_IOWAIT], delta);
- else
- cpustat[CPUTIME_IDLE] = ktime_add(cpustat[CPUTIME_IDLE], delta);
-
- ts->idle_entrytime = now;
- tick_sched_flag_clear(ts, TS_FLAG_IDLE_ACTIVE);
- write_seqcount_end(&ts->idle_sleeptime_seq);
-
- sched_clock_idle_wakeup_event();
-}
-
-static void tick_nohz_start_idle(struct tick_sched *ts)
-{
- if (vtime_generic_enabled_this_cpu())
- return;
-
- write_seqcount_begin(&ts->idle_sleeptime_seq);
- ts->idle_entrytime = ktime_get();
- tick_sched_flag_set(ts, TS_FLAG_IDLE_ACTIVE);
- write_seqcount_end(&ts->idle_sleeptime_seq);
- sched_clock_idle_sleep_event();
-}
-
-static u64 get_cpu_sleep_time_us(int cpu, enum cpu_usage_stat idx,
- bool compute_delta, u64 *last_update_time)
-{
- struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
- u64 *cpustat = kcpustat_cpu(cpu).cpustat;
- ktime_t now, idle;
- unsigned int seq;
-
- now = ktime_get();
- if (last_update_time)
- *last_update_time = ktime_to_us(now);
-
- if (vtime_generic_enabled_cpu(cpu)) {
- idle = kcpustat_field(idx, cpu);
- return ktime_to_us(idle);
- }
-
- do {
- seq = read_seqcount_begin(&ts->idle_sleeptime_seq);
-
- if (tick_sched_flag_test(ts, TS_FLAG_IDLE_ACTIVE) && compute_delta) {
- ktime_t delta = ktime_sub(now, ts->idle_entrytime);
-
- idle = ktime_add(cpustat[idx], delta);
- } else {
- idle = cpustat[idx];
- }
- } while (read_seqcount_retry(&ts->idle_sleeptime_seq, seq));
-
- return ktime_to_us(idle);
-
-}
-
-/**
- * get_cpu_idle_time_us - get the total idle time of a CPU
- * @cpu: CPU number to query
- * @last_update_time: variable to store update time in. Do not update
- * counters if NULL.
- *
- * Return the cumulative idle time (since boot) for a given
- * CPU, in microseconds. Note that this is partially broken due to
- * the counter of iowait tasks that can be remotely updated without
- * any synchronization. Therefore it is possible to observe backward
- * values within two consecutive reads.
- *
- * This time is measured via accounting rather than sampling,
- * and is as accurate as ktime_get() is.
- *
- * Return: -1 if generic vtime is enabled, else total idle time of the @cpu
- */
-u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time)
-{
- return get_cpu_sleep_time_us(cpu, CPUTIME_IDLE,
- !nr_iowait_cpu(cpu), last_update_time);
-}
-EXPORT_SYMBOL_GPL(get_cpu_idle_time_us);
-
-/**
- * get_cpu_iowait_time_us - get the total iowait time of a CPU
- * @cpu: CPU number to query
- * @last_update_time: variable to store update time in. Do not update
- * counters if NULL.
- *
- * Return the cumulative iowait time (since boot) for a given
- * CPU, in microseconds. Note this is partially broken due to
- * the counter of iowait tasks that can be remotely updated without
- * any synchronization. Therefore it is possible to observe backward
- * values within two consecutive reads.
- *
- * This time is measured via accounting rather than sampling,
- * and is as accurate as ktime_get() is.
- *
- * Return: -1 if generic vtime is enabled, else total iowait time of @cpu
- */
-u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time)
-{
- return get_cpu_sleep_time_us(cpu, CPUTIME_IOWAIT,
- nr_iowait_cpu(cpu), last_update_time);
-}
-EXPORT_SYMBOL_GPL(get_cpu_iowait_time_us);
-
static void tick_nohz_restart(struct tick_sched *ts, ktime_t now)
{
hrtimer_cancel(&ts->sched_timer);
@@ -1275,6 +1156,20 @@ void tick_nohz_idle_retain_tick(void)
tick_nohz_retain_tick(this_cpu_ptr(&tick_cpu_sched));
}
+static void tick_nohz_clock_sleep(struct tick_sched *ts)
+{
+ tick_sched_flag_set(ts, TS_FLAG_IDLE_ACTIVE);
+ sched_clock_idle_sleep_event();
+}
+
+static void tick_nohz_clock_wakeup(struct tick_sched *ts)
+{
+ if (tick_sched_flag_test(ts, TS_FLAG_IDLE_ACTIVE)) {
+ tick_sched_flag_clear(ts, TS_FLAG_IDLE_ACTIVE);
+ sched_clock_idle_wakeup_event();
+ }
+}
+
/**
* tick_nohz_idle_enter - prepare for entering idle on the current CPU
*
@@ -1289,12 +1184,11 @@ void tick_nohz_idle_enter(void)
local_irq_disable();
ts = this_cpu_ptr(&tick_cpu_sched);
-
WARN_ON_ONCE(ts->timer_expires_base);
-
tick_sched_flag_set(ts, TS_FLAG_INIDLE);
- kcpustat_dyntick_start();
- tick_nohz_start_idle(ts);
+ ts->idle_entrytime = ktime_get();
+ kcpustat_dyntick_start(ts->idle_entrytime);
+ tick_nohz_clock_sleep(ts);
local_irq_enable();
}
@@ -1322,10 +1216,13 @@ void tick_nohz_irq_exit(void)
{
struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
- if (tick_sched_flag_test(ts, TS_FLAG_INIDLE))
- tick_nohz_start_idle(ts);
- else
+ if (tick_sched_flag_test(ts, TS_FLAG_INIDLE)) {
+ ts->idle_entrytime = ktime_get();
+ kcpustat_irq_exit(ts->idle_entrytime);
+ tick_nohz_clock_sleep(ts);
+ } else {
tick_nohz_full_update_tick(ts);
+ }
}
/**
@@ -1470,11 +1367,11 @@ void tick_nohz_idle_exit(void)
now = ktime_get();
if (idle_active)
- tick_nohz_stop_idle(ts, now);
+ tick_nohz_clock_wakeup(ts);
if (tick_stopped)
tick_nohz_idle_update_tick(ts, now);
- kcpustat_dyntick_stop();
+ kcpustat_dyntick_stop(now);
local_irq_enable();
}
@@ -1530,9 +1427,14 @@ static inline void tick_nohz_irq_enter(void)
if (!tick_sched_flag_test(ts, TS_FLAG_STOPPED | TS_FLAG_IDLE_ACTIVE))
return;
+
now = ktime_get();
- if (tick_sched_flag_test(ts, TS_FLAG_IDLE_ACTIVE))
- tick_nohz_stop_idle(ts, now);
+
+ if (tick_sched_flag_test(ts, TS_FLAG_IDLE_ACTIVE)) {
+ tick_nohz_clock_wakeup(ts);
+ kcpustat_irq_enter(now);
+ }
+
/*
* If all CPUs are idle we may need to update a stale jiffies value.
* Note nohz_full is a special case: a timekeeper is guaranteed to stay
--
2.53.0
^ permalink raw reply related
* [PATCH 07/14] tick/sched: Remove nohz disabled special case in cputime fetch
From: Frederic Weisbecker @ 2026-03-31 13:16 UTC (permalink / raw)
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, Shrikanth Hegde,
Steven Rostedt, Sven Schnelle, Thomas Gleixner, Uladzislau Rezki,
Valentin Schneider, Vasily Gorbik, Vincent Guittot, Viresh Kumar,
Xin Zhao, linux-pm, linux-s390, linuxppc-dev
In-Reply-To: <20260331131622.30505-1-frederic@kernel.org>
Even when nohz is not runtime enabled, the dynticks idle cputime
accounting can run and the common idle cputime accessors are still
relevant.
Remove the nohz disabled special case accordingly.
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Tested-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
kernel/time/tick-sched.c | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index 2c0f0b81f452..6cd7777755dd 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -795,9 +795,6 @@ static u64 get_cpu_sleep_time_us(int cpu, enum cpu_usage_stat idx,
ktime_t now, idle;
unsigned int seq;
- if (!tick_nohz_active)
- return -1;
-
now = ktime_get();
if (last_update_time)
*last_update_time = ktime_to_us(now);
@@ -838,7 +835,7 @@ static u64 get_cpu_sleep_time_us(int cpu, enum cpu_usage_stat idx,
* This time is measured via accounting rather than sampling,
* and is as accurate as ktime_get() is.
*
- * Return: -1 if NOHZ is not enabled, else total idle time of the @cpu
+ * Return: -1 if generic vtime is enabled, else total idle time of the @cpu
*/
u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time)
{
@@ -862,7 +859,7 @@ EXPORT_SYMBOL_GPL(get_cpu_idle_time_us);
* This time is measured via accounting rather than sampling,
* and is as accurate as ktime_get() is.
*
- * Return: -1 if NOHZ is not enabled, else total iowait time of @cpu
+ * Return: -1 if generic vtime is enabled, else total iowait time of @cpu
*/
u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time)
{
--
2.53.0
^ permalink raw reply related
* [PATCH 06/14] tick/sched: Unify idle cputime accounting
From: Frederic Weisbecker @ 2026-03-31 13:16 UTC (permalink / raw)
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, Shrikanth Hegde,
Steven Rostedt, Sven Schnelle, Thomas Gleixner, Uladzislau Rezki,
Valentin Schneider, Vasily Gorbik, Vincent Guittot, Viresh Kumar,
Xin Zhao, linux-pm, linux-s390, linuxppc-dev
In-Reply-To: <20260331131622.30505-1-frederic@kernel.org>
The non-vtime dynticks-idle cputime accounting is a big mess that
accumulates within two concurrent statistics, each having their own
shortcomings:
* The accounting for online CPUs which is based on the delta between
tick_nohz_start_idle() and tick_nohz_stop_idle().
Pros:
- Works when the tick is off
- Has nsecs granularity
Cons:
- Account idle steal time but doesn't substract it from idle
cputime.
- Assumes CONFIG_IRQ_TIME_ACCOUNTING by not accounting IRQs but
the IRQ time is simply ignored when
CONFIG_IRQ_TIME_ACCOUNTING=n
- The windows between 1) idle task scheduling and the first call
to tick_nohz_start_idle() and 2) idle task between the last
tick_nohz_stop_idle() and the rest of the idle time are
blindspots wrt. cputime accounting (though mostly insignificant
amount)
- Relies on private fields outside of kernel stats, with specific
accessors.
* The accounting for offline CPUs which is based on ticks and the
jiffies delta during which the tick was stopped.
Pros:
- Handles steal time correctly
- Handle CONFIG_IRQ_TIME_ACCOUNTING=y and
CONFIG_IRQ_TIME_ACCOUNTING=n correctly.
- Handles the whole idle task
- Accounts directly to kernel stats, without midlayer accumulator.
Cons:
- Doesn't elapse when the tick is off, which doesn't make it
suitable for online CPUs.
- Has TICK_NSEC granularity (jiffies)
- Needs to track the dyntick-idle ticks that were accounted and
substract them from the total jiffies time spent while the tick
was stopped. This is an ugly workaround.
Having two different accounting for a single context is not the only
problem: since those accountings are of different natures, it is
possible to observe the global idle time going backward after a CPU goes
offline.
Clean up the situation with introducing a hybrid approach that stays
coherent and works for both online and offline CPUs:
* Tick based or native vtime accounting operate before the idle loop
is entered and resume once the idle loop prepares to exit.
* When the idle loop starts, switch to dynticks-idle accounting as is
done currently, except that the statistics accumulate directly to the
relevant kernel stat fields.
* Private dyntick cputime accounting fields are removed.
* Works on both online and offline case.
Further improvement will include:
* Only switch to dynticks-idle cputime accounting when the tick actually
goes in dynticks mode.
* Handle CONFIG_IRQ_TIME_ACCOUNTING=n correctly such that the
dynticks-idle accounting still elapses while on IRQs.
* Correctly substract idle steal cputime from idle time
Reported-by: Xin Zhao <jackzxcui1989@163.com>
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Tested-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
include/linux/kernel_stat.h | 24 ++++++++++---
include/linux/vtime.h | 7 +++-
kernel/sched/cputime.c | 62 ++++++++++++++++----------------
kernel/time/tick-sched.c | 71 +++++++++++--------------------------
4 files changed, 76 insertions(+), 88 deletions(-)
diff --git a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h
index dd020ecaf67b..ba65aad308a1 100644
--- a/include/linux/kernel_stat.h
+++ b/include/linux/kernel_stat.h
@@ -34,6 +34,9 @@ enum cpu_usage_stat {
};
struct kernel_cpustat {
+#ifdef CONFIG_NO_HZ_COMMON
+ int idle_dyntick;
+#endif
u64 cpustat[NR_STATS];
};
@@ -99,6 +102,20 @@ static inline unsigned long kstat_cpu_irqs_sum(unsigned int cpu)
return kstat_cpu(cpu).irqs_sum;
}
+#ifdef CONFIG_NO_HZ_COMMON
+extern void kcpustat_dyntick_start(void);
+extern void kcpustat_dyntick_stop(void);
+static inline bool kcpustat_idle_dyntick(void)
+{
+ return __this_cpu_read(kernel_cpustat.idle_dyntick);
+}
+#else
+static inline bool kcpustat_idle_dyntick(void)
+{
+ return false;
+}
+#endif /* CONFIG_NO_HZ_COMMON */
+
#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);
@@ -113,7 +130,7 @@ static inline void kcpustat_cpu_fetch(struct kernel_cpustat *dst, int cpu)
*dst = kcpustat_cpu(cpu);
}
-#endif
+#endif /* !CONFIG_VIRT_CPU_ACCOUNTING_GEN */
extern void account_user_time(struct task_struct *, u64);
extern void account_guest_time(struct task_struct *, u64);
@@ -127,14 +144,13 @@ extern u64 get_idle_time(struct kernel_cpustat *kcs, int cpu);
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
static inline void account_process_tick(struct task_struct *tsk, int user)
{
- vtime_flush(tsk);
+ if (!kcpustat_idle_dyntick())
+ vtime_flush(tsk);
}
#else
extern void account_process_tick(struct task_struct *, int user);
#endif
-extern void account_idle_ticks(unsigned long ticks);
-
#ifdef CONFIG_SCHED_CORE
extern void __account_forceidle_time(struct task_struct *tsk, u64 delta);
#endif
diff --git a/include/linux/vtime.h b/include/linux/vtime.h
index 61b94c12d7dd..a4506336002d 100644
--- a/include/linux/vtime.h
+++ b/include/linux/vtime.h
@@ -31,6 +31,11 @@ static inline bool vtime_generic_enabled_cpu(int cpu)
return context_tracking_enabled_cpu(cpu);
}
+static inline bool vtime_generic_enabled_this_cpu(void)
+{
+ return context_tracking_enabled_this_cpu();
+}
+
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
extern void vtime_account_idle(struct task_struct *tsk);
extern void vtime_account_irq(struct task_struct *tsk, unsigned int offset);
@@ -90,7 +95,7 @@ static inline bool vtime_accounting_enabled_cpu(int cpu)
static inline bool vtime_accounting_enabled_this_cpu(void)
{
- return context_tracking_enabled_this_cpu();
+ return vtime_generic_enabled_this_cpu();
}
extern void vtime_task_switch_generic(struct task_struct *prev);
diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c
index d91b495457ec..4934c537f5e3 100644
--- a/kernel/sched/cputime.c
+++ b/kernel/sched/cputime.c
@@ -414,16 +414,30 @@ static void irqtime_account_process_tick(struct task_struct *p, int user_tick,
}
}
-static void irqtime_account_idle_ticks(int ticks)
-{
- irqtime_account_process_tick(current, 0, ticks);
-}
#else /* !CONFIG_IRQ_TIME_ACCOUNTING: */
-static inline void irqtime_account_idle_ticks(int ticks) { }
static inline void irqtime_account_process_tick(struct task_struct *p, int user_tick,
int nr_ticks) { }
#endif /* !CONFIG_IRQ_TIME_ACCOUNTING */
+#ifdef CONFIG_NO_HZ_COMMON
+void kcpustat_dyntick_start(void)
+{
+ if (!vtime_generic_enabled_this_cpu()) {
+ vtime_dyntick_start();
+ __this_cpu_write(kernel_cpustat.idle_dyntick, 1);
+ }
+}
+
+void kcpustat_dyntick_stop(void)
+{
+ if (!vtime_generic_enabled_this_cpu()) {
+ __this_cpu_write(kernel_cpustat.idle_dyntick, 0);
+ vtime_dyntick_stop();
+ steal_account_process_time(ULONG_MAX);
+ }
+}
+#endif /* CONFIG_NO_HZ_COMMON */
+
/*
* Use precise platform statistics if available:
*/
@@ -437,11 +451,15 @@ void vtime_account_irq(struct task_struct *tsk, unsigned int offset)
vtime_account_hardirq(tsk);
} else if (pc & SOFTIRQ_OFFSET) {
vtime_account_softirq(tsk);
- } else if (!IS_ENABLED(CONFIG_HAVE_VIRT_CPU_ACCOUNTING_IDLE) &&
- is_idle_task(tsk)) {
- vtime_account_idle(tsk);
+ } else if (!kcpustat_idle_dyntick()) {
+ if (!IS_ENABLED(CONFIG_HAVE_VIRT_CPU_ACCOUNTING_IDLE) &&
+ is_idle_task(tsk)) {
+ vtime_account_idle(tsk);
+ } else {
+ vtime_account_kernel(tsk);
+ }
} else {
- vtime_account_kernel(tsk);
+ vtime_reset();
}
}
@@ -483,6 +501,9 @@ void account_process_tick(struct task_struct *p, int user_tick)
if (vtime_accounting_enabled_this_cpu())
return;
+ if (kcpustat_idle_dyntick())
+ return;
+
if (irqtime_enabled()) {
irqtime_account_process_tick(p, user_tick, 1);
return;
@@ -504,29 +525,6 @@ void account_process_tick(struct task_struct *p, int user_tick)
account_idle_time(cputime);
}
-/*
- * Account multiple ticks of idle time.
- * @ticks: number of stolen ticks
- */
-void account_idle_ticks(unsigned long ticks)
-{
- u64 cputime, steal;
-
- if (irqtime_enabled()) {
- irqtime_account_idle_ticks(ticks);
- return;
- }
-
- cputime = ticks * TICK_NSEC;
- steal = steal_account_process_time(ULONG_MAX);
-
- if (steal >= cputime)
- return;
-
- cputime -= steal;
- account_idle_time(cputime);
-}
-
/*
* Adjust tick based cputime random precision against scheduler runtime
* accounting.
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index 7224a50d9c44..2c0f0b81f452 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -285,8 +285,6 @@ static void tick_sched_handle(struct tick_sched *ts, struct pt_regs *regs)
if (IS_ENABLED(CONFIG_NO_HZ_COMMON) &&
tick_sched_flag_test(ts, TS_FLAG_STOPPED)) {
touch_softlockup_watchdog_sched();
- if (is_idle_task(current))
- ts->idle_jiffies++;
/*
* In case the current tick fired too early past its expected
* expiration, make sure we don't bypass the next clock reprogramming
@@ -753,8 +751,12 @@ static void tick_nohz_update_jiffies(ktime_t now)
static void tick_nohz_stop_idle(struct tick_sched *ts, ktime_t now)
{
+ u64 *cpustat = kcpustat_this_cpu->cpustat;
ktime_t delta;
+ if (vtime_generic_enabled_this_cpu())
+ return;
+
if (WARN_ON_ONCE(!tick_sched_flag_test(ts, TS_FLAG_IDLE_ACTIVE)))
return;
@@ -762,9 +764,9 @@ static void tick_nohz_stop_idle(struct tick_sched *ts, ktime_t now)
write_seqcount_begin(&ts->idle_sleeptime_seq);
if (nr_iowait_cpu(smp_processor_id()) > 0)
- ts->iowait_sleeptime = ktime_add(ts->iowait_sleeptime, delta);
+ cpustat[CPUTIME_IOWAIT] = ktime_add(cpustat[CPUTIME_IOWAIT], delta);
else
- ts->idle_sleeptime = ktime_add(ts->idle_sleeptime, delta);
+ cpustat[CPUTIME_IDLE] = ktime_add(cpustat[CPUTIME_IDLE], delta);
ts->idle_entrytime = now;
tick_sched_flag_clear(ts, TS_FLAG_IDLE_ACTIVE);
@@ -775,18 +777,21 @@ static void tick_nohz_stop_idle(struct tick_sched *ts, ktime_t now)
static void tick_nohz_start_idle(struct tick_sched *ts)
{
+ if (vtime_generic_enabled_this_cpu())
+ return;
+
write_seqcount_begin(&ts->idle_sleeptime_seq);
ts->idle_entrytime = ktime_get();
tick_sched_flag_set(ts, TS_FLAG_IDLE_ACTIVE);
write_seqcount_end(&ts->idle_sleeptime_seq);
-
sched_clock_idle_sleep_event();
}
-static u64 get_cpu_sleep_time_us(int cpu, enum cpu_usage_stat idx, ktime_t *sleeptime,
+static u64 get_cpu_sleep_time_us(int cpu, enum cpu_usage_stat idx,
bool compute_delta, u64 *last_update_time)
{
struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
+ u64 *cpustat = kcpustat_cpu(cpu).cpustat;
ktime_t now, idle;
unsigned int seq;
@@ -808,9 +813,9 @@ static u64 get_cpu_sleep_time_us(int cpu, enum cpu_usage_stat idx, ktime_t *slee
if (tick_sched_flag_test(ts, TS_FLAG_IDLE_ACTIVE) && compute_delta) {
ktime_t delta = ktime_sub(now, ts->idle_entrytime);
- idle = ktime_add(*sleeptime, delta);
+ idle = ktime_add(cpustat[idx], delta);
} else {
- idle = *sleeptime;
+ idle = cpustat[idx];
}
} while (read_seqcount_retry(&ts->idle_sleeptime_seq, seq));
@@ -837,9 +842,7 @@ static u64 get_cpu_sleep_time_us(int cpu, enum cpu_usage_stat idx, ktime_t *slee
*/
u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time)
{
- struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
-
- return get_cpu_sleep_time_us(cpu, CPUTIME_IDLE, &ts->idle_sleeptime,
+ return get_cpu_sleep_time_us(cpu, CPUTIME_IDLE,
!nr_iowait_cpu(cpu), last_update_time);
}
EXPORT_SYMBOL_GPL(get_cpu_idle_time_us);
@@ -863,9 +866,7 @@ EXPORT_SYMBOL_GPL(get_cpu_idle_time_us);
*/
u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time)
{
- struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
-
- return get_cpu_sleep_time_us(cpu, CPUTIME_IOWAIT, &ts->iowait_sleeptime,
+ return get_cpu_sleep_time_us(cpu, CPUTIME_IOWAIT,
nr_iowait_cpu(cpu), last_update_time);
}
EXPORT_SYMBOL_GPL(get_cpu_iowait_time_us);
@@ -1265,10 +1266,8 @@ void tick_nohz_idle_stop_tick(void)
ts->idle_sleeps++;
ts->idle_expires = expires;
- if (!was_stopped && tick_sched_flag_test(ts, TS_FLAG_STOPPED)) {
- ts->idle_jiffies = ts->last_jiffies;
+ if (!was_stopped && tick_sched_flag_test(ts, TS_FLAG_STOPPED))
nohz_balance_enter_idle(cpu);
- }
} else {
tick_nohz_retain_tick(ts);
}
@@ -1297,6 +1296,7 @@ void tick_nohz_idle_enter(void)
WARN_ON_ONCE(ts->timer_expires_base);
tick_sched_flag_set(ts, TS_FLAG_INIDLE);
+ kcpustat_dyntick_start();
tick_nohz_start_idle(ts);
local_irq_enable();
@@ -1422,37 +1422,12 @@ unsigned long tick_nohz_get_idle_calls_cpu(int cpu)
return ts->idle_calls;
}
-static void tick_nohz_account_idle_time(struct tick_sched *ts,
- ktime_t now)
-{
- unsigned long ticks;
-
- ts->idle_exittime = now;
-
- if (vtime_accounting_enabled_this_cpu())
- return;
- /*
- * We stopped the tick in idle. update_process_times() would miss the
- * time we slept, as it does only a 1 tick accounting.
- * Enforce that this is accounted to idle !
- */
- ticks = jiffies - ts->idle_jiffies;
- /*
- * We might be one off. Do not randomly account a huge number of ticks!
- */
- if (ticks && ticks < LONG_MAX)
- account_idle_ticks(ticks);
-}
-
void tick_nohz_idle_restart_tick(void)
{
struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
- if (tick_sched_flag_test(ts, TS_FLAG_STOPPED)) {
- ktime_t now = ktime_get();
- tick_nohz_restart_sched_tick(ts, now);
- tick_nohz_account_idle_time(ts, now);
- }
+ if (tick_sched_flag_test(ts, TS_FLAG_STOPPED))
+ tick_nohz_restart_sched_tick(ts, ktime_get());
}
static void tick_nohz_idle_update_tick(struct tick_sched *ts, ktime_t now)
@@ -1461,8 +1436,6 @@ static void tick_nohz_idle_update_tick(struct tick_sched *ts, ktime_t now)
__tick_nohz_full_update_tick(ts, now);
else
tick_nohz_restart_sched_tick(ts, now);
-
- tick_nohz_account_idle_time(ts, now);
}
/**
@@ -1504,6 +1477,7 @@ void tick_nohz_idle_exit(void)
if (tick_stopped)
tick_nohz_idle_update_tick(ts, now);
+ kcpustat_dyntick_stop();
local_irq_enable();
}
@@ -1640,20 +1614,15 @@ void tick_setup_sched_timer(bool hrtimer)
void tick_sched_timer_dying(int cpu)
{
struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
- ktime_t idle_sleeptime, iowait_sleeptime;
unsigned long idle_calls, idle_sleeps;
/* This must happen before hrtimers are migrated! */
if (tick_sched_flag_test(ts, TS_FLAG_HIGHRES))
hrtimer_cancel(&ts->sched_timer);
- idle_sleeptime = ts->idle_sleeptime;
- iowait_sleeptime = ts->iowait_sleeptime;
idle_calls = ts->idle_calls;
idle_sleeps = ts->idle_sleeps;
memset(ts, 0, sizeof(*ts));
- ts->idle_sleeptime = idle_sleeptime;
- ts->iowait_sleeptime = iowait_sleeptime;
ts->idle_calls = idle_calls;
ts->idle_sleeps = idle_sleeps;
}
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v5 09/11] mfd: bq257xx: Add BQ25792 support
From: Lee Jones @ 2026-03-31 13:17 UTC (permalink / raw)
To: Alexey Charkov
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chris Morgan,
Liam Girdwood, Mark Brown, Sebastian Reichel, devicetree,
linux-kernel, Sebastian Reichel, linux-pm
In-Reply-To: <CAKTNdwE4omfEkd6FNdj=hZUY2ZwLprXzUu1L4juFhWCxbWEjpQ@mail.gmail.com>
On Tue, 31 Mar 2026, Alexey Charkov wrote:
> On Tue, Mar 31, 2026 at 2:27 PM Lee Jones <lee@kernel.org> wrote:
> >
> > On Tue, 24 Mar 2026, Alexey Charkov wrote:
> >
> > > Add register definitions and a new 'type' enum to be passed via MFD
> > > private data to support the BQ25792, which is a newer variant of the
> > > BQ257xx family.
> > >
> > > BQ25792 shares similar logic of operation with the already supported
> > > BQ25703A but has a completely different register map and different
> > > electrical constraints.
> > >
> > > Tested-by: Chris Morgan <macromorgan@hotmail.com>
> > > Signed-off-by: Alexey Charkov <alchark@flipper.net>
> > > ---
> > > drivers/mfd/bq257xx.c | 54 +++++-
> > > include/linux/mfd/bq257xx.h | 412 ++++++++++++++++++++++++++++++++++++++++++++
> > > 2 files changed, 463 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/drivers/mfd/bq257xx.c b/drivers/mfd/bq257xx.c
> > > index e9d49dac0a16..31654925afa5 100644
> > > --- a/drivers/mfd/bq257xx.c
> > > +++ b/drivers/mfd/bq257xx.c
> > > @@ -39,6 +39,39 @@ static const struct regmap_config bq25703_regmap_config = {
> > > .val_format_endian = REGMAP_ENDIAN_LITTLE,
> > > };
> > >
> > > +static const struct regmap_range bq25792_writeable_reg_ranges[] = {
> > > + regmap_reg_range(BQ25792_REG00_MIN_SYS_VOLTAGE,
> > > + BQ25792_REG18_NTC_CONTROL_1),
> > > + regmap_reg_range(BQ25792_REG28_CHARGER_MASK_0,
> > > + BQ25792_REG30_ADC_FUNCTION_DISABLE_1),
> > > +};
> > > +
> > > +static const struct regmap_access_table bq25792_writeable_regs = {
> > > + .yes_ranges = bq25792_writeable_reg_ranges,
> > > + .n_yes_ranges = ARRAY_SIZE(bq25792_writeable_reg_ranges),
> > > +};
> > > +
> > > +static const struct regmap_range bq25792_volatile_reg_ranges[] = {
> > > + regmap_reg_range(BQ25792_REG19_ICO_CURRENT_LIMIT,
> > > + BQ25792_REG27_FAULT_FLAG_1),
> > > + regmap_reg_range(BQ25792_REG31_IBUS_ADC,
> > > + BQ25792_REG47_DPDM_DRIVER),
> > > +};
> > > +
> > > +static const struct regmap_access_table bq25792_volatile_regs = {
> > > + .yes_ranges = bq25792_volatile_reg_ranges,
> > > + .n_yes_ranges = ARRAY_SIZE(bq25792_volatile_reg_ranges),
> > > +};
> > > +
> > > +static const struct regmap_config bq25792_regmap_config = {
> > > + .reg_bits = 8,
> > > + .val_bits = 8,
> > > + .max_register = BQ25792_REG48_PART_INFORMATION,
> > > + .cache_type = REGCACHE_MAPLE,
> > > + .wr_table = &bq25792_writeable_regs,
> > > + .volatile_table = &bq25792_volatile_regs,
> > > +};
> > > +
> > > static const struct mfd_cell cells[] = {
> > > MFD_CELL_NAME("bq257xx-regulator"),
> > > MFD_CELL_NAME("bq257xx-charger"),
> > > @@ -46,6 +79,7 @@ static const struct mfd_cell cells[] = {
> > >
> > > static int bq257xx_probe(struct i2c_client *client)
> > > {
> > > + const struct regmap_config *rcfg;
> > > struct bq257xx_device *ddata;
> > > int ret;
> > >
> > > @@ -53,9 +87,21 @@ static int bq257xx_probe(struct i2c_client *client)
> > > if (!ddata)
> > > return -ENOMEM;
> > >
> > > + ddata->type = (uintptr_t)i2c_get_match_data(client);
> > > ddata->client = client;
> > >
> > > - ddata->regmap = devm_regmap_init_i2c(client, &bq25703_regmap_config);
> > > + switch (ddata->type) {
> > > + case BQ25703A:
> > > + rcfg = &bq25703_regmap_config;
> > > + break;
> > > + case BQ25792:
> > > + rcfg = &bq25792_regmap_config;
> > > + break;
> > > + default:
> > > + return dev_err_probe(&client->dev, -EINVAL, "Unsupported device type\n");
> >
> > Nit: Shouldn't we be returning '-ENODEV' here for an unsupported device?
>
> Hi Lee,
>
> Indeed, I've had a quick look and other drivers seem to return -ENODEV
> in similar situations. Shall I respin a new version with that change?
Yes please. And add a change log so I am reminded of it.
--
Lee Jones [李琼斯]
^ permalink raw reply
* [PATCH 05/14] s390/time: Prepare to stop elapsing in dynticks-idle
From: Frederic Weisbecker @ 2026-03-31 13:16 UTC (permalink / raw)
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, Shrikanth Hegde,
Steven Rostedt, Sven Schnelle, Thomas Gleixner, Uladzislau Rezki,
Valentin Schneider, Vasily Gorbik, Vincent Guittot, Viresh Kumar,
Xin Zhao, linux-pm, linux-s390, linuxppc-dev
In-Reply-To: <20260331131622.30505-1-frederic@kernel.org>
Currently the tick subsystem stores the idle cputime accounting in
private fields, allowing cohabitation with architecture idle vtime
accounting. The former is fetched on online CPUs, the latter on offline
CPUs.
For consolidation purpose, architecture vtime accounting will continue
to account the cputime but will make a break when the idle tick is
stopped. The dyntick cputime accounting will then be relayed by the tick
subsystem so that the idle cputime is still seen advancing coherently
even when the tick isn't there to flush the idle vtime.
Prepare for that and introduce three new APIs which will be used in
subsequent patches:
_ vtime_dynticks_start() is deemed to be called when idle enters in
dyntick mode. The idle cputime that elapsed so far is accumulated
and accounted. Also idle time accounting is ignored.
- vtime_dynticks_stop() is deemed to be called when idle exits from
dyntick mode. The vtime entry clocks are fast-forward to current time
so that idle accounting restarts elapsing from now. Also idle time
accounting is resumed.
- vtime_reset() is deemed to be called from dynticks idle IRQ entry to
fast-forward the clock to current time so that the IRQ time is still
accounted by vtime while nohz cputime is paused.
Also accumulated vtime won't be flushed from dyntick-idle ticks to avoid
accounting twice the idle cputime, along with nohz accounting.
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Co-developed-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
Tested-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
arch/s390/include/asm/idle.h | 2 ++
arch/s390/kernel/idle.c | 5 +++-
arch/s390/kernel/vtime.c | 57 ++++++++++++++++++++++++++++++++----
3 files changed, 57 insertions(+), 7 deletions(-)
diff --git a/arch/s390/include/asm/idle.h b/arch/s390/include/asm/idle.h
index 32536ee34aa0..e4ad09a22400 100644
--- a/arch/s390/include/asm/idle.h
+++ b/arch/s390/include/asm/idle.h
@@ -8,10 +8,12 @@
#ifndef _S390_IDLE_H
#define _S390_IDLE_H
+#include <linux/percpu-defs.h>
#include <linux/types.h>
#include <linux/device.h>
struct s390_idle_data {
+ bool idle_dyntick;
unsigned long idle_count;
unsigned long idle_time;
unsigned long clock_idle_enter;
diff --git a/arch/s390/kernel/idle.c b/arch/s390/kernel/idle.c
index 1f1b06b6b4ef..4685d7c5bc51 100644
--- a/arch/s390/kernel/idle.c
+++ b/arch/s390/kernel/idle.c
@@ -31,7 +31,10 @@ void account_idle_time_irq(void)
/* Account time spent with enabled wait psw loaded as idle time. */
__atomic64_add(idle_time, &idle->idle_time);
__atomic64_add_const(1, &idle->idle_count);
- account_idle_time(cputime_to_nsecs(idle_time));
+
+ /* Dyntick idle time accounted by nohz/scheduler */
+ if (!idle->idle_dyntick)
+ account_idle_time(cputime_to_nsecs(idle_time));
}
void noinstr arch_cpu_idle(void)
diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c
index bf48744d0912..b1c7700d082c 100644
--- a/arch/s390/kernel/vtime.c
+++ b/arch/s390/kernel/vtime.c
@@ -17,6 +17,7 @@
#include <asm/vtimer.h>
#include <asm/vtime.h>
#include <asm/cpu_mf.h>
+#include <asm/idle.h>
#include <asm/smp.h>
#include "entry.h"
@@ -110,6 +111,16 @@ static void account_system_index_scaled(struct task_struct *p, u64 cputime,
account_system_index_time(p, cputime_to_nsecs(cputime), index);
}
+static inline void vtime_reset_last_update(struct lowcore *lc)
+{
+ asm volatile(
+ " stpt %0\n" /* Store current cpu timer value */
+ " stckf %1" /* Store current tod clock value */
+ : "=Q" (lc->last_update_timer),
+ "=Q" (lc->last_update_clock)
+ : : "cc");
+}
+
/*
* Update process times based on virtual cpu times stored by entry.S
* to the lowcore fields user_timer, system_timer & steal_clock.
@@ -121,12 +132,9 @@ static int do_account_vtime(struct task_struct *tsk)
timer = lc->last_update_timer;
clock = lc->last_update_clock;
- asm volatile(
- " stpt %0\n" /* Store current cpu timer value */
- " stckf %1" /* Store current tod clock value */
- : "=Q" (lc->last_update_timer),
- "=Q" (lc->last_update_clock)
- : : "cc");
+
+ vtime_reset_last_update(lc);
+
clock = lc->last_update_clock - clock;
timer -= lc->last_update_timer;
@@ -239,6 +247,43 @@ void vtime_account_hardirq(struct task_struct *tsk)
get_lowcore()->hardirq_timer += vtime_delta();
}
+#ifdef CONFIG_NO_HZ_COMMON
+/**
+ * vtime_reset - Fast forward vtime entry clocks
+ *
+ * Called from dynticks idle IRQ entry to fast-forward the clocks to current time
+ * so that the IRQ time is still accounted by vtime while nohz cputime is paused.
+ */
+void vtime_reset(void)
+{
+ vtime_reset_last_update(get_lowcore());
+}
+
+/**
+ * vtime_dyntick_start - Inform vtime about entry to idle-dynticks
+ *
+ * Called when idle enters in dyntick mode. The idle cputime that elapsed so far
+ * is flushed and the tick subsystem takes over the idle cputime accounting.
+ */
+void vtime_dyntick_start(void)
+{
+ __this_cpu_write(s390_idle.idle_dyntick, true);
+ vtime_flush(current);
+}
+
+/**
+ * vtime_dyntick_stop - Inform vtime about exit from idle-dynticks
+ *
+ * Called when idle exits from dyntick mode. The vtime entry clocks are
+ * fast-forward to current time and idle accounting resumes.
+ */
+void vtime_dyntick_stop(void)
+{
+ vtime_reset_last_update(get_lowcore());
+ __this_cpu_write(s390_idle.idle_dyntick, false);
+}
+#endif /* CONFIG_NO_HZ_COMMON */
+
/*
* Sorted add to a list. List is linear searched until first bigger
* element is found.
--
2.53.0
^ permalink raw reply related
* [PATCH 04/14] powerpc/time: Prepare to stop elapsing in dynticks-idle
From: Frederic Weisbecker @ 2026-03-31 13:16 UTC (permalink / raw)
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, Shrikanth Hegde,
Steven Rostedt, Sven Schnelle, Thomas Gleixner, Uladzislau Rezki,
Valentin Schneider, Vasily Gorbik, Vincent Guittot, Viresh Kumar,
Xin Zhao, linux-pm, linux-s390, linuxppc-dev
In-Reply-To: <20260331131622.30505-1-frederic@kernel.org>
Currently the tick subsystem stores the idle cputime accounting in
private fields, allowing cohabitation with architecture idle vtime
accounting. The former is fetched on online CPUs, the latter on offline
CPUs.
For consolidation purpose, architecture vtime accounting will continue
to account the cputime but will make a break when the idle tick is
stopped. The dyntick cputime accounting will then be relayed by the tick
subsystem so that the idle cputime is still seen advancing coherently
even when the tick isn't there to flush the idle vtime.
Prepare for that and introduce three new APIs which will be used in
subsequent patches:
_ vtime_dynticks_start() is deemed to be called when idle enters in
dyntick mode. The idle cputime that elapsed so far is accumulated.
- vtime_dynticks_stop() is deemed to be called when idle exits from
dyntick mode. The vtime entry clocks are fast-forward to current time
so that idle accounting restarts elapsing from now.
- vtime_reset() is deemed to be called from dynticks idle IRQ entry to
fast-forward the clock to current time so that the IRQ time is still
accounted by vtime while nohz cputime is paused.
Also accumulated vtime won't be flushed from dyntick-idle ticks to avoid
accounting twice the idle cputime, along with nohz accounting.
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Reviewed-by: Shrikanth Hegde <sshegde@linux.ibm.com>
Tested-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
arch/powerpc/kernel/time.c | 41 ++++++++++++++++++++++++++++++++++++++
include/linux/vtime.h | 6 ++++++
2 files changed, 47 insertions(+)
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index 4bbeb8644d3d..18506740f4a4 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -376,6 +376,47 @@ void vtime_task_switch(struct task_struct *prev)
acct->starttime = acct0->starttime;
}
}
+
+#ifdef CONFIG_NO_HZ_COMMON
+/**
+ * vtime_reset - Fast forward vtime entry clocks
+ *
+ * Called from dynticks idle IRQ entry to fast-forward the clocks to current time
+ * so that the IRQ time is still accounted by vtime while nohz cputime is paused.
+ */
+void vtime_reset(void)
+{
+ struct cpu_accounting_data *acct = get_accounting(current);
+
+ acct->starttime = mftb();
+#ifdef CONFIG_ARCH_HAS_SCALED_CPUTIME
+ acct->startspurr = read_spurr(acct->starttime);
+#endif
+}
+
+/**
+ * vtime_dyntick_start - Inform vtime about entry to idle-dynticks
+ *
+ * Called when idle enters in dyntick mode. The idle cputime that elapsed so far
+ * is accumulated and the tick subsystem takes over the idle cputime accounting.
+ */
+void vtime_dyntick_start(void)
+{
+ vtime_account_idle(current);
+}
+
+/**
+ * vtime_dyntick_stop - Inform vtime about exit from idle-dynticks
+ *
+ * Called when idle exits from dyntick mode. The vtime entry clocks are
+ * fast-forward to current time so that idle accounting restarts elapsing from
+ * now.
+ */
+void vtime_dyntick_stop(void)
+{
+ vtime_reset();
+}
+#endif /* CONFIG_NO_HZ_COMMON */
#endif /* CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */
void __no_kcsan __delay(unsigned long loops)
diff --git a/include/linux/vtime.h b/include/linux/vtime.h
index 336875bea767..61b94c12d7dd 100644
--- a/include/linux/vtime.h
+++ b/include/linux/vtime.h
@@ -37,11 +37,17 @@ extern void vtime_account_irq(struct task_struct *tsk, unsigned int offset);
extern void vtime_account_softirq(struct task_struct *tsk);
extern void vtime_account_hardirq(struct task_struct *tsk);
extern void vtime_flush(struct task_struct *tsk);
+extern void vtime_reset(void);
+extern void vtime_dyntick_start(void);
+extern void vtime_dyntick_stop(void);
#else /* !CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */
static inline void vtime_account_irq(struct task_struct *tsk, unsigned int offset) { }
static inline void vtime_account_softirq(struct task_struct *tsk) { }
static inline void vtime_account_hardirq(struct task_struct *tsk) { }
static inline void vtime_flush(struct task_struct *tsk) { }
+static inline void vtime_reset(void) { }
+static inline void vtime_dyntick_start(void) { }
+extern inline void vtime_dyntick_stop(void) { }
#endif
/*
--
2.53.0
^ permalink raw reply related
* [PATCH 03/14] sched/cputime: Correctly support generic vtime idle time
From: Frederic Weisbecker @ 2026-03-31 13:16 UTC (permalink / raw)
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, Shrikanth Hegde,
Steven Rostedt, Sven Schnelle, Thomas Gleixner, Uladzislau Rezki,
Valentin Schneider, Vasily Gorbik, Vincent Guittot, Viresh Kumar,
Xin Zhao, linux-pm, linux-s390, linuxppc-dev
In-Reply-To: <20260331131622.30505-1-frederic@kernel.org>
Currently whether generic vtime is running or not, the idle cputime is
fetched from the nohz accounting.
However generic vtime already does its own idle cputime accounting. Only
the kernel stat accessors are not plugged to support it.
Read the idle generic vtime cputime when it's running, this will allow
to later more clearly split nohz and vtime cputime accounting.
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Tested-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
include/linux/vtime.h | 9 +++++++--
kernel/sched/cputime.c | 38 +++++++++++++++++++++++++++++---------
kernel/time/tick-sched.c | 12 +++++++++---
3 files changed, 45 insertions(+), 14 deletions(-)
diff --git a/include/linux/vtime.h b/include/linux/vtime.h
index 29dd5b91dd7d..336875bea767 100644
--- a/include/linux/vtime.h
+++ b/include/linux/vtime.h
@@ -10,7 +10,6 @@
*/
#ifdef CONFIG_VIRT_CPU_ACCOUNTING
extern void vtime_account_kernel(struct task_struct *tsk);
-extern void vtime_account_idle(struct task_struct *tsk);
#endif /* !CONFIG_VIRT_CPU_ACCOUNTING */
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
@@ -27,7 +26,13 @@ static inline void vtime_guest_exit(struct task_struct *tsk) { }
static inline void vtime_init_idle(struct task_struct *tsk, int cpu) { }
#endif
+static inline bool vtime_generic_enabled_cpu(int cpu)
+{
+ return context_tracking_enabled_cpu(cpu);
+}
+
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
+extern void vtime_account_idle(struct task_struct *tsk);
extern void vtime_account_irq(struct task_struct *tsk, unsigned int offset);
extern void vtime_account_softirq(struct task_struct *tsk);
extern void vtime_account_hardirq(struct task_struct *tsk);
@@ -74,7 +79,7 @@ static inline bool vtime_accounting_enabled(void)
static inline bool vtime_accounting_enabled_cpu(int cpu)
{
- return context_tracking_enabled_cpu(cpu);
+ return vtime_generic_enabled_cpu(cpu);
}
static inline bool vtime_accounting_enabled_this_cpu(void)
diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c
index caaaf0a04ced..d91b495457ec 100644
--- a/kernel/sched/cputime.c
+++ b/kernel/sched/cputime.c
@@ -773,9 +773,9 @@ void vtime_guest_exit(struct task_struct *tsk)
}
EXPORT_SYMBOL_GPL(vtime_guest_exit);
-void vtime_account_idle(struct task_struct *tsk)
+static void __vtime_account_idle(struct vtime *vtime)
{
- account_idle_time(get_vtime_delta(&tsk->vtime));
+ account_idle_time(get_vtime_delta(vtime));
}
void vtime_task_switch_generic(struct task_struct *prev)
@@ -784,7 +784,7 @@ void vtime_task_switch_generic(struct task_struct *prev)
write_seqcount_begin(&vtime->seqcount);
if (vtime->state == VTIME_IDLE)
- vtime_account_idle(prev);
+ __vtime_account_idle(vtime);
else
__vtime_account_kernel(prev, vtime);
vtime->state = VTIME_INACTIVE;
@@ -926,6 +926,7 @@ static int kcpustat_field_vtime(u64 *cpustat,
int cpu, u64 *val)
{
struct vtime *vtime = &tsk->vtime;
+ struct rq *rq = cpu_rq(cpu);
unsigned int seq;
do {
@@ -967,6 +968,14 @@ static int kcpustat_field_vtime(u64 *cpustat,
if (state == VTIME_GUEST && task_nice(tsk) > 0)
*val += vtime->gtime + vtime_delta(vtime);
break;
+ case CPUTIME_IDLE:
+ if (state == VTIME_IDLE && !atomic_read(&rq->nr_iowait))
+ *val += vtime_delta(vtime);
+ break;
+ case CPUTIME_IOWAIT:
+ if (state == VTIME_IDLE && atomic_read(&rq->nr_iowait) > 0)
+ *val += vtime_delta(vtime);
+ break;
default:
break;
}
@@ -1029,8 +1038,8 @@ static int kcpustat_cpu_fetch_vtime(struct kernel_cpustat *dst,
*dst = *src;
cpustat = dst->cpustat;
- /* Task is sleeping, dead or idle, nothing to add */
- if (state < VTIME_SYS)
+ /* Task is sleeping or dead, nothing to add */
+ if (state < VTIME_IDLE)
continue;
delta = vtime_delta(vtime);
@@ -1039,15 +1048,17 @@ static int kcpustat_cpu_fetch_vtime(struct kernel_cpustat *dst,
* Task runs either in user (including guest) or kernel space,
* add pending nohz time to the right place.
*/
- if (state == VTIME_SYS) {
+ switch (vtime->state) {
+ case VTIME_SYS:
cpustat[CPUTIME_SYSTEM] += vtime->stime + delta;
- } else if (state == VTIME_USER) {
+ break;
+ case VTIME_USER:
if (task_nice(tsk) > 0)
cpustat[CPUTIME_NICE] += vtime->utime + delta;
else
cpustat[CPUTIME_USER] += vtime->utime + delta;
- } else {
- WARN_ON_ONCE(state != VTIME_GUEST);
+ break;
+ case VTIME_GUEST:
if (task_nice(tsk) > 0) {
cpustat[CPUTIME_GUEST_NICE] += vtime->gtime + delta;
cpustat[CPUTIME_NICE] += vtime->gtime + delta;
@@ -1055,6 +1066,15 @@ static int kcpustat_cpu_fetch_vtime(struct kernel_cpustat *dst,
cpustat[CPUTIME_GUEST] += vtime->gtime + delta;
cpustat[CPUTIME_USER] += vtime->gtime + delta;
}
+ break;
+ case VTIME_IDLE:
+ if (atomic_read(&cpu_rq(cpu)->nr_iowait) > 0)
+ cpustat[CPUTIME_IOWAIT] += delta;
+ else
+ cpustat[CPUTIME_IDLE] += delta;
+ break;
+ default:
+ WARN_ON_ONCE(1);
}
} while (read_seqcount_retry(&vtime->seqcount, seq));
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index f7907fadd63f..7224a50d9c44 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -783,9 +783,10 @@ static void tick_nohz_start_idle(struct tick_sched *ts)
sched_clock_idle_sleep_event();
}
-static u64 get_cpu_sleep_time_us(struct tick_sched *ts, ktime_t *sleeptime,
+static u64 get_cpu_sleep_time_us(int cpu, enum cpu_usage_stat idx, ktime_t *sleeptime,
bool compute_delta, u64 *last_update_time)
{
+ struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
ktime_t now, idle;
unsigned int seq;
@@ -796,6 +797,11 @@ static u64 get_cpu_sleep_time_us(struct tick_sched *ts, ktime_t *sleeptime,
if (last_update_time)
*last_update_time = ktime_to_us(now);
+ if (vtime_generic_enabled_cpu(cpu)) {
+ idle = kcpustat_field(idx, cpu);
+ return ktime_to_us(idle);
+ }
+
do {
seq = read_seqcount_begin(&ts->idle_sleeptime_seq);
@@ -833,7 +839,7 @@ u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time)
{
struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
- return get_cpu_sleep_time_us(ts, &ts->idle_sleeptime,
+ return get_cpu_sleep_time_us(cpu, CPUTIME_IDLE, &ts->idle_sleeptime,
!nr_iowait_cpu(cpu), last_update_time);
}
EXPORT_SYMBOL_GPL(get_cpu_idle_time_us);
@@ -859,7 +865,7 @@ u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time)
{
struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
- return get_cpu_sleep_time_us(ts, &ts->iowait_sleeptime,
+ return get_cpu_sleep_time_us(cpu, CPUTIME_IOWAIT, &ts->iowait_sleeptime,
nr_iowait_cpu(cpu), last_update_time);
}
EXPORT_SYMBOL_GPL(get_cpu_iowait_time_us);
--
2.53.0
^ permalink raw reply related
* [PATCH 02/14] sched/cputime: Remove superfluous and error prone kcpustat_field() parameter
From: Frederic Weisbecker @ 2026-03-31 13:16 UTC (permalink / raw)
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, Shrikanth Hegde,
Steven Rostedt, Sven Schnelle, Thomas Gleixner, Uladzislau Rezki,
Valentin Schneider, Vasily Gorbik, Vincent Guittot, Viresh Kumar,
Xin Zhao, linux-pm, linux-s390, linuxppc-dev
In-Reply-To: <20260331131622.30505-1-frederic@kernel.org>
The first parameter to kcpustat_field() is a pointer to the cpu kcpustat
to be fetched from. This parameter is error prone because a copy to a
kcpustat could be passed by accident instead of the original one. Also
the kcpustat structure can already be retrieved with the help of the
mandatory CPU argument.
Remove the needless parameter.
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Reviewed-by: Shrikanth Hegde <sshegde@linux.ibm.com>
Tested-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
drivers/cpufreq/cpufreq_governor.c | 6 +++---
drivers/macintosh/rack-meter.c | 2 +-
include/linux/kernel_stat.h | 8 +++-----
kernel/rcu/tree.c | 9 +++------
kernel/rcu/tree_stall.h | 7 +++----
kernel/sched/cputime.c | 5 ++---
6 files changed, 15 insertions(+), 22 deletions(-)
diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c
index 36eb7aee4bcd..ed36d22195f6 100644
--- a/drivers/cpufreq/cpufreq_governor.c
+++ b/drivers/cpufreq/cpufreq_governor.c
@@ -105,7 +105,7 @@ void gov_update_cpu_data(struct dbs_data *dbs_data)
j_cdbs->prev_cpu_idle = get_cpu_idle_time(j, &j_cdbs->prev_update_time,
dbs_data->io_is_busy);
if (dbs_data->ignore_nice_load)
- j_cdbs->prev_cpu_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j);
+ j_cdbs->prev_cpu_nice = kcpustat_field(CPUTIME_NICE, j);
}
}
}
@@ -165,7 +165,7 @@ unsigned int dbs_update(struct cpufreq_policy *policy)
j_cdbs->prev_cpu_idle = cur_idle_time;
if (ignore_nice) {
- u64 cur_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j);
+ u64 cur_nice = kcpustat_field(CPUTIME_NICE, j);
idle_time += div_u64(cur_nice - j_cdbs->prev_cpu_nice, NSEC_PER_USEC);
j_cdbs->prev_cpu_nice = cur_nice;
@@ -539,7 +539,7 @@ int cpufreq_dbs_governor_start(struct cpufreq_policy *policy)
j_cdbs->prev_load = 0;
if (ignore_nice)
- j_cdbs->prev_cpu_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j);
+ j_cdbs->prev_cpu_nice = kcpustat_field(CPUTIME_NICE, j);
}
gov->start(policy);
diff --git a/drivers/macintosh/rack-meter.c b/drivers/macintosh/rack-meter.c
index 8a1e2c08b096..26cb93191ede 100644
--- a/drivers/macintosh/rack-meter.c
+++ b/drivers/macintosh/rack-meter.c
@@ -87,7 +87,7 @@ static inline u64 get_cpu_idle_time(unsigned int cpu)
kcpustat->cpustat[CPUTIME_IOWAIT];
if (rackmeter_ignore_nice)
- retval += kcpustat_field(kcpustat, CPUTIME_NICE, cpu);
+ retval += kcpustat_field(CPUTIME_NICE, cpu);
return retval;
}
diff --git a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h
index b97ce2df376f..dd020ecaf67b 100644
--- a/include/linux/kernel_stat.h
+++ b/include/linux/kernel_stat.h
@@ -100,14 +100,12 @@ static inline unsigned long kstat_cpu_irqs_sum(unsigned int cpu)
}
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
-extern u64 kcpustat_field(struct kernel_cpustat *kcpustat,
- enum cpu_usage_stat usage, int cpu);
+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(struct kernel_cpustat *kcpustat,
- enum cpu_usage_stat usage, int cpu)
+static inline u64 kcpustat_field(enum cpu_usage_stat usage, int cpu)
{
- return kcpustat->cpustat[usage];
+ return kcpustat_cpu(cpu).cpustat[usage];
}
static inline void kcpustat_cpu_fetch(struct kernel_cpustat *dst, int cpu)
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index 55df6d37145e..3cbf79bee976 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -969,14 +969,11 @@ static int rcu_watching_snap_recheck(struct rcu_data *rdp)
if (rcu_cpu_stall_cputime && rdp->snap_record.gp_seq != rdp->gp_seq) {
int cpu = rdp->cpu;
struct rcu_snap_record *rsrp;
- struct kernel_cpustat *kcsp;
-
- kcsp = &kcpustat_cpu(cpu);
rsrp = &rdp->snap_record;
- rsrp->cputime_irq = kcpustat_field(kcsp, CPUTIME_IRQ, cpu);
- rsrp->cputime_softirq = kcpustat_field(kcsp, CPUTIME_SOFTIRQ, cpu);
- rsrp->cputime_system = kcpustat_field(kcsp, CPUTIME_SYSTEM, cpu);
+ rsrp->cputime_irq = kcpustat_field(CPUTIME_IRQ, cpu);
+ rsrp->cputime_softirq = kcpustat_field(CPUTIME_SOFTIRQ, cpu);
+ rsrp->cputime_system = kcpustat_field(CPUTIME_SYSTEM, cpu);
rsrp->nr_hardirqs = kstat_cpu_irqs_sum(cpu) + arch_irq_stat_cpu(cpu);
rsrp->nr_softirqs = kstat_cpu_softirqs_sum(cpu);
rsrp->nr_csw = nr_context_switches_cpu(cpu);
diff --git a/kernel/rcu/tree_stall.h b/kernel/rcu/tree_stall.h
index b67532cb8770..cf7ae51cba40 100644
--- a/kernel/rcu/tree_stall.h
+++ b/kernel/rcu/tree_stall.h
@@ -479,7 +479,6 @@ static void print_cpu_stat_info(int cpu)
{
struct rcu_snap_record rsr, *rsrp;
struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu);
- struct kernel_cpustat *kcsp = &kcpustat_cpu(cpu);
if (!rcu_cpu_stall_cputime)
return;
@@ -488,9 +487,9 @@ static void print_cpu_stat_info(int cpu)
if (rsrp->gp_seq != rdp->gp_seq)
return;
- rsr.cputime_irq = kcpustat_field(kcsp, CPUTIME_IRQ, cpu);
- rsr.cputime_softirq = kcpustat_field(kcsp, CPUTIME_SOFTIRQ, cpu);
- rsr.cputime_system = kcpustat_field(kcsp, CPUTIME_SYSTEM, cpu);
+ rsr.cputime_irq = kcpustat_field(CPUTIME_IRQ, cpu);
+ rsr.cputime_softirq = kcpustat_field(CPUTIME_SOFTIRQ, cpu);
+ rsr.cputime_system = kcpustat_field(CPUTIME_SYSTEM, cpu);
pr_err("\t hardirqs softirqs csw/system\n");
pr_err("\t number: %8lld %10d %12lld\n",
diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c
index fbf31db0d2f3..caaaf0a04ced 100644
--- a/kernel/sched/cputime.c
+++ b/kernel/sched/cputime.c
@@ -975,10 +975,9 @@ static int kcpustat_field_vtime(u64 *cpustat,
return 0;
}
-u64 kcpustat_field(struct kernel_cpustat *kcpustat,
- enum cpu_usage_stat usage, int cpu)
+u64 kcpustat_field(enum cpu_usage_stat usage, int cpu)
{
- u64 *cpustat = kcpustat->cpustat;
+ u64 *cpustat = kcpustat_cpu(cpu).cpustat;
u64 val = cpustat[usage];
struct rq *rq;
int err;
--
2.53.0
^ permalink raw reply related
* [PATCH 01/14] sched/idle: Handle offlining first in idle loop
From: Frederic Weisbecker @ 2026-03-31 13:16 UTC (permalink / raw)
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, Shrikanth Hegde,
Steven Rostedt, Sven Schnelle, Thomas Gleixner, Uladzislau Rezki,
Valentin Schneider, Vasily Gorbik, Vincent Guittot, Viresh Kumar,
Xin Zhao, linux-pm, linux-s390, linuxppc-dev
In-Reply-To: <20260331131622.30505-1-frederic@kernel.org>
Offline handling happens from within the inner idle loop,
after the beginning of dyntick cputime accounting, nohz idle
load balancing and TIF_NEED_RESCHED polling.
This is not necessary and even buggy because:
* There is no dyntick handling to do. And calling tick_nohz_idle_enter()
messes up with the struct tick_sched reset that was performed on
tick_sched_timer_dying().
* There is no nohz idle balancing to do.
* Polling on TIF_RESCHED is irrelevant at this stage, there are no more
tasks allowed to run.
* No need to check if need_resched() before offline handling since
stop_machine is done and all per-cpu kthread should be done with
their job.
Therefore move the offline handling at the beginning of the idle loop.
This will also ease the idle cputime unification later by not elapsing
idle time while offline through the call to:
tick_nohz_idle_enter() -> tick_nohz_start_idle()
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Reviewed-by: Shrikanth Hegde <sshegde@linux.ibm.com>
Tested-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
kernel/sched/idle.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c
index 3681b6ad9276..521a076d8845 100644
--- a/kernel/sched/idle.c
+++ b/kernel/sched/idle.c
@@ -260,6 +260,14 @@ static void do_idle(void)
{
int cpu = smp_processor_id();
+ if (cpu_is_offline(cpu)) {
+ local_irq_disable();
+ /* All per-CPU kernel threads should be done by now. */
+ WARN_ON_ONCE(need_resched());
+ cpuhp_report_idle_dead();
+ arch_cpu_idle_dead();
+ }
+
/*
* Check if we need to update blocked load
*/
@@ -311,11 +319,6 @@ static void do_idle(void)
*/
local_irq_disable();
- if (cpu_is_offline(cpu)) {
- cpuhp_report_idle_dead();
- arch_cpu_idle_dead();
- }
-
arch_cpu_idle_enter();
rcu_nocb_flush_deferred_wakeup();
--
2.53.0
^ permalink raw reply related
* [PATCH 00/14 v3] tick/sched: Refactor idle cputime accounting
From: Frederic Weisbecker @ 2026-03-31 13:16 UTC (permalink / raw)
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, Shrikanth Hegde,
Steven Rostedt, Sven Schnelle, Thomas Gleixner, Uladzislau Rezki,
Valentin Schneider, Vasily Gorbik, Vincent Guittot, Viresh Kumar,
Xin Zhao, linux-pm, linux-s390, linuxppc-dev
Hi,
After the issue reported here:
https://lore.kernel.org/all/20251210083135.3993562-1-jackzxcui1989@163.com/
It occurs that the idle cputime accounting is a big mess that
accumulates within two concurrent statistics, each having their own
shortcomings:
* The accounting for online CPUs which is based on the delta between
tick_nohz_start_idle() and tick_nohz_stop_idle().
Pros:
- Works when the tick is off
- Has nsecs granularity
Cons:
- Account idle steal time but doesn't substract it from idle
cputime.
- Assumes CONFIG_IRQ_TIME_ACCOUNTING by not accounting IRQs but
the IRQ time is simply ignored when
CONFIG_IRQ_TIME_ACCOUNTING=n
- The windows between 1) idle task scheduling and the first call
to tick_nohz_start_idle() and 2) idle task between the last
tick_nohz_stop_idle() and the rest of the idle time are
blindspots wrt. cputime accounting (though mostly insignificant
amount)
- Relies on private fields outside of kernel stats, with specific
accessors.
* The accounting for offline CPUs which is based on ticks and the
jiffies delta during which the tick was stopped.
Pros:
- Handles steal time correctly
- Handle CONFIG_IRQ_TIME_ACCOUNTING=y and
CONFIG_IRQ_TIME_ACCOUNTING=n correctly.
- Handles the whole idle task
- Accounts directly to kernel stats, without midlayer accumulator.
Cons:
- Doesn't elapse when the tick is off, which doesn't make it
suitable for online CPUs.
- Has TICK_NSEC granularity (jiffies)
- Needs to track the dyntick-idle ticks that were accounted and
substract them from the total jiffies time spent while the tick
was stopped. This is an ugly workaround.
Having two different accounting for a single context is not the only
problem: since those accountings are of different natures, it is
possible to observe the global idle time going backward after a CPU goes
offline, as reported by Xin Zhao.
Clean up the situation with introducing a hybrid approach that stays
coherent, fixes the backward jumps and works for both online and offline
CPUs:
* Tick based or native vtime accounting operate before the tick is
stopped and resumes once the tick is restarted.
* When the idle loop starts, switch to dynticks-idle accounting as is
done currently, except that the statistics accumulate directly to the
relevant kernel stat fields.
* Private dyntick cputime accounting fields are removed.
* Works on both online and offline case.
* Move most of the relevant code to the common sched/cputime subsystem
* Handle CONFIG_IRQ_TIME_ACCOUNTING=n correctly such that the
dynticks-idle accounting still elapses while on IRQs.
* Correctly substract idle steal cputime from idle time
Changes since v2:
- Add tags
- Fix frenglish
- Add fixup from Heiko to s390 patch
- Drop "cpufreq: ondemand: Simplify idle cputime granularity test" as it's upstream
- Fix cpufreq regression reported by Shrikanth
- Simplfy irqtime handling with relying on kcpustat_idle_dyntick()
git://git.kernel.org/pub/scm/linux/kernel/git/frederic/linux-dynticks.git
timers/core-v3
HEAD: e37a063888aac70d4c598ce2ed367f8ce3552a69
Thanks!
Frederic Weisbecker (14):
sched/idle: Handle offlining first in idle loop
sched/cputime: Remove superfluous and error prone kcpustat_field()
parameter
sched/cputime: Correctly support generic vtime idle time
powerpc/time: Prepare to stop elapsing in dynticks-idle
s390/time: Prepare to stop elapsing in dynticks-idle
tick/sched: Unify idle cputime accounting
tick/sched: Remove nohz disabled special case in cputime fetch
tick/sched: Move dyntick-idle cputime accounting to cputime code
tick/sched: Remove unused fields
tick/sched: Account tickless idle cputime only when tick is stopped
tick/sched: Consolidate idle time fetching APIs
sched/cputime: Provide get_cpu_[idle|iowait]_time_us() off-case
sched/cputime: Handle idle irqtime gracefully
sched/cputime: Handle dyntick-idle steal time correctly
arch/powerpc/kernel/time.c | 41 ++++
arch/s390/include/asm/idle.h | 2 +
arch/s390/kernel/idle.c | 5 +-
arch/s390/kernel/vtime.c | 57 +++++-
drivers/cpufreq/cpufreq.c | 29 +--
drivers/cpufreq/cpufreq_governor.c | 6 +-
drivers/macintosh/rack-meter.c | 2 +-
fs/proc/stat.c | 40 +---
fs/proc/uptime.c | 8 +-
include/linux/kernel_stat.h | 76 ++++++--
include/linux/tick.h | 4 -
include/linux/vtime.h | 22 ++-
kernel/rcu/tree.c | 9 +-
kernel/rcu/tree_stall.h | 7 +-
kernel/sched/cputime.c | 289 ++++++++++++++++++++++++-----
kernel/sched/idle.c | 13 +-
kernel/time/tick-sched.c | 202 ++++----------------
kernel/time/tick-sched.h | 12 --
kernel/time/timer_list.c | 6 +-
scripts/gdb/linux/timerlist.py | 4 -
20 files changed, 481 insertions(+), 353 deletions(-)
--
2.53.0
^ permalink raw reply
* Re: [PATCH v2] PM: hibernate: call preallocate_image after freeze prepare
From: Rafael J. Wysocki @ 2026-03-31 12:45 UTC (permalink / raw)
To: Matthew Leach
Cc: Rafael J. Wysocki, Pavel Machek, Len Brown, Mario Limonciello,
linux-pm, linux-kernel, YoungJun Park, kernel
In-Reply-To: <20260326-hibernation-fixes-v2-1-f6707d82b7b9@collabora.com>
On Thu, Mar 26, 2026 at 12:37 PM Matthew Leach
<matthew.leach@collabora.com> wrote:
>
> Certain drivers release resources (pinned pages, etc.) into system
> memory during the prepare freeze PM op, making them swappable.
> Currently, hibernate_preallocate_memory is called before prepare freeze,
> so those drivers have no opportunity to release resources first. If a
> driver is holding a large amount of unswappable system RAM, this can
> cause hibernate_preallocate_memory to fail.
>
> Move the call to hibernate_preallocate_memory after prepare freeze.
> According to the documentation for the prepare callback, devices should
> be left in a usable state, so storage drivers should still be able to
> service I/O requests. This allows drivers to release unswappable
> resources prior to preallocation, so they can be swapped out through
> hibernate_preallocate_memory's reclaim path. Also remove
> shrink_shmem_memory since hibernate_preallocate_memory will have
> reclaimed enough memory for the hibernation image.
>
> Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
> ---
> Changes in v2:
> - Removed shrink_shmem_memory.
> - Fixed missing call to dpm_prepare in error path.
> - Link to v1: https://lore.kernel.org/r/20260321-hibernation-fixes-v1-1-5fe9637b6ff9@collabora.com
> ---
> kernel/power/hibernate.c | 46 +++++++++-------------------------------------
> 1 file changed, 9 insertions(+), 37 deletions(-)
>
> diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
> index af8d07bafe02..39b0a8ea4024 100644
> --- a/kernel/power/hibernate.c
> +++ b/kernel/power/hibernate.c
> @@ -392,23 +392,6 @@ static int create_image(int platform_mode)
> return error;
> }
>
> -static void shrink_shmem_memory(void)
> -{
> - struct sysinfo info;
> - unsigned long nr_shmem_pages, nr_freed_pages;
> -
> - si_meminfo(&info);
> - nr_shmem_pages = info.sharedram; /* current page count used for shmem */
> - /*
> - * The intent is to reclaim all shmem pages. Though shrink_all_memory() can
> - * only reclaim about half of them, it's enough for creating the hibernation
> - * image.
> - */
> - nr_freed_pages = shrink_all_memory(nr_shmem_pages);
> - pr_debug("requested to reclaim %lu shmem pages, actually freed %lu pages\n",
> - nr_shmem_pages, nr_freed_pages);
> -}
> -
> /**
> * hibernation_snapshot - Quiesce devices and create a hibernation image.
> * @platform_mode: If set, use platform driver to prepare for the transition.
> @@ -425,14 +408,9 @@ int hibernation_snapshot(int platform_mode)
> if (error)
> goto Close;
>
> - /* Preallocate image memory before shutting down devices. */
> - error = hibernate_preallocate_memory();
> - if (error)
> - goto Close;
> -
> error = freeze_kernel_threads();
> if (error)
> - goto Cleanup;
> + goto Close;
>
> if (hibernation_test(TEST_FREEZER)) {
>
> @@ -441,23 +419,17 @@ int hibernation_snapshot(int platform_mode)
> * successful freezer test.
> */
> freezer_test_done = true;
> - goto Thaw;
> + goto ThawKThreads;
Do not change the name of this label.
> }
>
> error = dpm_prepare(PMSG_FREEZE);
> - if (error) {
> - dpm_complete(PMSG_RECOVER);
> + if (error)
> goto Thaw;
But add a new label called "Complete" for this jump.
> - }
>
> - /*
> - * Device drivers may move lots of data to shmem in dpm_prepare(). The shmem
> - * pages will use lots of system memory, causing hibernation image creation
> - * fail due to insufficient free memory.
> - * This call is to force flush the shmem pages to swap disk and reclaim
> - * the system memory so that image creation can succeed.
> - */
> - shrink_shmem_memory();
> + /* Preallocate image memory before shutting down devices. */
> + error = hibernate_preallocate_memory();
> + if (error)
> + goto Thaw;
>
> console_suspend_all();
> pm_restrict_gfp_mask();
> @@ -493,9 +465,9 @@ int hibernation_snapshot(int platform_mode)
> return error;
>
> Thaw:
swsusp_free() needs to be called here AFAICS or you'll see memory
allocation failures subsequently in some cases.
> + dpm_complete(PMSG_RECOVER);
> + ThawKThreads:
> thaw_kernel_threads();
> - Cleanup:
> - swsusp_free();
> goto Close;
> }
>
>
> ---
^ permalink raw reply
* Re: [PATCH v5 09/11] mfd: bq257xx: Add BQ25792 support
From: Alexey Charkov @ 2026-03-31 12:37 UTC (permalink / raw)
To: Lee Jones
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chris Morgan,
Liam Girdwood, Mark Brown, Sebastian Reichel, devicetree,
linux-kernel, Sebastian Reichel, linux-pm
In-Reply-To: <20260331102710.GB3795166@google.com>
On Tue, Mar 31, 2026 at 2:27 PM Lee Jones <lee@kernel.org> wrote:
>
> On Tue, 24 Mar 2026, Alexey Charkov wrote:
>
> > Add register definitions and a new 'type' enum to be passed via MFD
> > private data to support the BQ25792, which is a newer variant of the
> > BQ257xx family.
> >
> > BQ25792 shares similar logic of operation with the already supported
> > BQ25703A but has a completely different register map and different
> > electrical constraints.
> >
> > Tested-by: Chris Morgan <macromorgan@hotmail.com>
> > Signed-off-by: Alexey Charkov <alchark@flipper.net>
> > ---
> > drivers/mfd/bq257xx.c | 54 +++++-
> > include/linux/mfd/bq257xx.h | 412 ++++++++++++++++++++++++++++++++++++++++++++
> > 2 files changed, 463 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/mfd/bq257xx.c b/drivers/mfd/bq257xx.c
> > index e9d49dac0a16..31654925afa5 100644
> > --- a/drivers/mfd/bq257xx.c
> > +++ b/drivers/mfd/bq257xx.c
> > @@ -39,6 +39,39 @@ static const struct regmap_config bq25703_regmap_config = {
> > .val_format_endian = REGMAP_ENDIAN_LITTLE,
> > };
> >
> > +static const struct regmap_range bq25792_writeable_reg_ranges[] = {
> > + regmap_reg_range(BQ25792_REG00_MIN_SYS_VOLTAGE,
> > + BQ25792_REG18_NTC_CONTROL_1),
> > + regmap_reg_range(BQ25792_REG28_CHARGER_MASK_0,
> > + BQ25792_REG30_ADC_FUNCTION_DISABLE_1),
> > +};
> > +
> > +static const struct regmap_access_table bq25792_writeable_regs = {
> > + .yes_ranges = bq25792_writeable_reg_ranges,
> > + .n_yes_ranges = ARRAY_SIZE(bq25792_writeable_reg_ranges),
> > +};
> > +
> > +static const struct regmap_range bq25792_volatile_reg_ranges[] = {
> > + regmap_reg_range(BQ25792_REG19_ICO_CURRENT_LIMIT,
> > + BQ25792_REG27_FAULT_FLAG_1),
> > + regmap_reg_range(BQ25792_REG31_IBUS_ADC,
> > + BQ25792_REG47_DPDM_DRIVER),
> > +};
> > +
> > +static const struct regmap_access_table bq25792_volatile_regs = {
> > + .yes_ranges = bq25792_volatile_reg_ranges,
> > + .n_yes_ranges = ARRAY_SIZE(bq25792_volatile_reg_ranges),
> > +};
> > +
> > +static const struct regmap_config bq25792_regmap_config = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > + .max_register = BQ25792_REG48_PART_INFORMATION,
> > + .cache_type = REGCACHE_MAPLE,
> > + .wr_table = &bq25792_writeable_regs,
> > + .volatile_table = &bq25792_volatile_regs,
> > +};
> > +
> > static const struct mfd_cell cells[] = {
> > MFD_CELL_NAME("bq257xx-regulator"),
> > MFD_CELL_NAME("bq257xx-charger"),
> > @@ -46,6 +79,7 @@ static const struct mfd_cell cells[] = {
> >
> > static int bq257xx_probe(struct i2c_client *client)
> > {
> > + const struct regmap_config *rcfg;
> > struct bq257xx_device *ddata;
> > int ret;
> >
> > @@ -53,9 +87,21 @@ static int bq257xx_probe(struct i2c_client *client)
> > if (!ddata)
> > return -ENOMEM;
> >
> > + ddata->type = (uintptr_t)i2c_get_match_data(client);
> > ddata->client = client;
> >
> > - ddata->regmap = devm_regmap_init_i2c(client, &bq25703_regmap_config);
> > + switch (ddata->type) {
> > + case BQ25703A:
> > + rcfg = &bq25703_regmap_config;
> > + break;
> > + case BQ25792:
> > + rcfg = &bq25792_regmap_config;
> > + break;
> > + default:
> > + return dev_err_probe(&client->dev, -EINVAL, "Unsupported device type\n");
>
> Nit: Shouldn't we be returning '-ENODEV' here for an unsupported device?
Hi Lee,
Indeed, I've had a quick look and other drivers seem to return -ENODEV
in similar situations. Shall I respin a new version with that change?
Thanks a lot,
Alexey
^ permalink raw reply
* Re: [PATCH v9 3/6] mfd: max77759: add register bitmasks and modify irq configs for charger
From: Lee Jones @ 2026-03-31 12:31 UTC (permalink / raw)
To: amitsd
Cc: Sebastian Reichel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
André Draszik, Greg Kroah-Hartman, Badhri Jagan Sridharan,
Heikki Krogerus, Peter Griffin, Tudor Ambarus, Alim Akhtar,
Mark Brown, Matti Vaittinen, Andrew Morton, linux-kernel,
linux-pm, devicetree, linux-usb, linux-arm-kernel,
linux-samsung-soc, RD Babiera, Kyle Tso
In-Reply-To: <20260325-max77759-charger-v9-3-4486dd297adc@google.com>
On Wed, 25 Mar 2026, Amit Sunil Dhamne via B4 Relay wrote:
> From: Amit Sunil Dhamne <amitsd@google.com>
>
> Add register bitmasks for charger function.
> In addition split the charger IRQs further such that each bit represents
> an IRQ downstream of charger regmap irq chip. In addition populate the
> ack_base to offload irq ack to the regmap irq chip framework.
>
> Signed-off-by: Amit Sunil Dhamne <amitsd@google.com>
> Reviewed-by: André Draszik <andre.draszik@linaro.org>
> ---
> drivers/mfd/max77759.c | 95 ++++++++++++++++++++++---
> include/linux/mfd/max77759.h | 166 +++++++++++++++++++++++++++++++++++--------
> 2 files changed, 222 insertions(+), 39 deletions(-)
>
[...]
> +/*
> + * enum max77759_chgr_chgin_dtls_status - Charger Input Status
> + * @MAX77759_CHGR_CHGIN_DTLS_VBUS_UNDERVOLTAGE:
> + * Charger input voltage (Vchgin) < Under Voltage Threshold (Vuvlo)
> + * @MAX77759_CHGR_CHGIN_DTLS_VBUS_MARGINAL_VOLTAGE: Vchgin > Vuvlo and
> + * Vchgin < (Battery Voltage (Vbatt) + system voltage (Vsys))
> + * @MAX77759_CHGR_CHGIN_DTLS_VBUS_OVERVOLTAGE:
> + * Vchgin > Over Voltage threshold (Vovlo)
> + * @MAX77759_CHGR_CHGIN_DTLS_VBUS_VALID:
> + * Vchgin > Vuvlo, Vchgin < Vovlo and Vchgin > (Vsys + Vbatt)
> + */
This comment is masquerading as a kernel-doc header, but isn't actually
kernel-doc. Either change the formatting or adapt the formatting to use
/** and use W=1 to check it.
> +enum max77759_chgr_chgin_dtls_status {
> + MAX77759_CHGR_CHGIN_DTLS_VBUS_UNDERVOLTAGE,
> + MAX77759_CHGR_CHGIN_DTLS_VBUS_MARGINAL_VOLTAGE,
> + MAX77759_CHGR_CHGIN_DTLS_VBUS_OVERVOLTAGE,
> + MAX77759_CHGR_CHGIN_DTLS_VBUS_VALID,
> +};
--
Lee Jones [李琼斯]
^ permalink raw reply
* Re: [PATCH v5 0/5] amd-pstate Dynamic EPP and raw EPP
From: Mario Limonciello @ 2026-03-31 12:09 UTC (permalink / raw)
To: Gautham R . Shenoy
Cc: Perry Yuan, open list:X86 ARCHITECTURE (32-BIT AND 64-BIT),
open list:CPU FREQUENCY SCALING FRAMEWORK
In-Reply-To: <20260106051441.60093-1-superm1@kernel.org>
On 1/5/26 11:14 PM, Mario Limonciello (AMD) wrote:
> Dynamic EPP allows the kernel to register amd-pstate as part of
> a platform profile. It will change EPP modes matching the user's
> preference to the platform profile sysfs files as well as power
> adapter state.
>
> Raw EPP allows userspace to write integers to
> energy_performance_preference.
>
> v4: https://lore.kernel.org/linux-pm/20250321022858.1538173-1-superm1@kernel.org/
> Mario Limonciello (5):
> cpufreq/amd-pstate: Add dynamic energy performance preference
> cpufreq/amd-pstate: add kernel command line to override dynamic epp
> cpufreq/amd-pstate: Add support for platform profile class
> cpufreq/amd-pstate: Add support for raw EPP writes
> cpufreq/amd-pstate-ut: Add a unit test for raw EPP
>
> .../admin-guide/kernel-parameters.txt | 7 +
> Documentation/admin-guide/pm/amd-pstate.rst | 41 ++-
> drivers/cpufreq/Kconfig.x86 | 13 +
> drivers/cpufreq/amd-pstate-ut.c | 58 ++++
> drivers/cpufreq/amd-pstate.c | 283 ++++++++++++++++--
> drivers/cpufreq/amd-pstate.h | 16 +-
> 6 files changed, 394 insertions(+), 24 deletions(-)
>
I've applied this series.
^ permalink raw reply
* Re: [PATCH v3] cpufreq/amd-pstate: Cache the max frequency in cpudata
From: Mario Limonciello @ 2026-03-31 12:09 UTC (permalink / raw)
To: Gautham R. Shenoy, Mario Limonciello
Cc: Perry Yuan, open list:X86 ARCHITECTURE (32-BIT AND 64-BIT),
open list:CPU FREQUENCY SCALING FRAMEWORK
In-Reply-To: <acuP/Dqh6+jBm+23@BLRRASHENOY1.amd.com>
On 3/31/26 4:12 AM, Gautham R. Shenoy wrote:
> Hello Mario,
>
> On Thu, Mar 26, 2026 at 02:36:20PM -0500, Mario Limonciello wrote:
>> From: "Mario Limonciello (AMD)" <superm1@kernel.org>
>>
>> The value of maximum frequency is fixed and never changes. Doing
>> calculations every time based off of perf is unnecessary.
>>
>> Signed-off-by: Mario Limonciello (AMD) <superm1@kernel.org>
>
> This one looks good to me. Thanks for fixing this.
>
> Reviewed-by: Gautham R. Shenoy <gautham.shenoy@amd.com>
>
Thanks, I've applied it.
^ permalink raw reply
* Re: [PATCH] Documentation: amd-pstate: fix dead links in the reference section
From: Mario Limonciello @ 2026-03-31 11:58 UTC (permalink / raw)
To: Ninad Naik, ray.huang, gautham.shenoy, mario.limonciello,
perry.yuan, corbet, skhan
Cc: linux-pm, linux-doc, linux-kernel, me
In-Reply-To: <20260330190855.1115304-1-ninadnaik07@gmail.com>
On 3/30/26 2:08 PM, Ninad Naik wrote:
> The links for AMD64 Architecture Programmer's Manual and PPR for AMD
> Family 19h Model 51h, Revision A1 Processors redirect to a generic page.
> Update the links to the working ones.
>
> Signed-off-by: Ninad Naik <ninadnaik07@gmail.com>
Acked-by: Mario Limonciello (AMD) <superm1@kernel.org>
Applied.
> ---
> Documentation/admin-guide/pm/amd-pstate.rst | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/admin-guide/pm/amd-pstate.rst b/Documentation/admin-guide/pm/amd-pstate.rst
> index e1771f2225d5..13d6580894bc 100644
> --- a/Documentation/admin-guide/pm/amd-pstate.rst
> +++ b/Documentation/admin-guide/pm/amd-pstate.rst
> @@ -790,13 +790,13 @@ Reference
> ===========
>
> .. [1] AMD64 Architecture Programmer's Manual Volume 2: System Programming,
> - https://www.amd.com/system/files/TechDocs/24593.pdf
> + https://docs.amd.com/v/u/en-US/24593_3.44_APM_Vol2
>
> .. [2] Advanced Configuration and Power Interface Specification,
> https://uefi.org/sites/default/files/resources/ACPI_Spec_6_4_Jan22.pdf
>
> .. [3] Processor Programming Reference (PPR) for AMD Family 19h Model 51h, Revision A1 Processors
> - https://www.amd.com/system/files/TechDocs/56569-A1-PUB.zip
> + https://docs.amd.com/v/u/en-US/56569-A1-PUB_3.03
>
> .. [4] Linux Kernel Selftests,
> https://www.kernel.org/doc/html/latest/dev-tools/kselftest.html
^ permalink raw reply
* Re: [PATCH v5 2/5] media: iris: scale MMCX power domain on SM8250
From: Ulf Hansson @ 2026-03-31 11:33 UTC (permalink / raw)
To: Dikshita Agarwal, Dmitry Baryshkov
Cc: Bjorn Andersson, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Taniya Das, Jonathan Marek,
Rafael J. Wysocki, Bryan O'Donoghue, Vikash Garodia,
Mauro Carvalho Chehab, Stanimir Varbanov, Abhinav Kumar,
Hans Verkuil, Stefan Schmidt, Konrad Dybcio, Bryan O'Donoghue,
Dikshita Agarwal, linux-arm-msm, linux-clk, devicetree,
linux-kernel, linux-pm, linux-media, Mauro Carvalho Chehab
In-Reply-To: <0b41ee7c-83fe-d604-b750-8a5a0bd62bf8@oss.qualcomm.com>
On Mon, 30 Mar 2026 at 15:06, Dikshita Agarwal
<dikshita.agarwal@oss.qualcomm.com> wrote:
>
>
>
> On 3/30/2026 4:45 PM, Dmitry Baryshkov wrote:
> > On Mon, Mar 30, 2026 at 10:55:02AM +0530, Dikshita Agarwal wrote:
> >>
> >>
> >> On 2/9/2026 7:02 AM, Dmitry Baryshkov wrote:
> >>> On SM8250 most of the video clocks are powered by the MMCX domain, while
> >>> the PLL is powered on by the MX domain. Extend the driver to support
> >>> scaling both power domains, while keeping compatibility with the
> >>> existing DTs, which define only the MX domain.
> >>>
> >>> Fixes: 79865252acb6 ("media: iris: enable video driver probe of SM8250 SoC")
> >>> Reviewed-by: Dikshita Agarwal <dikshita.agarwal@oss.qualcomm.com>
> >>> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> >>> ---
> >>> drivers/media/platform/qcom/iris/iris_platform_gen1.c | 2 +-
> >>> drivers/media/platform/qcom/iris/iris_probe.c | 7 +++++++
> >>> 2 files changed, 8 insertions(+), 1 deletion(-)
> >>>
> >>> diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_platform_gen1.c
> >>> index df8e6bf9430e..aa71f7f53ee3 100644
> >>> --- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c
> >>> +++ b/drivers/media/platform/qcom/iris/iris_platform_gen1.c
> >>> @@ -281,7 +281,7 @@ static const struct bw_info sm8250_bw_table_dec[] = {
> >>>
> >>> static const char * const sm8250_pmdomain_table[] = { "venus", "vcodec0" };
> >>>
> >>> -static const char * const sm8250_opp_pd_table[] = { "mx" };
> >>> +static const char * const sm8250_opp_pd_table[] = { "mx", "mmcx" };
> >>>
> >>> static const struct platform_clk_data sm8250_clk_table[] = {
> >>> {IRIS_AXI_CLK, "iface" },
> >>> diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c
> >>> index 7b612ad37e4f..74ec81e3d622 100644
> >>> --- a/drivers/media/platform/qcom/iris/iris_probe.c
> >>> +++ b/drivers/media/platform/qcom/iris/iris_probe.c
> >>> @@ -64,6 +64,13 @@ static int iris_init_power_domains(struct iris_core *core)
> >>> return ret;
> >>>
> >>> ret = devm_pm_domain_attach_list(core->dev, &iris_opp_pd_data, &core->opp_pmdomain_tbl);
> >>> + /* backwards compatibility for incomplete ABI SM8250 */
> >>> + if (ret == -ENODEV &&
> >>> + of_device_is_compatible(core->dev->of_node, "qcom,sm8250-venus")) {
> >>> + iris_opp_pd_data.num_pd_names--;
> >>> + ret = devm_pm_domain_attach_list(core->dev, &iris_opp_pd_data,
> >>> + &core->opp_pmdomain_tbl);
> >>> + }
> >>> if (ret < 0)
> >>> return ret;
> >>>
> >>>
> >>
> >> Hitting below compilation error on latest kernel
> >>
> >> drivers/media/platform/qcom/iris/iris_probe.c: In function
> >> ‘iris_init_power_domains’:
> >> drivers/media/platform/qcom/iris/iris_probe.c:71:46: error: decrement of
> >> read-only member ‘num_pd_names’
> >> 71 | iris_opp_pd_data.num_pd_names--;
> >
> > See commit 7ad7f43e568b ("pmdomain: de-constify fields struct
> > dev_pm_domain_attach_data")
The intent was for this patch to be part of v7.0-rc1, but I failed
with my pull-request to Linus.
Instead this will be part of v7.1-rc1, assuming everything goes as expected.
Is it possible to drop/defer these changes until v7.2?
Kind regards
Uffe
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox