Commit f38d1a6d authored by Ulf Hansson's avatar Ulf Hansson Committed by Rafael J. Wysocki

PM: domains: Allocate governor data dynamically based on a genpd governor

If a genpd doesn't have an associated governor assigned, several variables
in the struct generic_pm_domain becomes superfluous.

Rather than wasting memory in allocated genpds, let's move the variables
from the struct generic_pm_domain into a new separate struct. In this way,
we can instead dynamically decide when we need to allocate the
corresponding data for it.
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent ba43d6db
...@@ -527,7 +527,8 @@ static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) ...@@ -527,7 +527,8 @@ static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)
goto out; goto out;
genpd->states[state_idx].power_on_latency_ns = elapsed_ns; genpd->states[state_idx].power_on_latency_ns = elapsed_ns;
genpd->max_off_time_changed = true; if (genpd->gd)
genpd->gd->max_off_time_changed = true;
pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n",
genpd->name, "on", elapsed_ns); genpd->name, "on", elapsed_ns);
...@@ -576,7 +577,8 @@ static int _genpd_power_off(struct generic_pm_domain *genpd, bool timed) ...@@ -576,7 +577,8 @@ static int _genpd_power_off(struct generic_pm_domain *genpd, bool timed)
goto out; goto out;
genpd->states[state_idx].power_off_latency_ns = elapsed_ns; genpd->states[state_idx].power_off_latency_ns = elapsed_ns;
genpd->max_off_time_changed = true; if (genpd->gd)
genpd->gd->max_off_time_changed = true;
pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n",
genpd->name, "off", elapsed_ns); genpd->name, "off", elapsed_ns);
...@@ -772,7 +774,7 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, ...@@ -772,7 +774,7 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
dev = gpd_data->base.dev; dev = gpd_data->base.dev;
for (;;) { for (;;) {
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd = ERR_PTR(-ENODATA);
struct pm_domain_data *pdd; struct pm_domain_data *pdd;
struct gpd_timing_data *td; struct gpd_timing_data *td;
...@@ -782,18 +784,17 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, ...@@ -782,18 +784,17 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
dev->power.subsys_data->domain_data : NULL; dev->power.subsys_data->domain_data : NULL;
if (pdd) { if (pdd) {
td = to_gpd_data(pdd)->td; td = to_gpd_data(pdd)->td;
if (td) if (td) {
td->constraint_changed = true; td->constraint_changed = true;
genpd = dev_to_genpd(dev); genpd = dev_to_genpd(dev);
} else { }
genpd = ERR_PTR(-ENODATA);
} }
spin_unlock_irq(&dev->power.lock); spin_unlock_irq(&dev->power.lock);
if (!IS_ERR(genpd)) { if (!IS_ERR(genpd)) {
genpd_lock(genpd); genpd_lock(genpd);
genpd->max_off_time_changed = true; genpd->gd->max_off_time_changed = true;
genpd_unlock(genpd); genpd_unlock(genpd);
} }
...@@ -922,7 +923,7 @@ static int genpd_runtime_suspend(struct device *dev) ...@@ -922,7 +923,7 @@ static int genpd_runtime_suspend(struct device *dev)
td->suspend_latency_ns = elapsed_ns; td->suspend_latency_ns = elapsed_ns;
dev_dbg(dev, "suspend latency exceeded, %lld ns\n", dev_dbg(dev, "suspend latency exceeded, %lld ns\n",
elapsed_ns); elapsed_ns);
genpd->max_off_time_changed = true; genpd->gd->max_off_time_changed = true;
td->constraint_changed = true; td->constraint_changed = true;
} }
} }
...@@ -1002,7 +1003,7 @@ static int genpd_runtime_resume(struct device *dev) ...@@ -1002,7 +1003,7 @@ static int genpd_runtime_resume(struct device *dev)
td->resume_latency_ns = elapsed_ns; td->resume_latency_ns = elapsed_ns;
dev_dbg(dev, "resume latency exceeded, %lld ns\n", dev_dbg(dev, "resume latency exceeded, %lld ns\n",
elapsed_ns); elapsed_ns);
genpd->max_off_time_changed = true; genpd->gd->max_off_time_changed = true;
td->constraint_changed = true; td->constraint_changed = true;
} }
} }
...@@ -1617,6 +1618,7 @@ static int genpd_get_cpu(struct generic_pm_domain *genpd, struct device *dev) ...@@ -1617,6 +1618,7 @@ static int genpd_get_cpu(struct generic_pm_domain *genpd, struct device *dev)
static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
struct device *base_dev) struct device *base_dev)
{ {
struct genpd_governor_data *gd = genpd->gd;
struct generic_pm_domain_data *gpd_data; struct generic_pm_domain_data *gpd_data;
int ret; int ret;
...@@ -1625,7 +1627,7 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, ...@@ -1625,7 +1627,7 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
return -EINVAL; return -EINVAL;
gpd_data = genpd_alloc_dev_data(dev, genpd->gov); gpd_data = genpd_alloc_dev_data(dev, gd);
if (IS_ERR(gpd_data)) if (IS_ERR(gpd_data))
return PTR_ERR(gpd_data); return PTR_ERR(gpd_data);
...@@ -1641,7 +1643,8 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, ...@@ -1641,7 +1643,8 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
dev_pm_domain_set(dev, &genpd->domain); dev_pm_domain_set(dev, &genpd->domain);
genpd->device_count++; genpd->device_count++;
genpd->max_off_time_changed = true; if (gd)
gd->max_off_time_changed = true;
list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
...@@ -1695,7 +1698,8 @@ static int genpd_remove_device(struct generic_pm_domain *genpd, ...@@ -1695,7 +1698,8 @@ static int genpd_remove_device(struct generic_pm_domain *genpd,
} }
genpd->device_count--; genpd->device_count--;
genpd->max_off_time_changed = true; if (genpd->gd)
genpd->gd->max_off_time_changed = true;
genpd_clear_cpumask(genpd, gpd_data->cpu); genpd_clear_cpumask(genpd, gpd_data->cpu);
dev_pm_domain_set(dev, NULL); dev_pm_domain_set(dev, NULL);
...@@ -1970,12 +1974,25 @@ static int genpd_set_default_power_state(struct generic_pm_domain *genpd) ...@@ -1970,12 +1974,25 @@ static int genpd_set_default_power_state(struct generic_pm_domain *genpd)
static int genpd_alloc_data(struct generic_pm_domain *genpd) static int genpd_alloc_data(struct generic_pm_domain *genpd)
{ {
struct genpd_governor_data *gd = NULL;
int ret; int ret;
if (genpd_is_cpu_domain(genpd) && if (genpd_is_cpu_domain(genpd) &&
!zalloc_cpumask_var(&genpd->cpus, GFP_KERNEL)) !zalloc_cpumask_var(&genpd->cpus, GFP_KERNEL))
return -ENOMEM; return -ENOMEM;
if (genpd->gov) {
gd = kzalloc(sizeof(*gd), GFP_KERNEL);
if (!gd) {
ret = -ENOMEM;
goto free;
}
gd->max_off_time_ns = -1;
gd->max_off_time_changed = true;
gd->next_wakeup = KTIME_MAX;
}
/* Use only one "off" state if there were no states declared */ /* Use only one "off" state if there were no states declared */
if (genpd->state_count == 0) { if (genpd->state_count == 0) {
ret = genpd_set_default_power_state(genpd); ret = genpd_set_default_power_state(genpd);
...@@ -1983,11 +2000,13 @@ static int genpd_alloc_data(struct generic_pm_domain *genpd) ...@@ -1983,11 +2000,13 @@ static int genpd_alloc_data(struct generic_pm_domain *genpd)
goto free; goto free;
} }
genpd->gd = gd;
return 0; return 0;
free: free:
if (genpd_is_cpu_domain(genpd)) if (genpd_is_cpu_domain(genpd))
free_cpumask_var(genpd->cpus); free_cpumask_var(genpd->cpus);
kfree(gd);
return ret; return ret;
} }
...@@ -1997,6 +2016,7 @@ static void genpd_free_data(struct generic_pm_domain *genpd) ...@@ -1997,6 +2016,7 @@ static void genpd_free_data(struct generic_pm_domain *genpd)
free_cpumask_var(genpd->cpus); free_cpumask_var(genpd->cpus);
if (genpd->free_states) if (genpd->free_states)
genpd->free_states(genpd->states, genpd->state_count); genpd->free_states(genpd->states, genpd->state_count);
kfree(genpd->gd);
} }
static void genpd_lock_init(struct generic_pm_domain *genpd) static void genpd_lock_init(struct generic_pm_domain *genpd)
...@@ -2036,9 +2056,6 @@ int pm_genpd_init(struct generic_pm_domain *genpd, ...@@ -2036,9 +2056,6 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
atomic_set(&genpd->sd_count, 0); atomic_set(&genpd->sd_count, 0);
genpd->status = is_off ? GENPD_STATE_OFF : GENPD_STATE_ON; genpd->status = is_off ? GENPD_STATE_OFF : GENPD_STATE_ON;
genpd->device_count = 0; genpd->device_count = 0;
genpd->max_off_time_ns = -1;
genpd->max_off_time_changed = true;
genpd->next_wakeup = KTIME_MAX;
genpd->provider = NULL; genpd->provider = NULL;
genpd->has_provider = false; genpd->has_provider = false;
genpd->accounting_time = ktime_get_mono_fast_ns(); genpd->accounting_time = ktime_get_mono_fast_ns();
......
...@@ -146,19 +146,21 @@ static void update_domain_next_wakeup(struct generic_pm_domain *genpd, ktime_t n ...@@ -146,19 +146,21 @@ static void update_domain_next_wakeup(struct generic_pm_domain *genpd, ktime_t n
} }
list_for_each_entry(link, &genpd->parent_links, parent_node) { list_for_each_entry(link, &genpd->parent_links, parent_node) {
next_wakeup = link->child->next_wakeup; struct genpd_governor_data *cgd = link->child->gd;
next_wakeup = cgd ? cgd->next_wakeup : KTIME_MAX;
if (next_wakeup != KTIME_MAX && !ktime_before(next_wakeup, now)) if (next_wakeup != KTIME_MAX && !ktime_before(next_wakeup, now))
if (ktime_before(next_wakeup, domain_wakeup)) if (ktime_before(next_wakeup, domain_wakeup))
domain_wakeup = next_wakeup; domain_wakeup = next_wakeup;
} }
genpd->next_wakeup = domain_wakeup; genpd->gd->next_wakeup = domain_wakeup;
} }
static bool next_wakeup_allows_state(struct generic_pm_domain *genpd, static bool next_wakeup_allows_state(struct generic_pm_domain *genpd,
unsigned int state, ktime_t now) unsigned int state, ktime_t now)
{ {
ktime_t domain_wakeup = genpd->next_wakeup; ktime_t domain_wakeup = genpd->gd->next_wakeup;
s64 idle_time_ns, min_sleep_ns; s64 idle_time_ns, min_sleep_ns;
min_sleep_ns = genpd->states[state].power_off_latency_ns + min_sleep_ns = genpd->states[state].power_off_latency_ns +
...@@ -188,8 +190,9 @@ static bool __default_power_down_ok(struct dev_pm_domain *pd, ...@@ -188,8 +190,9 @@ static bool __default_power_down_ok(struct dev_pm_domain *pd,
* All subdomains have been powered off already at this point. * All subdomains have been powered off already at this point.
*/ */
list_for_each_entry(link, &genpd->parent_links, parent_node) { list_for_each_entry(link, &genpd->parent_links, parent_node) {
struct generic_pm_domain *sd = link->child; struct genpd_governor_data *cgd = link->child->gd;
s64 sd_max_off_ns = sd->max_off_time_ns;
s64 sd_max_off_ns = cgd ? cgd->max_off_time_ns : -1;
if (sd_max_off_ns < 0) if (sd_max_off_ns < 0)
continue; continue;
...@@ -247,7 +250,7 @@ static bool __default_power_down_ok(struct dev_pm_domain *pd, ...@@ -247,7 +250,7 @@ static bool __default_power_down_ok(struct dev_pm_domain *pd,
* time and the time needed to turn the domain on is the maximum * time and the time needed to turn the domain on is the maximum
* theoretical time this domain can spend in the "off" state. * theoretical time this domain can spend in the "off" state.
*/ */
genpd->max_off_time_ns = min_off_time_ns - genpd->gd->max_off_time_ns = min_off_time_ns -
genpd->states[state].power_on_latency_ns; genpd->states[state].power_on_latency_ns;
return true; return true;
} }
...@@ -262,6 +265,7 @@ static bool __default_power_down_ok(struct dev_pm_domain *pd, ...@@ -262,6 +265,7 @@ static bool __default_power_down_ok(struct dev_pm_domain *pd,
static bool _default_power_down_ok(struct dev_pm_domain *pd, ktime_t now) static bool _default_power_down_ok(struct dev_pm_domain *pd, ktime_t now)
{ {
struct generic_pm_domain *genpd = pd_to_genpd(pd); struct generic_pm_domain *genpd = pd_to_genpd(pd);
struct genpd_governor_data *gd = genpd->gd;
int state_idx = genpd->state_count - 1; int state_idx = genpd->state_count - 1;
struct gpd_link *link; struct gpd_link *link;
...@@ -272,11 +276,11 @@ static bool _default_power_down_ok(struct dev_pm_domain *pd, ktime_t now) ...@@ -272,11 +276,11 @@ static bool _default_power_down_ok(struct dev_pm_domain *pd, ktime_t now)
* cannot be met. * cannot be met.
*/ */
update_domain_next_wakeup(genpd, now); update_domain_next_wakeup(genpd, now);
if ((genpd->flags & GENPD_FLAG_MIN_RESIDENCY) && (genpd->next_wakeup != KTIME_MAX)) { if ((genpd->flags & GENPD_FLAG_MIN_RESIDENCY) && (gd->next_wakeup != KTIME_MAX)) {
/* Let's find out the deepest domain idle state, the devices prefer */ /* Let's find out the deepest domain idle state, the devices prefer */
while (state_idx >= 0) { while (state_idx >= 0) {
if (next_wakeup_allows_state(genpd, state_idx, now)) { if (next_wakeup_allows_state(genpd, state_idx, now)) {
genpd->max_off_time_changed = true; gd->max_off_time_changed = true;
break; break;
} }
state_idx--; state_idx--;
...@@ -284,14 +288,14 @@ static bool _default_power_down_ok(struct dev_pm_domain *pd, ktime_t now) ...@@ -284,14 +288,14 @@ static bool _default_power_down_ok(struct dev_pm_domain *pd, ktime_t now)
if (state_idx < 0) { if (state_idx < 0) {
state_idx = 0; state_idx = 0;
genpd->cached_power_down_ok = false; gd->cached_power_down_ok = false;
goto done; goto done;
} }
} }
if (!genpd->max_off_time_changed) { if (!gd->max_off_time_changed) {
genpd->state_idx = genpd->cached_power_down_state_idx; genpd->state_idx = gd->cached_power_down_state_idx;
return genpd->cached_power_down_ok; return gd->cached_power_down_ok;
} }
/* /*
...@@ -300,12 +304,16 @@ static bool _default_power_down_ok(struct dev_pm_domain *pd, ktime_t now) ...@@ -300,12 +304,16 @@ static bool _default_power_down_ok(struct dev_pm_domain *pd, ktime_t now)
* going to be called for any parent until this instance * going to be called for any parent until this instance
* returns. * returns.
*/ */
list_for_each_entry(link, &genpd->child_links, child_node) list_for_each_entry(link, &genpd->child_links, child_node) {
link->parent->max_off_time_changed = true; struct genpd_governor_data *pgd = link->parent->gd;
if (pgd)
pgd->max_off_time_changed = true;
}
genpd->max_off_time_ns = -1; gd->max_off_time_ns = -1;
genpd->max_off_time_changed = false; gd->max_off_time_changed = false;
genpd->cached_power_down_ok = true; gd->cached_power_down_ok = true;
/* /*
* Find a state to power down to, starting from the state * Find a state to power down to, starting from the state
...@@ -313,7 +321,7 @@ static bool _default_power_down_ok(struct dev_pm_domain *pd, ktime_t now) ...@@ -313,7 +321,7 @@ static bool _default_power_down_ok(struct dev_pm_domain *pd, ktime_t now)
*/ */
while (!__default_power_down_ok(pd, state_idx)) { while (!__default_power_down_ok(pd, state_idx)) {
if (state_idx == 0) { if (state_idx == 0) {
genpd->cached_power_down_ok = false; gd->cached_power_down_ok = false;
break; break;
} }
state_idx--; state_idx--;
...@@ -321,8 +329,8 @@ static bool _default_power_down_ok(struct dev_pm_domain *pd, ktime_t now) ...@@ -321,8 +329,8 @@ static bool _default_power_down_ok(struct dev_pm_domain *pd, ktime_t now)
done: done:
genpd->state_idx = state_idx; genpd->state_idx = state_idx;
genpd->cached_power_down_state_idx = genpd->state_idx; gd->cached_power_down_state_idx = genpd->state_idx;
return genpd->cached_power_down_ok; return gd->cached_power_down_ok;
} }
static bool default_power_down_ok(struct dev_pm_domain *pd) static bool default_power_down_ok(struct dev_pm_domain *pd)
......
...@@ -91,6 +91,14 @@ struct gpd_dev_ops { ...@@ -91,6 +91,14 @@ struct gpd_dev_ops {
int (*stop)(struct device *dev); int (*stop)(struct device *dev);
}; };
struct genpd_governor_data {
s64 max_off_time_ns;
bool max_off_time_changed;
ktime_t next_wakeup;
bool cached_power_down_ok;
bool cached_power_down_state_idx;
};
struct genpd_power_state { struct genpd_power_state {
s64 power_off_latency_ns; s64 power_off_latency_ns;
s64 power_on_latency_ns; s64 power_on_latency_ns;
...@@ -114,6 +122,7 @@ struct generic_pm_domain { ...@@ -114,6 +122,7 @@ struct generic_pm_domain {
struct list_head child_links; /* Links with PM domain as a child */ struct list_head child_links; /* Links with PM domain as a child */
struct list_head dev_list; /* List of devices */ struct list_head dev_list; /* List of devices */
struct dev_power_governor *gov; struct dev_power_governor *gov;
struct genpd_governor_data *gd; /* Data used by a genpd governor. */
struct work_struct power_off_work; struct work_struct power_off_work;
struct fwnode_handle *provider; /* Identity of the domain provider */ struct fwnode_handle *provider; /* Identity of the domain provider */
bool has_provider; bool has_provider;
...@@ -134,11 +143,6 @@ struct generic_pm_domain { ...@@ -134,11 +143,6 @@ struct generic_pm_domain {
int (*set_performance_state)(struct generic_pm_domain *genpd, int (*set_performance_state)(struct generic_pm_domain *genpd,
unsigned int state); unsigned int state);
struct gpd_dev_ops dev_ops; struct gpd_dev_ops dev_ops;
s64 max_off_time_ns; /* Maximum allowed "suspended" time. */
ktime_t next_wakeup; /* Maintained by the domain governor */
bool max_off_time_changed;
bool cached_power_down_ok;
bool cached_power_down_state_idx;
int (*attach_dev)(struct generic_pm_domain *domain, int (*attach_dev)(struct generic_pm_domain *domain,
struct device *dev); struct device *dev);
void (*detach_dev)(struct generic_pm_domain *domain, void (*detach_dev)(struct generic_pm_domain *domain,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment