diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2011-08-08 17:43:14 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2011-08-25 09:33:43 -0400 |
commit | 3c07cbc488bfd1ad1abf64d09cc692339b5f8a83 (patch) | |
tree | d53a2a9a3635092f289b66dabcbac0fbcefe8d50 /drivers/base/power | |
parent | c4bb3160c8823d3a1e581d7e05fb8b343097e7c8 (diff) |
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/power')
-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) | |||
93 | int ret = 0; | 93 | int ret = 0; |
94 | 94 | ||
95 | start: | 95 | start: |
96 | if (parent) { | 96 | mutex_lock(&genpd->lock); |
97 | genpd_acquire_lock(parent); | ||
98 | mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); | ||
99 | } else { | ||
100 | mutex_lock(&genpd->lock); | ||
101 | } | ||
102 | 97 | ||
103 | if (genpd->status == GPD_STATE_ACTIVE | 98 | if (genpd->status == GPD_STATE_ACTIVE |
104 | || (genpd->prepared_count > 0 && genpd->suspend_power_off)) | 99 | || (genpd->prepared_count > 0 && genpd->suspend_power_off)) |
@@ -109,31 +104,33 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd) | |||
109 | goto out; | 104 | goto out; |
110 | } | 105 | } |
111 | 106 | ||
112 | if (parent && parent->status != GPD_STATE_ACTIVE) { | 107 | if (parent) { |
108 | genpd_sd_counter_inc(parent); | ||
109 | |||
113 | mutex_unlock(&genpd->lock); | 110 | mutex_unlock(&genpd->lock); |
114 | genpd_release_lock(parent); | ||
115 | 111 | ||
116 | ret = pm_genpd_poweron(parent); | 112 | ret = pm_genpd_poweron(parent); |
117 | if (ret) | 113 | if (ret) { |
114 | genpd_sd_counter_dec(parent); | ||
118 | return ret; | 115 | return ret; |
116 | } | ||
119 | 117 | ||
118 | parent = NULL; | ||
120 | goto start; | 119 | goto start; |
121 | } | 120 | } |
122 | 121 | ||
123 | if (genpd->power_on) { | 122 | if (genpd->power_on) |
124 | ret = genpd->power_on(genpd); | 123 | ret = genpd->power_on(genpd); |
125 | if (ret) | ||
126 | goto out; | ||
127 | } | ||
128 | 124 | ||
129 | genpd_set_active(genpd); | 125 | if (ret) { |
130 | if (parent) | 126 | if (genpd->parent) |
131 | genpd_sd_counter_inc(parent); | 127 | genpd_sd_counter_dec(genpd->parent); |
128 | } else { | ||
129 | genpd_set_active(genpd); | ||
130 | } | ||
132 | 131 | ||
133 | out: | 132 | out: |
134 | mutex_unlock(&genpd->lock); | 133 | mutex_unlock(&genpd->lock); |
135 | if (parent) | ||
136 | genpd_release_lock(parent); | ||
137 | 134 | ||
138 | return ret; | 135 | return ret; |
139 | } | 136 | } |
@@ -293,7 +290,8 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) | |||
293 | genpd->poweroff_task = current; | 290 | genpd->poweroff_task = current; |
294 | 291 | ||
295 | list_for_each_entry_reverse(dle, &genpd->dev_list, node) { | 292 | list_for_each_entry_reverse(dle, &genpd->dev_list, node) { |
296 | ret = __pm_genpd_save_device(dle, genpd); | 293 | ret = atomic_read(&genpd->sd_count) == 0 ? |
294 | __pm_genpd_save_device(dle, genpd) : -EBUSY; | ||
297 | if (ret) { | 295 | if (ret) { |
298 | genpd_set_active(genpd); | 296 | genpd_set_active(genpd); |
299 | goto out; | 297 | goto out; |
@@ -308,38 +306,32 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) | |||
308 | } | 306 | } |
309 | } | 307 | } |
310 | 308 | ||
311 | parent = genpd->parent; | 309 | if (genpd->power_off) { |
312 | if (parent) { | 310 | if (atomic_read(&genpd->sd_count) > 0) { |
313 | mutex_unlock(&genpd->lock); | 311 | ret = -EBUSY; |
314 | |||
315 | genpd_acquire_lock(parent); | ||
316 | mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); | ||
317 | |||
318 | if (genpd_abort_poweroff(genpd)) { | ||
319 | genpd_release_lock(parent); | ||
320 | goto out; | 312 | goto out; |
321 | } | 313 | } |
322 | } | ||
323 | 314 | ||
324 | if (genpd->power_off) { | 315 | /* |
316 | * If sd_count > 0 at this point, one of the children hasn't | ||
317 | * managed to call pm_genpd_poweron() for the parent yet after | ||
318 | * incrementing it. In that case pm_genpd_poweron() will wait | ||
319 | * for us to drop the lock, so we can call .power_off() and let | ||
320 | * the pm_genpd_poweron() restore power for us (this shouldn't | ||
321 | * happen very often). | ||
322 | */ | ||
325 | ret = genpd->power_off(genpd); | 323 | ret = genpd->power_off(genpd); |
326 | if (ret == -EBUSY) { | 324 | if (ret == -EBUSY) { |
327 | genpd_set_active(genpd); | 325 | genpd_set_active(genpd); |
328 | if (parent) | ||
329 | genpd_release_lock(parent); | ||
330 | |||
331 | goto out; | 326 | goto out; |
332 | } | 327 | } |
333 | } | 328 | } |
334 | 329 | ||
335 | genpd->status = GPD_STATE_POWER_OFF; | 330 | genpd->status = GPD_STATE_POWER_OFF; |
336 | 331 | ||
337 | if (parent) { | 332 | parent = genpd->parent; |
338 | if (genpd_sd_counter_dec(parent)) | 333 | if (parent && genpd_sd_counter_dec(parent)) |
339 | genpd_queue_power_off_work(parent); | 334 | genpd_queue_power_off_work(parent); |
340 | |||
341 | genpd_release_lock(parent); | ||
342 | } | ||
343 | 335 | ||
344 | out: | 336 | out: |
345 | genpd->poweroff_task = NULL; | 337 | genpd->poweroff_task = NULL; |