From: Rafael J. Wysocki Date: Mon, 8 Aug 2011 21:43:22 +0000 (+0200) Subject: PM / Domains: Make pm_genpd_poweron() always survive parent removal X-Git-Url: http://drtracing.org/?a=commitdiff_plain;h=9e08cf429697090d0fac57d493dc7b6de17a5eee;p=deliverable%2Flinux.git 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 --- diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index ef25b6f99f99..dc423a99c67c 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 */