diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2011-08-08 23:43:14 +0200 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2011-08-25 15:33:43 +0200 |
commit | 3c07cbc488bfd1ad1abf64d09cc692339b5f8a83 (patch) | |
tree | d53a2a9a3635092f289b66dabcbac0fbcefe8d50 /drivers/base | |
parent | PM / Domains: Implement subdomain counters as atomic fields (diff) | |
download | linux-3c07cbc488bfd1ad1abf64d09cc692339b5f8a83.tar.xz linux-3c07cbc488bfd1ad1abf64d09cc692339b5f8a83.zip |
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>
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/power/domain.c | 70 |
1 files changed, 31 insertions, 39 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 20e2b52d9c9c..ef25b6f99f99 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; |