| From f334d73383ca5539e6401bb3b696b036533471bd Mon Sep 17 00:00:00 2001 |
| From: "Rafael J. Wysocki" <rjw@sisk.pl> |
| Date: Mon, 8 Aug 2011 23:43:22 +0200 |
| Subject: 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> |
| (cherry picked from commit 9e08cf429697090d0fac57d493dc7b6de17a5eee) |
| |
| Signed-off-by: Simon Horman <horms@verge.net.au> |
| --- |
| drivers/base/power/domain.c | 33 ++++++++++++++++++++------------- |
| 1 file 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 */ |
| -- |
| 1.7.10.1.362.g242cab3 |
| |