summaryrefslogtreecommitdiff
path: root/drivers/base/power/domain_governor.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2012-05-01 21:34:07 +0200
committerRafael J. Wysocki <rjw@sisk.pl>2012-05-05 21:51:58 +0200
commit6ff7bb0d02f82968be13937c03e93b6c090229df (patch)
treeac2605f2f2b1602fddb88c4ba33c647608b594e6 /drivers/base/power/domain_governor.c
parentefa6902501ffc87d69bfb10b8a09b7d6ee222d77 (diff)
downloadlwn-6ff7bb0d02f82968be13937c03e93b6c090229df.tar.gz
lwn-6ff7bb0d02f82968be13937c03e93b6c090229df.zip
PM / Domains: Cache device stop and domain power off governor results, v3
The results of the default device stop and domain power off governor functions for generic PM domains, default_stop_ok() and default_power_down_ok(), depend only on the timing data of devices, which are static, and on their PM QoS constraints. Thus, in theory, these functions only need to carry out their computations, which may be time consuming in general, when it is known that the PM QoS constraint of at least one of the devices in question has changed. Use the PM QoS notifiers of devices to implement that. First, introduce new fields, constraint_changed and max_off_time_changed, into struct gpd_timing_data and struct generic_pm_domain, respectively, and register a PM QoS notifier function when adding a device into a domain that will set those fields to 'true' whenever the device's PM QoS constraint is modified. Second, make default_stop_ok() and default_power_down_ok() use those fields to decide whether or not to carry out their computations from scratch. The device and PM domain hierarchies are taken into account in that and the expense is that the changes of PM QoS constraints of suspended devices will not be taken into account immediately, which isn't guaranteed anyway in general. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Diffstat (limited to 'drivers/base/power/domain_governor.c')
-rw-r--r--drivers/base/power/domain_governor.c45
1 files changed, 42 insertions, 3 deletions
diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c
index 2aae623fd840..3a5c5346bc47 100644
--- a/drivers/base/power/domain_governor.c
+++ b/drivers/base/power/domain_governor.c
@@ -46,18 +46,34 @@ static int dev_update_qos_constraint(struct device *dev, void *data)
bool default_stop_ok(struct device *dev)
{
struct gpd_timing_data *td = &dev_gpd_data(dev)->td;
+ unsigned long flags;
s64 constraint_ns;
dev_dbg(dev, "%s()\n", __func__);
- constraint_ns = dev_pm_qos_read_value(dev);
+ spin_lock_irqsave(&dev->power.lock, flags);
+
+ if (!td->constraint_changed) {
+ bool ret = td->cached_stop_ok;
+
+ spin_unlock_irqrestore(&dev->power.lock, flags);
+ return ret;
+ }
+ td->constraint_changed = false;
+ td->cached_stop_ok = false;
+ td->effective_constraint_ns = -1;
+ constraint_ns = __dev_pm_qos_read_value(dev);
+
+ spin_unlock_irqrestore(&dev->power.lock, flags);
+
if (constraint_ns < 0)
return false;
constraint_ns *= NSEC_PER_USEC;
/*
* We can walk the children without any additional locking, because
- * they all have been suspended at this point.
+ * they all have been suspended at this point and their
+ * effective_constraint_ns fields won't be modified in parallel with us.
*/
if (!dev->power.ignore_children)
device_for_each_child(dev, &constraint_ns,
@@ -69,11 +85,13 @@ bool default_stop_ok(struct device *dev)
return false;
}
td->effective_constraint_ns = constraint_ns;
+ td->cached_stop_ok = constraint_ns > td->stop_latency_ns ||
+ constraint_ns == 0;
/*
* The children have been suspended already, so we don't need to take
* their stop latencies into account here.
*/
- return constraint_ns > td->stop_latency_ns || constraint_ns == 0;
+ return td->cached_stop_ok;
}
/**
@@ -90,6 +108,25 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)
s64 min_dev_off_time_ns;
s64 off_on_time_ns;
+ if (genpd->max_off_time_changed) {
+ struct gpd_link *link;
+
+ /*
+ * We have to invalidate the cached results for the masters, so
+ * use the observation that default_power_down_ok() is not
+ * going to be called for any master until this instance
+ * returns.
+ */
+ list_for_each_entry(link, &genpd->slave_links, slave_node)
+ link->master->max_off_time_changed = true;
+
+ genpd->max_off_time_changed = false;
+ genpd->cached_power_down_ok = false;
+ genpd->max_off_time_ns = -1;
+ } else {
+ return genpd->cached_power_down_ok;
+ }
+
off_on_time_ns = genpd->power_off_latency_ns +
genpd->power_on_latency_ns;
/*
@@ -165,6 +202,8 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)
min_dev_off_time_ns = constraint_ns;
}
+ genpd->cached_power_down_ok = true;
+
/*
* If the computed minimum device off time is negative, there are no
* latency constraints, so the domain can spend arbitrary time in the