PM / Domains: Rework default device stop governor function, v2
authorRafael J. Wysocki <rjw@sisk.pl>
Sun, 29 Apr 2012 20:54:17 +0000 (22:54 +0200)
committerRafael J. Wysocki <rjw@sisk.pl>
Tue, 1 May 2012 19:28:03 +0000 (21:28 +0200)
The existing default device stop governor function for PM domains,
default_stop_ok(), is supposed to check whether or not the device's
PM QoS latency constraint will be violated if the device is stopped
by pm_genpd_runtime_suspend().  However, the computations carried out
by it don't reflect the definition of the PM QoS latency constrait in
Documentation/ABI/testing/sysfs-devices-power.

Make default_stop_ok() follow the definition of the PM QoS latency
constrait.  In particular, make it take the device's start and stop
latencies correctly.

Add a new field, effective_constraint_ns, to struct gpd_timing_data
and use it to store the difference between the device's PM QoS
constraint and its resume latency for use by the device's parent
(the effective_constraint_ns values for the children are used for
computing the parent's one along with its PM QoS constraint).

Remove the break_even_ns field from struct gpd_timing_data, because
it's not used any more.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
drivers/base/power/domain.c
drivers/base/power/domain_governor.c
include/linux/pm_domain.h

index 73ce9fbe983961598d53f3bbeef0057cda084d4b..3c6e94fe058a292f315cd5fe9db519aa0924d98f 100644 (file)
@@ -506,6 +506,7 @@ static int pm_genpd_runtime_suspend(struct device *dev)
        if (dev_gpd_data(dev)->always_on)
                return -EBUSY;
 
+       dev_gpd_data(dev)->td.effective_constraint_ns = -1;
        stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
        if (stop_ok && !stop_ok(dev))
                return -EBUSY;
index 66a265bf5867d1b516a03f34e8e50946db4c1e3e..a67f157a7a7458115d6a293cac0e12de3380473d 100644 (file)
 
 #ifdef CONFIG_PM_RUNTIME
 
+static int dev_update_qos_constraint(struct device *dev, void *data)
+{
+       s64 *constraint_ns_p = data;
+       s32 constraint_ns = -1;
+
+       if (dev->power.subsys_data && dev->power.subsys_data->domain_data)
+               constraint_ns = dev_gpd_data(dev)->td.effective_constraint_ns;
+
+       if (constraint_ns < 0) {
+               constraint_ns = dev_pm_qos_read_value(dev);
+               constraint_ns *= NSEC_PER_USEC;
+       }
+       if (constraint_ns == 0)
+               return 0;
+
+       /*
+        * constraint_ns cannot be negative here, because the device has been
+        * suspended.
+        */
+       if (constraint_ns < *constraint_ns_p || *constraint_ns_p == 0)
+               *constraint_ns_p = constraint_ns;
+
+       return 0;
+}
+
 /**
  * default_stop_ok - Default PM domain governor routine for stopping devices.
  * @dev: Device to check.
 bool default_stop_ok(struct device *dev)
 {
        struct gpd_timing_data *td = &dev_gpd_data(dev)->td;
+       s64 constraint_ns;
 
        dev_dbg(dev, "%s()\n", __func__);
 
-       if (dev->power.max_time_suspended_ns < 0 || td->break_even_ns == 0)
-               return true;
+       constraint_ns = dev_pm_qos_read_value(dev);
+       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.
+        */
+       if (!dev->power.ignore_children)
+               device_for_each_child(dev, &constraint_ns,
+                                     dev_update_qos_constraint);
 
-       return td->stop_latency_ns + td->start_latency_ns < td->break_even_ns
-               && td->break_even_ns < dev->power.max_time_suspended_ns;
+       if (constraint_ns > 0) {
+               constraint_ns -= td->start_latency_ns;
+               if (constraint_ns == 0)
+                       return false;
+       }
+       td->effective_constraint_ns = constraint_ns;
+       /*
+        * 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;
 }
 
 /**
index 91f8286106eaed1e9b8e9acdeab5a8efe9a6f452..9c25219458c2eab69729efe8954e656c33eeacc5 100644 (file)
@@ -93,7 +93,7 @@ struct gpd_timing_data {
        s64 start_latency_ns;
        s64 save_state_latency_ns;
        s64 restore_state_latency_ns;
-       s64 break_even_ns;
+       s64 effective_constraint_ns;
 };
 
 struct generic_pm_domain_data {