summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2011-08-08 23:43:04 +0200
committerRafael J. Wysocki <rjw@sisk.pl>2011-08-25 15:33:43 +0200
commitc4bb3160c8823d3a1e581d7e05fb8b343097e7c8 (patch)
tree550ba22e33176d4e5116a50cb8789ababe6dd09d
parentff35336d3efd1ec4015b56f690191ed69730cbb0 (diff)
downloadlinux-c4bb3160c8823d3a1e581d7e05fb8b343097e7c8.tar.bz2
PM / Domains: Implement subdomain counters as atomic fields
Currently, pm_genpd_poweron() and pm_genpd_poweroff() need to take the parent PM domain's lock in order to modify the parent's counter of active subdomains in a nonracy way. This causes the locking to be considerably complex and in fact is not necessary, because the subdomain counters may be implemented as atomic fields and they won't have to be modified under a lock. Replace the unsigned in sd_count field in struct generic_pm_domain by an atomic_t one and modify the code in drivers/base/power/domain.c to take this change into account. This patch doesn't change the locking yet, that is going to be done in a separate subsequent patch. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
-rw-r--r--drivers/base/power/domain.c30
-rw-r--r--include/linux/pm_domain.h2
2 files changed, 21 insertions, 11 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 1c374579407c..20e2b52d9c9c 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -29,10 +29,20 @@ static struct generic_pm_domain *dev_to_genpd(struct device *dev)
return pd_to_genpd(dev->pm_domain);
}
-static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
+static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd)
{
- if (!WARN_ON(genpd->sd_count == 0))
- genpd->sd_count--;
+ bool ret = false;
+
+ if (!WARN_ON(atomic_read(&genpd->sd_count) == 0))
+ ret = !!atomic_dec_and_test(&genpd->sd_count);
+
+ return ret;
+}
+
+static void genpd_sd_counter_inc(struct generic_pm_domain *genpd)
+{
+ atomic_inc(&genpd->sd_count);
+ smp_mb__after_atomic_inc();
}
static void genpd_acquire_lock(struct generic_pm_domain *genpd)
@@ -118,7 +128,7 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd)
genpd_set_active(genpd);
if (parent)
- parent->sd_count++;
+ genpd_sd_counter_inc(parent);
out:
mutex_unlock(&genpd->lock);
@@ -254,7 +264,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
|| genpd->resume_count > 0)
return 0;
- if (genpd->sd_count > 0)
+ if (atomic_read(&genpd->sd_count) > 0)
return -EBUSY;
not_suspended = 0;
@@ -325,8 +335,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
genpd->status = GPD_STATE_POWER_OFF;
if (parent) {
- genpd_sd_counter_dec(parent);
- if (parent->sd_count == 0)
+ if (genpd_sd_counter_dec(parent))
genpd_queue_power_off_work(parent);
genpd_release_lock(parent);
@@ -506,7 +515,8 @@ static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
if (genpd->status == GPD_STATE_POWER_OFF)
return;
- if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0)
+ if (genpd->suspended_count != genpd->device_count
+ || atomic_read(&genpd->sd_count) > 0)
return;
if (genpd->power_off)
@@ -1167,7 +1177,7 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
new_subdomain->parent = genpd;
if (subdomain->status != GPD_STATE_POWER_OFF)
- genpd->sd_count++;
+ genpd_sd_counter_inc(genpd);
out:
mutex_unlock(&new_subdomain->lock);
@@ -1242,7 +1252,7 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
genpd->gov = gov;
INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
genpd->in_progress = 0;
- genpd->sd_count = 0;
+ atomic_set(&genpd->sd_count, 0);
genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE;
init_waitqueue_head(&genpd->status_wait_queue);
genpd->poweroff_task = NULL;
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index f9ec1736a116..81c5782d90a3 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -33,7 +33,7 @@ struct generic_pm_domain {
struct dev_power_governor *gov;
struct work_struct power_off_work;
unsigned int in_progress; /* Number of devices being suspended now */
- unsigned int sd_count; /* Number of subdomains with power "on" */
+ atomic_t sd_count; /* Number of subdomains with power "on" */
enum gpd_status status; /* Current state of the domain */
wait_queue_head_t status_wait_queue;
struct task_struct *poweroff_task; /* Powering off task */