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

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: default avatarUlf Hansson <ulf.hansson@linaro.org>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 0106ef51
...@@ -1340,8 +1340,8 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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;
} }
......
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