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