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

PM / Domains: Eliminate the mutex for the generic_pm_domain_data

While adding devices to their PM domains, dev_pm_qos_add_notifier() was
invoked while allocating the generic_pm_domain_data for the device.

Since the generic_pm_domain_data's device pointer will be assigned
after allocation, the ->genpd_dev_pm_qos_notifier() callback could be
called prior having a valid pointer to the device. Similar scenario
existed while removing a device from a genpd.

To cope with these scenarios a mutex was used to protect the pointer to
the device.

By re-order the sequence for when dev_pm_qos_add|remove_notifier() are
invoked, we make sure the ->genpd_dev_pm_qos_notifier() callback are
always called with a valid device pointer available.

In this way, we eliminate the need for protecting the pointer and thus
we can remove the mutex from the struct generic_pm_domain_data.
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent e07b45cb
...@@ -344,14 +344,7 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, ...@@ -344,14 +344,7 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
struct device *dev; struct device *dev;
gpd_data = container_of(nb, struct generic_pm_domain_data, nb); gpd_data = container_of(nb, struct generic_pm_domain_data, nb);
mutex_lock(&gpd_data->lock);
dev = gpd_data->base.dev; dev = gpd_data->base.dev;
if (!dev) {
mutex_unlock(&gpd_data->lock);
return NOTIFY_DONE;
}
mutex_unlock(&gpd_data->lock);
for (;;) { for (;;) {
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd;
...@@ -1392,16 +1385,12 @@ static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev) ...@@ -1392,16 +1385,12 @@ static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev)
if (!gpd_data) if (!gpd_data)
return NULL; return NULL;
mutex_init(&gpd_data->lock);
gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
dev_pm_qos_add_notifier(dev, &gpd_data->nb);
return gpd_data; return gpd_data;
} }
static void genpd_free_dev_data(struct device *dev, static void genpd_free_dev_data(struct device *dev,
struct generic_pm_domain_data *gpd_data) struct generic_pm_domain_data *gpd_data)
{ {
dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
kfree(gpd_data); kfree(gpd_data);
} }
...@@ -1414,7 +1403,7 @@ static void genpd_free_dev_data(struct device *dev, ...@@ -1414,7 +1403,7 @@ static void genpd_free_dev_data(struct device *dev,
int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
struct gpd_timing_data *td) struct gpd_timing_data *td)
{ {
struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL; struct generic_pm_domain_data *gpd_data;
int ret = 0; int ret = 0;
dev_dbg(dev, "%s()\n", __func__); dev_dbg(dev, "%s()\n", __func__);
...@@ -1422,8 +1411,8 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, ...@@ -1422,8 +1411,8 @@ int __pm_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_new = genpd_alloc_dev_data(dev); gpd_data = genpd_alloc_dev_data(dev);
if (!gpd_data_new) if (!gpd_data)
return -ENOMEM; return -ENOMEM;
genpd_acquire_lock(genpd); genpd_acquire_lock(genpd);
...@@ -1445,7 +1434,6 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, ...@@ -1445,7 +1434,6 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
goto out; goto out;
} }
gpd_data = gpd_data_new;
dev->power.subsys_data->domain_data = &gpd_data->base; dev->power.subsys_data->domain_data = &gpd_data->base;
if (td) if (td)
...@@ -1461,19 +1449,20 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, ...@@ -1461,19 +1449,20 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
genpd->device_count++; genpd->device_count++;
genpd->max_off_time_changed = true; genpd->max_off_time_changed = true;
mutex_lock(&gpd_data->lock);
gpd_data->base.dev = dev; gpd_data->base.dev = dev;
list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
gpd_data->need_restore = -1; gpd_data->need_restore = -1;
gpd_data->td.constraint_changed = true; gpd_data->td.constraint_changed = true;
gpd_data->td.effective_constraint_ns = -1; gpd_data->td.effective_constraint_ns = -1;
mutex_unlock(&gpd_data->lock); gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
out: out:
genpd_release_lock(genpd); genpd_release_lock(genpd);
if (gpd_data != gpd_data_new) if (ret)
genpd_free_dev_data(dev, gpd_data_new); genpd_free_dev_data(dev, gpd_data);
else
dev_pm_qos_add_notifier(dev, &gpd_data->nb);
return ret; return ret;
} }
...@@ -1509,6 +1498,11 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, ...@@ -1509,6 +1498,11 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
|| pd_to_genpd(dev->pm_domain) != genpd) || pd_to_genpd(dev->pm_domain) != genpd)
return -EINVAL; return -EINVAL;
/* The above validation also means we have existing domain_data. */
pdd = dev->power.subsys_data->domain_data;
gpd_data = to_gpd_data(pdd);
dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
genpd_acquire_lock(genpd); genpd_acquire_lock(genpd);
if (genpd->prepared_count > 0) { if (genpd->prepared_count > 0) {
...@@ -1525,16 +1519,12 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, ...@@ -1525,16 +1519,12 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
spin_lock_irq(&dev->power.lock); spin_lock_irq(&dev->power.lock);
dev->pm_domain = NULL; dev->pm_domain = NULL;
pdd = dev->power.subsys_data->domain_data;
list_del_init(&pdd->list_node); list_del_init(&pdd->list_node);
gpd_data = to_gpd_data(pdd);
dev->power.subsys_data->domain_data = NULL; dev->power.subsys_data->domain_data = NULL;
spin_unlock_irq(&dev->power.lock); spin_unlock_irq(&dev->power.lock);
mutex_lock(&gpd_data->lock);
pdd->dev = NULL; pdd->dev = NULL;
mutex_unlock(&gpd_data->lock);
genpd_release_lock(genpd); genpd_release_lock(genpd);
...@@ -1545,6 +1535,7 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, ...@@ -1545,6 +1535,7 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
out: out:
genpd_release_lock(genpd); genpd_release_lock(genpd);
dev_pm_qos_add_notifier(dev, &gpd_data->nb);
return ret; return ret;
} }
......
...@@ -113,7 +113,6 @@ struct generic_pm_domain_data { ...@@ -113,7 +113,6 @@ 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 mutex lock;
int need_restore; int need_restore;
}; };
......
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