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:
committed by
Rafael J. Wysocki
parent
bcc19f69f7
commit
66d29d802e
@@ -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);
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
@@ -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;
|
||||||
|
Reference in New Issue
Block a user