diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2012-04-29 16:54:17 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2012-05-01 15:28:03 -0400 |
commit | a5bef810ad9816a3a8e500d8832be77d52903a12 (patch) | |
tree | dd108feaf2fa45f9520b63ea94ecec2c42259194 | |
parent | 69964ea4c7b68c9399f7977aa5b9aa6539a6a98a (diff) |
PM / Domains: Rework default device stop governor function, v2
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>
-rw-r--r-- | drivers/base/power/domain.c | 1 | ||||
-rw-r--r-- | drivers/base/power/domain_governor.c | 53 | ||||
-rw-r--r-- | include/linux/pm_domain.h | 2 |
3 files changed, 51 insertions, 5 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 73ce9fbe9839..3c6e94fe058a 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c | |||
@@ -506,6 +506,7 @@ static int pm_genpd_runtime_suspend(struct device *dev) | |||
506 | if (dev_gpd_data(dev)->always_on) | 506 | if (dev_gpd_data(dev)->always_on) |
507 | return -EBUSY; | 507 | return -EBUSY; |
508 | 508 | ||
509 | dev_gpd_data(dev)->td.effective_constraint_ns = -1; | ||
509 | stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL; | 510 | stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL; |
510 | if (stop_ok && !stop_ok(dev)) | 511 | if (stop_ok && !stop_ok(dev)) |
511 | return -EBUSY; | 512 | return -EBUSY; |
diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c index 66a265bf5867..a67f157a7a74 100644 --- a/drivers/base/power/domain_governor.c +++ b/drivers/base/power/domain_governor.c | |||
@@ -14,6 +14,31 @@ | |||
14 | 14 | ||
15 | #ifdef CONFIG_PM_RUNTIME | 15 | #ifdef CONFIG_PM_RUNTIME |
16 | 16 | ||
17 | static int dev_update_qos_constraint(struct device *dev, void *data) | ||
18 | { | ||
19 | s64 *constraint_ns_p = data; | ||
20 | s32 constraint_ns = -1; | ||
21 | |||
22 | if (dev->power.subsys_data && dev->power.subsys_data->domain_data) | ||
23 | constraint_ns = dev_gpd_data(dev)->td.effective_constraint_ns; | ||
24 | |||
25 | if (constraint_ns < 0) { | ||
26 | constraint_ns = dev_pm_qos_read_value(dev); | ||
27 | constraint_ns *= NSEC_PER_USEC; | ||
28 | } | ||
29 | if (constraint_ns == 0) | ||
30 | return 0; | ||
31 | |||
32 | /* | ||
33 | * constraint_ns cannot be negative here, because the device has been | ||
34 | * suspended. | ||
35 | */ | ||
36 | if (constraint_ns < *constraint_ns_p || *constraint_ns_p == 0) | ||
37 | *constraint_ns_p = constraint_ns; | ||
38 | |||
39 | return 0; | ||
40 | } | ||
41 | |||
17 | /** | 42 | /** |
18 | * default_stop_ok - Default PM domain governor routine for stopping devices. | 43 | * default_stop_ok - Default PM domain governor routine for stopping devices. |
19 | * @dev: Device to check. | 44 | * @dev: Device to check. |
@@ -21,14 +46,34 @@ | |||
21 | bool default_stop_ok(struct device *dev) | 46 | bool default_stop_ok(struct device *dev) |
22 | { | 47 | { |
23 | struct gpd_timing_data *td = &dev_gpd_data(dev)->td; | 48 | struct gpd_timing_data *td = &dev_gpd_data(dev)->td; |
49 | s64 constraint_ns; | ||
24 | 50 | ||
25 | dev_dbg(dev, "%s()\n", __func__); | 51 | dev_dbg(dev, "%s()\n", __func__); |
26 | 52 | ||
27 | if (dev->power.max_time_suspended_ns < 0 || td->break_even_ns == 0) | 53 | constraint_ns = dev_pm_qos_read_value(dev); |
28 | return true; | 54 | if (constraint_ns < 0) |
55 | return false; | ||
56 | |||
57 | constraint_ns *= NSEC_PER_USEC; | ||
58 | /* | ||
59 | * We can walk the children without any additional locking, because | ||
60 | * they all have been suspended at this point. | ||
61 | */ | ||
62 | if (!dev->power.ignore_children) | ||
63 | device_for_each_child(dev, &constraint_ns, | ||
64 | dev_update_qos_constraint); | ||
29 | 65 | ||
30 | return td->stop_latency_ns + td->start_latency_ns < td->break_even_ns | 66 | if (constraint_ns > 0) { |
31 | && td->break_even_ns < dev->power.max_time_suspended_ns; | 67 | constraint_ns -= td->start_latency_ns; |
68 | if (constraint_ns == 0) | ||
69 | return false; | ||
70 | } | ||
71 | td->effective_constraint_ns = constraint_ns; | ||
72 | /* | ||
73 | * The children have been suspended already, so we don't need to take | ||
74 | * their stop latencies into account here. | ||
75 | */ | ||
76 | return constraint_ns > td->stop_latency_ns || constraint_ns == 0; | ||
32 | } | 77 | } |
33 | 78 | ||
34 | /** | 79 | /** |
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 91f8286106ea..9c25219458c2 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h | |||
@@ -93,7 +93,7 @@ struct gpd_timing_data { | |||
93 | s64 start_latency_ns; | 93 | s64 start_latency_ns; |
94 | s64 save_state_latency_ns; | 94 | s64 save_state_latency_ns; |
95 | s64 restore_state_latency_ns; | 95 | s64 restore_state_latency_ns; |
96 | s64 break_even_ns; | 96 | s64 effective_constraint_ns; |
97 | }; | 97 | }; |
98 | 98 | ||
99 | struct generic_pm_domain_data { | 99 | struct generic_pm_domain_data { |