PM / Domains: Fix potential deadlock while adding/removing subdomains
We must preserve the same order of how we acquire and release the lock for genpd, as otherwise we may encounter deadlocks. The power on phase of a genpd starts by acquiring its lock. Then it walks the hierarchy of its parent domains to be able to power on these first, as per design of genpd. From a locking perspective this means the locks of the parents becomes acquired after the lock of the subdomain. Let's fix pm_genpd_add|remove_subdomain() to maintain the same order of acquiring/releasing the genpd lock as being applied in the power on/off sequence. 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:
parent
0106ef5146
commit
cdb300a041
@ -1340,8 +1340,8 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
|
|||||||
if (!link)
|
if (!link)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
mutex_lock(&genpd->lock);
|
mutex_lock(&subdomain->lock);
|
||||||
mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
|
mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
|
||||||
|
|
||||||
if (genpd->status == GPD_STATE_POWER_OFF
|
if (genpd->status == GPD_STATE_POWER_OFF
|
||||||
&& subdomain->status != GPD_STATE_POWER_OFF) {
|
&& subdomain->status != GPD_STATE_POWER_OFF) {
|
||||||
@ -1364,8 +1364,8 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
|
|||||||
genpd_sd_counter_inc(genpd);
|
genpd_sd_counter_inc(genpd);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&subdomain->lock);
|
|
||||||
mutex_unlock(&genpd->lock);
|
mutex_unlock(&genpd->lock);
|
||||||
|
mutex_unlock(&subdomain->lock);
|
||||||
if (ret)
|
if (ret)
|
||||||
kfree(link);
|
kfree(link);
|
||||||
return ret;
|
return ret;
|
||||||
@ -1386,7 +1386,8 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
|
|||||||
if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
|
if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
mutex_lock(&genpd->lock);
|
mutex_lock(&subdomain->lock);
|
||||||
|
mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
|
||||||
|
|
||||||
if (!list_empty(&subdomain->slave_links) || subdomain->device_count) {
|
if (!list_empty(&subdomain->slave_links) || subdomain->device_count) {
|
||||||
pr_warn("%s: unable to remove subdomain %s\n", genpd->name,
|
pr_warn("%s: unable to remove subdomain %s\n", genpd->name,
|
||||||
@ -1399,22 +1400,19 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
|
|||||||
if (link->slave != subdomain)
|
if (link->slave != subdomain)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
|
|
||||||
|
|
||||||
list_del(&link->master_node);
|
list_del(&link->master_node);
|
||||||
list_del(&link->slave_node);
|
list_del(&link->slave_node);
|
||||||
kfree(link);
|
kfree(link);
|
||||||
if (subdomain->status != GPD_STATE_POWER_OFF)
|
if (subdomain->status != GPD_STATE_POWER_OFF)
|
||||||
genpd_sd_counter_dec(genpd);
|
genpd_sd_counter_dec(genpd);
|
||||||
|
|
||||||
mutex_unlock(&subdomain->lock);
|
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&genpd->lock);
|
mutex_unlock(&genpd->lock);
|
||||||
|
mutex_unlock(&subdomain->lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user