| From 66926d50341a303bfaa8135cda00ec2fe0fa68a9 Mon Sep 17 00:00:00 2001 |
| From: "Rafael J. Wysocki" <rjw@sisk.pl> |
| Date: Mon, 8 Aug 2011 23:43:14 +0200 |
| Subject: PM / Domains: Do not take parent locks to modify subdomain counters |
| |
| After the subdomain counter in struct generic_pm_domain has been |
| changed into an atomic_t field, it is possible to modify |
| pm_genpd_poweron() and pm_genpd_poweroff() so that they don't take |
| the parents locks. This requires pm_genpd_poweron() to increment |
| the parent's subdomain counter before calling itself recursively |
| for the parent and to decrement it if an error is to be returned. |
| |
| Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> |
| (cherry picked from commit 3c07cbc488bfd1ad1abf64d09cc692339b5f8a83) |
| |
| Signed-off-by: Simon Horman <horms@verge.net.au> |
| --- |
| drivers/base/power/domain.c | 70 +++++++++++++++++++------------------------ |
| 1 file changed, 31 insertions(+), 39 deletions(-) |
| |
| diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c |
| index 20e2b52..ef25b6f 100644 |
| --- a/drivers/base/power/domain.c |
| +++ b/drivers/base/power/domain.c |
| @@ -93,12 +93,7 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd) |
| int ret = 0; |
| |
| start: |
| - if (parent) { |
| - genpd_acquire_lock(parent); |
| - mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); |
| - } else { |
| - mutex_lock(&genpd->lock); |
| - } |
| + mutex_lock(&genpd->lock); |
| |
| if (genpd->status == GPD_STATE_ACTIVE |
| || (genpd->prepared_count > 0 && genpd->suspend_power_off)) |
| @@ -109,31 +104,33 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd) |
| goto out; |
| } |
| |
| - if (parent && parent->status != GPD_STATE_ACTIVE) { |
| + if (parent) { |
| + genpd_sd_counter_inc(parent); |
| + |
| mutex_unlock(&genpd->lock); |
| - genpd_release_lock(parent); |
| |
| ret = pm_genpd_poweron(parent); |
| - if (ret) |
| + if (ret) { |
| + genpd_sd_counter_dec(parent); |
| return ret; |
| + } |
| |
| + parent = NULL; |
| goto start; |
| } |
| |
| - if (genpd->power_on) { |
| + if (genpd->power_on) |
| ret = genpd->power_on(genpd); |
| - if (ret) |
| - goto out; |
| - } |
| |
| - genpd_set_active(genpd); |
| - if (parent) |
| - genpd_sd_counter_inc(parent); |
| + if (ret) { |
| + if (genpd->parent) |
| + genpd_sd_counter_dec(genpd->parent); |
| + } else { |
| + genpd_set_active(genpd); |
| + } |
| |
| out: |
| mutex_unlock(&genpd->lock); |
| - if (parent) |
| - genpd_release_lock(parent); |
| |
| return ret; |
| } |
| @@ -293,7 +290,8 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) |
| genpd->poweroff_task = current; |
| |
| list_for_each_entry_reverse(dle, &genpd->dev_list, node) { |
| - ret = __pm_genpd_save_device(dle, genpd); |
| + ret = atomic_read(&genpd->sd_count) == 0 ? |
| + __pm_genpd_save_device(dle, genpd) : -EBUSY; |
| if (ret) { |
| genpd_set_active(genpd); |
| goto out; |
| @@ -308,38 +306,32 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) |
| } |
| } |
| |
| - parent = genpd->parent; |
| - if (parent) { |
| - mutex_unlock(&genpd->lock); |
| - |
| - genpd_acquire_lock(parent); |
| - mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); |
| - |
| - if (genpd_abort_poweroff(genpd)) { |
| - genpd_release_lock(parent); |
| + if (genpd->power_off) { |
| + if (atomic_read(&genpd->sd_count) > 0) { |
| + ret = -EBUSY; |
| goto out; |
| } |
| - } |
| |
| - if (genpd->power_off) { |
| + /* |
| + * If sd_count > 0 at this point, one of the children hasn't |
| + * managed to call pm_genpd_poweron() for the parent yet after |
| + * incrementing it. In that case pm_genpd_poweron() will wait |
| + * for us to drop the lock, so we can call .power_off() and let |
| + * the pm_genpd_poweron() restore power for us (this shouldn't |
| + * happen very often). |
| + */ |
| ret = genpd->power_off(genpd); |
| if (ret == -EBUSY) { |
| genpd_set_active(genpd); |
| - if (parent) |
| - genpd_release_lock(parent); |
| - |
| goto out; |
| } |
| } |
| |
| genpd->status = GPD_STATE_POWER_OFF; |
| |
| - if (parent) { |
| - if (genpd_sd_counter_dec(parent)) |
| - genpd_queue_power_off_work(parent); |
| - |
| - genpd_release_lock(parent); |
| - } |
| + parent = genpd->parent; |
| + if (parent && genpd_sd_counter_dec(parent)) |
| + genpd_queue_power_off_work(parent); |
| |
| out: |
| genpd->poweroff_task = NULL; |
| -- |
| 1.7.10.1.362.g242cab3 |
| |