PM: domains: Allocate gpd_timing_data dynamically based on governor

If a genpd doesn't have an associated governor assigned, there's really no
point to allocate the per device gpd_timing_data, as the data isn't being
used by a governor anyway.

To avoid wasting memory, let's therefore convert the corresponding td
variable in the struct generic_pm_domain_data into a pointer and manage the
allocation of its data dynamically.

Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
Ulf Hansson
2022-05-11 16:56:56 +02:00
committed by Rafael J. Wysocki
parent bcc19f69f7
commit 66d29d802e
3 changed files with 40 additions and 19 deletions

View File

@@ -773,13 +773,16 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
for (;;) { for (;;) {
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd;
struct pm_domain_data *pdd; struct pm_domain_data *pdd;
struct gpd_timing_data *td;
spin_lock_irq(&dev->power.lock); spin_lock_irq(&dev->power.lock);
pdd = dev->power.subsys_data ? pdd = dev->power.subsys_data ?
dev->power.subsys_data->domain_data : NULL; dev->power.subsys_data->domain_data : NULL;
if (pdd) { if (pdd) {
to_gpd_data(pdd)->td.constraint_changed = true; td = to_gpd_data(pdd)->td;
if (td)
td->constraint_changed = true;
genpd = dev_to_genpd(dev); genpd = dev_to_genpd(dev);
} else { } else {
genpd = ERR_PTR(-ENODATA); genpd = ERR_PTR(-ENODATA);
@@ -875,7 +878,7 @@ static int genpd_runtime_suspend(struct device *dev)
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd;
bool (*suspend_ok)(struct device *__dev); bool (*suspend_ok)(struct device *__dev);
struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev); struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev);
struct gpd_timing_data *td = &gpd_data->td; struct gpd_timing_data *td = gpd_data->td;
bool runtime_pm = pm_runtime_enabled(dev); bool runtime_pm = pm_runtime_enabled(dev);
ktime_t time_start; ktime_t time_start;
s64 elapsed_ns; s64 elapsed_ns;
@@ -915,7 +918,7 @@ static int genpd_runtime_suspend(struct device *dev)
/* Update suspend latency value if the measured time exceeds it. */ /* Update suspend latency value if the measured time exceeds it. */
if (runtime_pm) { if (runtime_pm) {
elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
if (elapsed_ns > td->suspend_latency_ns) { if (td && (elapsed_ns > td->suspend_latency_ns)) {
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);
@@ -951,7 +954,7 @@ static int genpd_runtime_resume(struct device *dev)
{ {
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd;
struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev); struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev);
struct gpd_timing_data *td = &gpd_data->td; struct gpd_timing_data *td = gpd_data->td;
bool runtime_pm = pm_runtime_enabled(dev); bool runtime_pm = pm_runtime_enabled(dev);
ktime_t time_start; ktime_t time_start;
s64 elapsed_ns; s64 elapsed_ns;
@@ -999,7 +1002,7 @@ static int genpd_runtime_resume(struct device *dev)
/* Update resume latency value if the measured time exceeds it. */ /* Update resume latency value if the measured time exceeds it. */
if (timed && runtime_pm) { if (timed && runtime_pm) {
elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
if (elapsed_ns > td->resume_latency_ns) { if (td && (elapsed_ns > td->resume_latency_ns)) {
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);
@@ -1496,9 +1499,11 @@ EXPORT_SYMBOL_GPL(dev_pm_genpd_resume);
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */
static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev) static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev,
bool has_governor)
{ {
struct generic_pm_domain_data *gpd_data; struct generic_pm_domain_data *gpd_data;
struct gpd_timing_data *td;
int ret; int ret;
ret = dev_pm_get_subsys_data(dev); ret = dev_pm_get_subsys_data(dev);
@@ -1512,26 +1517,38 @@ static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev)
} }
gpd_data->base.dev = dev; gpd_data->base.dev = dev;
gpd_data->td.constraint_changed = true;
gpd_data->td.effective_constraint_ns = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS;
gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
gpd_data->next_wakeup = KTIME_MAX; gpd_data->next_wakeup = KTIME_MAX;
spin_lock_irq(&dev->power.lock); /* Allocate data used by a governor. */
if (has_governor) {
td = kzalloc(sizeof(*td), GFP_KERNEL);
if (!td) {
ret = -ENOMEM;
goto err_free;
}
if (dev->power.subsys_data->domain_data) { td->constraint_changed = true;
ret = -EINVAL; td->effective_constraint_ns = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS;
goto err_free; gpd_data->td = td;
} }
dev->power.subsys_data->domain_data = &gpd_data->base; spin_lock_irq(&dev->power.lock);
if (dev->power.subsys_data->domain_data)
ret = -EINVAL;
else
dev->power.subsys_data->domain_data = &gpd_data->base;
spin_unlock_irq(&dev->power.lock); spin_unlock_irq(&dev->power.lock);
if (ret)
goto err_free;
return gpd_data; return gpd_data;
err_free: err_free:
spin_unlock_irq(&dev->power.lock); kfree(gpd_data->td);
kfree(gpd_data); kfree(gpd_data);
err_put: err_put:
dev_pm_put_subsys_data(dev); dev_pm_put_subsys_data(dev);
@@ -1547,6 +1564,7 @@ static void genpd_free_dev_data(struct device *dev,
spin_unlock_irq(&dev->power.lock); spin_unlock_irq(&dev->power.lock);
kfree(gpd_data->td);
kfree(gpd_data); kfree(gpd_data);
dev_pm_put_subsys_data(dev); dev_pm_put_subsys_data(dev);
} }
@@ -1611,7 +1629,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); gpd_data = genpd_alloc_dev_data(dev, genpd->gov);
if (IS_ERR(gpd_data)) if (IS_ERR(gpd_data))
return PTR_ERR(gpd_data); return PTR_ERR(gpd_data);

View File

@@ -18,6 +18,8 @@ static int dev_update_qos_constraint(struct device *dev, void *data)
s64 constraint_ns; s64 constraint_ns;
if (dev->power.subsys_data && dev->power.subsys_data->domain_data) { if (dev->power.subsys_data && dev->power.subsys_data->domain_data) {
struct gpd_timing_data *td = dev_gpd_data(dev)->td;
/* /*
* Only take suspend-time QoS constraints of devices into * Only take suspend-time QoS constraints of devices into
* account, because constraints updated after the device has * account, because constraints updated after the device has
@@ -25,7 +27,8 @@ static int dev_update_qos_constraint(struct device *dev, void *data)
* anyway. In order for them to take effect, the device has to * anyway. In order for them to take effect, the device has to
* be resumed and suspended again. * be resumed and suspended again.
*/ */
constraint_ns = dev_gpd_data(dev)->td.effective_constraint_ns; constraint_ns = td ? td->effective_constraint_ns :
PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS;
} else { } else {
/* /*
* The child is not in a domain and there's no info on its * The child is not in a domain and there's no info on its
@@ -49,7 +52,7 @@ static int dev_update_qos_constraint(struct device *dev, void *data)
*/ */
static bool default_suspend_ok(struct device *dev) static bool default_suspend_ok(struct device *dev)
{ {
struct gpd_timing_data *td = &dev_gpd_data(dev)->td; struct gpd_timing_data *td = dev_gpd_data(dev)->td;
unsigned long flags; unsigned long flags;
s64 constraint_ns; s64 constraint_ns;
@@ -215,7 +218,7 @@ static bool __default_power_down_ok(struct dev_pm_domain *pd,
* domain to turn off and on (that's how much time it will * domain to turn off and on (that's how much time it will
* have to wait worst case). * have to wait worst case).
*/ */
td = &to_gpd_data(pdd)->td; td = to_gpd_data(pdd)->td;
constraint_ns = td->effective_constraint_ns; constraint_ns = td->effective_constraint_ns;
/* /*
* Zero means "no suspend at all" and this runs only when all * Zero means "no suspend at all" and this runs only when all

View File

@@ -193,7 +193,7 @@ struct pm_domain_data {
struct generic_pm_domain_data { struct generic_pm_domain_data {
struct pm_domain_data base; struct pm_domain_data base;
struct gpd_timing_data td; struct gpd_timing_data *td;
struct notifier_block nb; struct notifier_block nb;
struct notifier_block *power_nb; struct notifier_block *power_nb;
int cpu; int cpu;