* [PATCH 0/4] pmdomain/cpuidle-psci: Improve domain-idlestate statistics
@ 2025-03-14 10:00 Ulf Hansson
2025-03-14 10:00 ` [PATCH 1/4] pmdomain: core: Add genpd helper to correct the usage/rejected counters Ulf Hansson
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Ulf Hansson @ 2025-03-14 10:00 UTC (permalink / raw)
To: Rafael J . Wysocki, Sudeep Holla, linux-pm
Cc: Lorenzo Pieralisi, Daniel Lezcano, Anup Patel, Ulf Hansson,
linux-arm-kernel, linux-kernel
This series improves and extends the support for domain-idlestates statistic
for genpd and the cpuidle-psci-domain. More information is available in each
commit message.
Please help to review and test!
Kind regards
Ulf Hansson
Ulf Hansson (4):
pmdomain: core: Add genpd helper to correct the usage/rejected
counters
cpuidle: psci: Move the per CPU variable domain_state to a struct
cpuidle: psci: Correct the domain-idlestate statistics in debugfs
pmdomain: core: Add residency reflection for domain-idlestates to
debugfs
drivers/cpuidle/cpuidle-psci-domain.c | 2 +-
drivers/cpuidle/cpuidle-psci.c | 40 ++++++++++++-----
drivers/cpuidle/cpuidle-psci.h | 4 +-
drivers/pmdomain/core.c | 65 +++++++++++++++++++++++++--
drivers/pmdomain/governor.c | 2 +
include/linux/pm_domain.h | 10 +++++
6 files changed, 106 insertions(+), 17 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/4] pmdomain: core: Add genpd helper to correct the usage/rejected counters
2025-03-14 10:00 [PATCH 0/4] pmdomain/cpuidle-psci: Improve domain-idlestate statistics Ulf Hansson
@ 2025-03-14 10:00 ` Ulf Hansson
2025-03-14 10:00 ` [PATCH 2/4] cpuidle: psci: Move the per CPU variable domain_state to a struct Ulf Hansson
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Ulf Hansson @ 2025-03-14 10:00 UTC (permalink / raw)
To: Rafael J . Wysocki, Sudeep Holla, linux-pm
Cc: Lorenzo Pieralisi, Daniel Lezcano, Anup Patel, Ulf Hansson,
linux-arm-kernel, linux-kernel
In the cpuidle-psci-domain case the ->power_off() callback is usually
returning zero to indicate success. This is because the actual call to the
PSCI FW to enter the selected domain-idlestate, needs to be done after the
->power_off() callback has returned.
When the call to the PSCI FW fails, this leads to receiving an incorrect
tracking of the usage/rejected counts for the selected domain-idlestate.
In other words, the presented debug-statistics for genpd may look better
than what the actually are.
To allow a better correctness of the data, let's add a new genpd helper
function, which enables the caller adjust the usage/rejected counters for a
domain-idlestate, in cases of errors during power-off.
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
---
drivers/pmdomain/core.c | 25 +++++++++++++++++++++++++
include/linux/pm_domain.h | 6 ++++++
2 files changed, 31 insertions(+)
diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c
index 9b2f28b34bb5..c79ef6e3ab85 100644
--- a/drivers/pmdomain/core.c
+++ b/drivers/pmdomain/core.c
@@ -728,6 +728,31 @@ int dev_pm_genpd_rpm_always_on(struct device *dev, bool on)
}
EXPORT_SYMBOL_GPL(dev_pm_genpd_rpm_always_on);
+/**
+ * pm_genpd_inc_rejected() - Adjust the rejected/usage counts for an idle-state.
+ *
+ * @genpd: The PM domain the idle-state belongs to.
+ * @state_idx: The index of the idle-state that failed.
+ *
+ * In some special cases the ->power_off() callback is asynchronously powering
+ * off the PM domain, leading to that it may return zero to indicate success,
+ * even though the actual power-off could fail. To account for this correctly in
+ * the rejected/usage counts for the idle-state statistics, users can call this
+ * function to adjust the values.
+ *
+ * It is assumed that the users guarantee that the genpd doesn't get removed
+ * while this routine is getting called.
+ */
+void pm_genpd_inc_rejected(struct generic_pm_domain *genpd,
+ unsigned int state_idx)
+{
+ genpd_lock(genpd);
+ genpd->states[genpd->state_idx].rejected++;
+ genpd->states[genpd->state_idx].usage--;
+ genpd_unlock(genpd);
+}
+EXPORT_SYMBOL_GPL(pm_genpd_inc_rejected);
+
static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)
{
unsigned int state_idx = genpd->state_idx;
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index d56a78af4af1..6e808aeecbcb 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -285,6 +285,8 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
int pm_genpd_init(struct generic_pm_domain *genpd,
struct dev_power_governor *gov, bool is_off);
int pm_genpd_remove(struct generic_pm_domain *genpd);
+void pm_genpd_inc_rejected(struct generic_pm_domain *genpd,
+ unsigned int state_idx);
struct device *dev_to_genpd_dev(struct device *dev);
int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state);
int dev_pm_genpd_add_notifier(struct device *dev, struct notifier_block *nb);
@@ -336,6 +338,10 @@ static inline int pm_genpd_remove(struct generic_pm_domain *genpd)
return -EOPNOTSUPP;
}
+static inline void pm_genpd_inc_rejected(struct generic_pm_domain *genpd,
+ unsigned int state_idx)
+{ }
+
static inline struct device *dev_to_genpd_dev(struct device *dev)
{
return ERR_PTR(-EOPNOTSUPP);
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/4] cpuidle: psci: Move the per CPU variable domain_state to a struct
2025-03-14 10:00 [PATCH 0/4] pmdomain/cpuidle-psci: Improve domain-idlestate statistics Ulf Hansson
2025-03-14 10:00 ` [PATCH 1/4] pmdomain: core: Add genpd helper to correct the usage/rejected counters Ulf Hansson
@ 2025-03-14 10:00 ` Ulf Hansson
2025-03-14 10:00 ` [PATCH 3/4] cpuidle: psci: Correct the domain-idlestate statistics in debugfs Ulf Hansson
2025-03-14 10:00 ` [PATCH 4/4] pmdomain: core: Add residency reflection for domain-idlestates to debugfs Ulf Hansson
3 siblings, 0 replies; 5+ messages in thread
From: Ulf Hansson @ 2025-03-14 10:00 UTC (permalink / raw)
To: Rafael J . Wysocki, Sudeep Holla, linux-pm
Cc: Lorenzo Pieralisi, Daniel Lezcano, Anup Patel, Ulf Hansson,
linux-arm-kernel, linux-kernel
To prepare to extend the per CPU variable domain_state to include more
data, let's move it into a struct. A subsequent change will add the new
data. This change have no intended functional impact.
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
---
drivers/cpuidle/cpuidle-psci.c | 21 +++++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/drivers/cpuidle/cpuidle-psci.c b/drivers/cpuidle/cpuidle-psci.c
index dd8d776d6e39..1aff1ec555d5 100644
--- a/drivers/cpuidle/cpuidle-psci.c
+++ b/drivers/cpuidle/cpuidle-psci.c
@@ -36,19 +36,28 @@ struct psci_cpuidle_data {
struct device *dev;
};
+struct psci_cpuidle_domain_state {
+ u32 state;
+};
+
static DEFINE_PER_CPU_READ_MOSTLY(struct psci_cpuidle_data, psci_cpuidle_data);
-static DEFINE_PER_CPU(u32, domain_state);
+static DEFINE_PER_CPU(struct psci_cpuidle_domain_state, psci_domain_state);
static bool psci_cpuidle_use_syscore;
static bool psci_cpuidle_use_cpuhp;
void psci_set_domain_state(u32 state)
{
- __this_cpu_write(domain_state, state);
+ __this_cpu_write(psci_domain_state.state, state);
}
static inline u32 psci_get_domain_state(void)
{
- return __this_cpu_read(domain_state);
+ return __this_cpu_read(psci_domain_state.state);
+}
+
+static inline void psci_clear_domain_state(void)
+{
+ __this_cpu_write(psci_domain_state.state, 0);
}
static __cpuidle int __psci_enter_domain_idle_state(struct cpuidle_device *dev,
@@ -87,7 +96,7 @@ static __cpuidle int __psci_enter_domain_idle_state(struct cpuidle_device *dev,
cpu_pm_exit();
/* Clear the domain state to start fresh when back from idle. */
- psci_set_domain_state(0);
+ psci_clear_domain_state();
return ret;
}
@@ -121,7 +130,7 @@ static int psci_idle_cpuhp_down(unsigned int cpu)
if (pd_dev) {
pm_runtime_put_sync(pd_dev);
/* Clear domain state to start fresh at next online. */
- psci_set_domain_state(0);
+ psci_clear_domain_state();
}
return 0;
@@ -147,7 +156,7 @@ static void psci_idle_syscore_switch(bool suspend)
/* Clear domain state to re-start fresh. */
if (!cleared) {
- psci_set_domain_state(0);
+ psci_clear_domain_state();
cleared = true;
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 3/4] cpuidle: psci: Correct the domain-idlestate statistics in debugfs
2025-03-14 10:00 [PATCH 0/4] pmdomain/cpuidle-psci: Improve domain-idlestate statistics Ulf Hansson
2025-03-14 10:00 ` [PATCH 1/4] pmdomain: core: Add genpd helper to correct the usage/rejected counters Ulf Hansson
2025-03-14 10:00 ` [PATCH 2/4] cpuidle: psci: Move the per CPU variable domain_state to a struct Ulf Hansson
@ 2025-03-14 10:00 ` Ulf Hansson
2025-03-14 10:00 ` [PATCH 4/4] pmdomain: core: Add residency reflection for domain-idlestates to debugfs Ulf Hansson
3 siblings, 0 replies; 5+ messages in thread
From: Ulf Hansson @ 2025-03-14 10:00 UTC (permalink / raw)
To: Rafael J . Wysocki, Sudeep Holla, linux-pm
Cc: Lorenzo Pieralisi, Daniel Lezcano, Anup Patel, Ulf Hansson,
linux-arm-kernel, linux-kernel
When trying to enter a domain-idlestate, we may occasionally fail to enter
the state, which is informed to us by psci_cpu_suspend_enter() returning an
error-code. In these cases, our corresponding genpd->power_off() callback
has already returned zero to indicate success, leading to getting
in-correct domain-idlestate statistics in debugfs for the genpd in
question.
Let's fix this by making use of the new pm_genpd_inc_rejected() helper, as
it allows us to correct the domain-idlestate statistics for this type of
scenario.
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
---
drivers/cpuidle/cpuidle-psci-domain.c | 2 +-
drivers/cpuidle/cpuidle-psci.c | 27 +++++++++++++++++----------
drivers/cpuidle/cpuidle-psci.h | 4 +++-
3 files changed, 21 insertions(+), 12 deletions(-)
diff --git a/drivers/cpuidle/cpuidle-psci-domain.c b/drivers/cpuidle/cpuidle-psci-domain.c
index 5fb5228f6bf1..2041f59116ce 100644
--- a/drivers/cpuidle/cpuidle-psci-domain.c
+++ b/drivers/cpuidle/cpuidle-psci-domain.c
@@ -43,7 +43,7 @@ static int psci_pd_power_off(struct generic_pm_domain *pd)
/* OSI mode is enabled, set the corresponding domain state. */
pd_state = state->data;
- psci_set_domain_state(*pd_state);
+ psci_set_domain_state(pd, pd->state_idx, *pd_state);
return 0;
}
diff --git a/drivers/cpuidle/cpuidle-psci.c b/drivers/cpuidle/cpuidle-psci.c
index 1aff1ec555d5..26a0885444c4 100644
--- a/drivers/cpuidle/cpuidle-psci.c
+++ b/drivers/cpuidle/cpuidle-psci.c
@@ -37,6 +37,8 @@ struct psci_cpuidle_data {
};
struct psci_cpuidle_domain_state {
+ struct generic_pm_domain *pd;
+ unsigned int state_idx;
u32 state;
};
@@ -45,14 +47,14 @@ static DEFINE_PER_CPU(struct psci_cpuidle_domain_state, psci_domain_state);
static bool psci_cpuidle_use_syscore;
static bool psci_cpuidle_use_cpuhp;
-void psci_set_domain_state(u32 state)
+void psci_set_domain_state(struct generic_pm_domain *pd, unsigned int state_idx,
+ u32 state)
{
- __this_cpu_write(psci_domain_state.state, state);
-}
+ struct psci_cpuidle_domain_state *ds = this_cpu_ptr(&psci_domain_state);
-static inline u32 psci_get_domain_state(void)
-{
- return __this_cpu_read(psci_domain_state.state);
+ ds->pd = pd;
+ ds->state_idx = state_idx;
+ ds->state = state;
}
static inline void psci_clear_domain_state(void)
@@ -67,7 +69,8 @@ static __cpuidle int __psci_enter_domain_idle_state(struct cpuidle_device *dev,
struct psci_cpuidle_data *data = this_cpu_ptr(&psci_cpuidle_data);
u32 *states = data->psci_states;
struct device *pd_dev = data->dev;
- u32 state;
+ struct psci_cpuidle_domain_state *ds;
+ u32 state = states[idx];
int ret;
ret = cpu_pm_enter();
@@ -80,9 +83,9 @@ static __cpuidle int __psci_enter_domain_idle_state(struct cpuidle_device *dev,
else
pm_runtime_put_sync_suspend(pd_dev);
- state = psci_get_domain_state();
- if (!state)
- state = states[idx];
+ ds = this_cpu_ptr(&psci_domain_state);
+ if (ds->state)
+ state = ds->state;
trace_psci_domain_idle_enter(dev->cpu, state, s2idle);
ret = psci_cpu_suspend_enter(state) ? -1 : idx;
@@ -95,6 +98,10 @@ static __cpuidle int __psci_enter_domain_idle_state(struct cpuidle_device *dev,
cpu_pm_exit();
+ /* Correct domain-idlestate statistics if we failed to enter. */
+ if (ret == -1 && ds->state)
+ pm_genpd_inc_rejected(ds->pd, ds->state_idx);
+
/* Clear the domain state to start fresh when back from idle. */
psci_clear_domain_state();
return ret;
diff --git a/drivers/cpuidle/cpuidle-psci.h b/drivers/cpuidle/cpuidle-psci.h
index ef004ec7a7c5..d29cbd796cd5 100644
--- a/drivers/cpuidle/cpuidle-psci.h
+++ b/drivers/cpuidle/cpuidle-psci.h
@@ -4,8 +4,10 @@
#define __CPUIDLE_PSCI_H
struct device_node;
+struct generic_pm_domain;
-void psci_set_domain_state(u32 state);
+void psci_set_domain_state(struct generic_pm_domain *pd, unsigned int state_idx,
+ u32 state);
int psci_dt_parse_state_node(struct device_node *np, u32 *state);
#endif /* __CPUIDLE_PSCI_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 4/4] pmdomain: core: Add residency reflection for domain-idlestates to debugfs
2025-03-14 10:00 [PATCH 0/4] pmdomain/cpuidle-psci: Improve domain-idlestate statistics Ulf Hansson
` (2 preceding siblings ...)
2025-03-14 10:00 ` [PATCH 3/4] cpuidle: psci: Correct the domain-idlestate statistics in debugfs Ulf Hansson
@ 2025-03-14 10:00 ` Ulf Hansson
3 siblings, 0 replies; 5+ messages in thread
From: Ulf Hansson @ 2025-03-14 10:00 UTC (permalink / raw)
To: Rafael J . Wysocki, Sudeep Holla, linux-pm
Cc: Lorenzo Pieralisi, Daniel Lezcano, Anup Patel, Ulf Hansson,
linux-arm-kernel, linux-kernel
For regular cpuidle states we are reflecting over the selected/entered
state to see if the sleep-duration meets the residency for the state. The
output from the reflection is an "above" value to indicate the number of
times the state was too deep and a "below" value for the number of times it
was too shallow.
Let's implement the similar thing for genpd's domain-idlestates along with
genpd's governor and put the information in the genpd's debugfs.
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
---
drivers/pmdomain/core.c | 40 ++++++++++++++++++++++++++++++++++---
drivers/pmdomain/governor.c | 2 ++
include/linux/pm_domain.h | 4 ++++
3 files changed, 43 insertions(+), 3 deletions(-)
diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c
index c79ef6e3ab85..3327de2f9ed2 100644
--- a/drivers/pmdomain/core.c
+++ b/drivers/pmdomain/core.c
@@ -304,10 +304,40 @@ static void genpd_update_accounting(struct generic_pm_domain *genpd)
genpd->accounting_time = now;
}
+
+static void genpd_reflect_residency(struct generic_pm_domain *genpd)
+{
+ struct genpd_governor_data *gd = genpd->gd;
+ struct genpd_power_state *state, *next_state;
+ unsigned int state_idx;
+ s64 sleep_ns, target_ns;
+
+ if (!gd || !gd->reflect_residency)
+ return;
+
+ sleep_ns = ktime_to_ns(ktime_sub(ktime_get(), gd->last_enter));
+ state_idx = genpd->state_idx;
+ state = &genpd->states[state_idx];
+ target_ns = state->power_off_latency_ns + state->residency_ns;
+
+ if (sleep_ns < target_ns) {
+ state->above++;
+ } else if (state_idx < (genpd->state_count -1)) {
+ next_state = &genpd->states[state_idx + 1];
+ target_ns = next_state->power_off_latency_ns +
+ next_state->residency_ns;
+
+ if (sleep_ns >= target_ns)
+ state->below++;
+ }
+
+ gd->reflect_residency = false;
+}
#else
static inline void genpd_debug_add(struct generic_pm_domain *genpd) {}
static inline void genpd_debug_remove(struct generic_pm_domain *genpd) {}
static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {}
+static inline void genpd_reflect_residency(struct generic_pm_domain *genpd) {}
#endif
static int _genpd_reeval_performance_state(struct generic_pm_domain *genpd,
@@ -982,6 +1012,9 @@ static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth)
if (genpd_status_on(genpd))
return 0;
+ /* Reflect over the entered idle-states residency for debugfs. */
+ genpd_reflect_residency(genpd);
+
/*
* The list is guaranteed not to change while the loop below is being
* executed, unless one of the parents' .power_on() callbacks fiddles
@@ -3517,7 +3550,7 @@ static int idle_states_show(struct seq_file *s, void *data)
if (ret)
return -ERESTARTSYS;
- seq_puts(s, "State Time Spent(ms) Usage Rejected\n");
+ seq_puts(s, "State Time Spent(ms) Usage Rejected Above Below\n");
for (i = 0; i < genpd->state_count; i++) {
struct genpd_power_state *state = &genpd->states[i];
@@ -3537,9 +3570,10 @@ static int idle_states_show(struct seq_file *s, void *data)
snprintf(state_name, ARRAY_SIZE(state_name), "S%-13d", i);
do_div(idle_time, NSEC_PER_MSEC);
- seq_printf(s, "%-14s %-14llu %-14llu %llu\n",
+ seq_printf(s, "%-14s %-14llu %-10llu %-10llu %-10llu %llu\n",
state->name ?: state_name, idle_time,
- state->usage, state->rejected);
+ state->usage, state->rejected, state->above,
+ state->below);
}
genpd_unlock(genpd);
diff --git a/drivers/pmdomain/governor.c b/drivers/pmdomain/governor.c
index d1a10eeebd16..c1e148657c87 100644
--- a/drivers/pmdomain/governor.c
+++ b/drivers/pmdomain/governor.c
@@ -392,6 +392,8 @@ static bool cpu_power_down_ok(struct dev_pm_domain *pd)
if (idle_duration_ns >= (genpd->states[i].residency_ns +
genpd->states[i].power_off_latency_ns)) {
genpd->state_idx = i;
+ genpd->gd->last_enter = now;
+ genpd->gd->reflect_residency = true;
return true;
}
} while (--i >= 0);
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 6e808aeecbcb..0b18160901a2 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -142,6 +142,8 @@ struct genpd_governor_data {
bool max_off_time_changed;
ktime_t next_wakeup;
ktime_t next_hrtimer;
+ ktime_t last_enter;
+ bool reflect_residency;
bool cached_power_down_ok;
bool cached_power_down_state_idx;
};
@@ -153,6 +155,8 @@ struct genpd_power_state {
s64 residency_ns;
u64 usage;
u64 rejected;
+ u64 above;
+ u64 below;
struct fwnode_handle *fwnode;
u64 idle_time;
void *data;
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2025-03-14 10:12 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-03-14 10:00 [PATCH 0/4] pmdomain/cpuidle-psci: Improve domain-idlestate statistics Ulf Hansson
2025-03-14 10:00 ` [PATCH 1/4] pmdomain: core: Add genpd helper to correct the usage/rejected counters Ulf Hansson
2025-03-14 10:00 ` [PATCH 2/4] cpuidle: psci: Move the per CPU variable domain_state to a struct Ulf Hansson
2025-03-14 10:00 ` [PATCH 3/4] cpuidle: psci: Correct the domain-idlestate statistics in debugfs Ulf Hansson
2025-03-14 10:00 ` [PATCH 4/4] pmdomain: core: Add residency reflection for domain-idlestates to debugfs Ulf Hansson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).