cpuidle: psci: Correct the domain-idlestate statistics in debugfs

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>
Link: https://lore.kernel.org/r/20250314100103.1294715-4-ulf.hansson@linaro.org
This commit is contained in:
Ulf Hansson
2025-03-14 11:00:57 +01:00
parent 3290e9f98a
commit d0252ba821
3 changed files with 21 additions and 12 deletions

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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 */