diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2011-08-08 23:43:22 +0200 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2011-08-25 15:33:44 +0200 |
commit | 9e08cf429697090d0fac57d493dc7b6de17a5eee (patch) | |
tree | 70b18a96a5510bdec9bd35307bbf9d3b3e57981d /drivers/base/power | |
parent | 3c07cbc488bfd1ad1abf64d09cc692339b5f8a83 (diff) | |
download | op-kernel-dev-9e08cf429697090d0fac57d493dc7b6de17a5eee.zip op-kernel-dev-9e08cf429697090d0fac57d493dc7b6de17a5eee.tar.gz |
PM / Domains: Make pm_genpd_poweron() always survive parent removal
If pm_genpd_remove_subdomain() is called to remove a PM domain's
subdomain and pm_genpd_poweron() is called for that subdomain at
the same time, and the pm_genpd_poweron() called by it recursively
for the parent returns an error, the first pm_genpd_poweron()'s
error code path will attempt to decrement the subdomain counter of
a PM domain that it's not a subdomain of any more.
Rearrange the code in pm_genpd_poweron() to prevent this from
happening.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Diffstat (limited to 'drivers/base/power')
-rw-r--r-- | drivers/base/power/domain.c | 33 |
1 files changed, 20 insertions, 13 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index ef25b6f..dc423a9 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -89,12 +89,14 @@ static void genpd_set_active(struct generic_pm_domain *genpd) */ int pm_genpd_poweron(struct generic_pm_domain *genpd) { - struct generic_pm_domain *parent = genpd->parent; + struct generic_pm_domain *parent; int ret = 0; - start: mutex_lock(&genpd->lock); + parent = genpd->parent; + + start: if (genpd->status == GPD_STATE_ACTIVE || (genpd->prepared_count > 0 && genpd->suspend_power_off)) goto out; @@ -110,29 +112,34 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd) mutex_unlock(&genpd->lock); ret = pm_genpd_poweron(parent); - if (ret) { - genpd_sd_counter_dec(parent); - return ret; - } + + mutex_lock(&genpd->lock); + + if (ret) + goto err; parent = NULL; goto start; } - if (genpd->power_on) + if (genpd->power_on) { ret = genpd->power_on(genpd); - - if (ret) { - if (genpd->parent) - genpd_sd_counter_dec(genpd->parent); - } else { - genpd_set_active(genpd); + if (ret) + goto err; } + genpd_set_active(genpd); + out: mutex_unlock(&genpd->lock); return ret; + + err: + if (genpd->parent) + genpd_sd_counter_dec(genpd->parent); + + goto out; } #endif /* CONFIG_PM */ |