diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2011-11-30 18:02:17 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2011-12-01 15:48:07 -0500 |
commit | 0140d8bd47f798d55c3720f7fcade9e50929a5e5 (patch) | |
tree | 8be169e9eab129f60fc0af2977e0335cfe764dee | |
parent | 221e9b58380abdd6c05e11b4538597e2586ee141 (diff) |
PM / Domains: Automatically update overoptimistic latency information
Measure the time of execution of the .stop(), .start(), .save_state()
and .restore_state() PM domain device callbacks and if the result
is greater than the corresponding latency value stored in the
device's struct generic_pm_domain_data object, replace the inaccurate
value with the measured time.
Do analogously for the PM domains' .power_off() and .power_off()
callbacks.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
-rw-r--r-- | drivers/base/power/domain.c | 43 |
1 files changed, 39 insertions, 4 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 91896194e76b..5a8d67d51f0e 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c | |||
@@ -33,6 +33,20 @@ | |||
33 | __ret; \ | 33 | __ret; \ |
34 | }) | 34 | }) |
35 | 35 | ||
36 | #define GENPD_DEV_TIMED_CALLBACK(genpd, type, callback, dev, field, name) \ | ||
37 | ({ \ | ||
38 | ktime_t __start = ktime_get(); \ | ||
39 | type __retval = GENPD_DEV_CALLBACK(genpd, type, callback, dev); \ | ||
40 | s64 __elapsed = ktime_to_ns(ktime_sub(ktime_get(), __start)); \ | ||
41 | struct generic_pm_domain_data *__gpd_data = dev_gpd_data(dev); \ | ||
42 | if (__elapsed > __gpd_data->td.field) { \ | ||
43 | __gpd_data->td.field = __elapsed; \ | ||
44 | dev_warn(dev, name " latency exceeded, new value %lld ns\n", \ | ||
45 | __elapsed); \ | ||
46 | } \ | ||
47 | __retval; \ | ||
48 | }) | ||
49 | |||
36 | static LIST_HEAD(gpd_list); | 50 | static LIST_HEAD(gpd_list); |
37 | static DEFINE_MUTEX(gpd_list_lock); | 51 | static DEFINE_MUTEX(gpd_list_lock); |
38 | 52 | ||
@@ -48,22 +62,27 @@ struct generic_pm_domain *dev_to_genpd(struct device *dev) | |||
48 | 62 | ||
49 | static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev) | 63 | static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev) |
50 | { | 64 | { |
51 | return GENPD_DEV_CALLBACK(genpd, int, stop, dev); | 65 | return GENPD_DEV_TIMED_CALLBACK(genpd, int, stop, dev, |
66 | stop_latency_ns, "stop"); | ||
52 | } | 67 | } |
53 | 68 | ||
54 | static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev) | 69 | static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev) |
55 | { | 70 | { |
56 | return GENPD_DEV_CALLBACK(genpd, int, start, dev); | 71 | return GENPD_DEV_TIMED_CALLBACK(genpd, int, start, dev, |
72 | start_latency_ns, "start"); | ||
57 | } | 73 | } |
58 | 74 | ||
59 | static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev) | 75 | static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev) |
60 | { | 76 | { |
61 | return GENPD_DEV_CALLBACK(genpd, int, save_state, dev); | 77 | return GENPD_DEV_TIMED_CALLBACK(genpd, int, save_state, dev, |
78 | save_state_latency_ns, "state save"); | ||
62 | } | 79 | } |
63 | 80 | ||
64 | static int genpd_restore_dev(struct generic_pm_domain *genpd, struct device *dev) | 81 | static int genpd_restore_dev(struct generic_pm_domain *genpd, struct device *dev) |
65 | { | 82 | { |
66 | return GENPD_DEV_CALLBACK(genpd, int, restore_state, dev); | 83 | return GENPD_DEV_TIMED_CALLBACK(genpd, int, restore_state, dev, |
84 | restore_state_latency_ns, | ||
85 | "state restore"); | ||
67 | } | 86 | } |
68 | 87 | ||
69 | static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd) | 88 | static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd) |
@@ -182,9 +201,16 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd) | |||
182 | } | 201 | } |
183 | 202 | ||
184 | if (genpd->power_on) { | 203 | if (genpd->power_on) { |
204 | ktime_t time_start = ktime_get(); | ||
205 | s64 elapsed_ns; | ||
206 | |||
185 | ret = genpd->power_on(genpd); | 207 | ret = genpd->power_on(genpd); |
186 | if (ret) | 208 | if (ret) |
187 | goto err; | 209 | goto err; |
210 | |||
211 | elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); | ||
212 | if (elapsed_ns > genpd->power_on_latency_ns) | ||
213 | genpd->power_on_latency_ns = elapsed_ns; | ||
188 | } | 214 | } |
189 | 215 | ||
190 | genpd_set_active(genpd); | 216 | genpd_set_active(genpd); |
@@ -377,11 +403,16 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) | |||
377 | } | 403 | } |
378 | 404 | ||
379 | if (genpd->power_off) { | 405 | if (genpd->power_off) { |
406 | ktime_t time_start; | ||
407 | s64 elapsed_ns; | ||
408 | |||
380 | if (atomic_read(&genpd->sd_count) > 0) { | 409 | if (atomic_read(&genpd->sd_count) > 0) { |
381 | ret = -EBUSY; | 410 | ret = -EBUSY; |
382 | goto out; | 411 | goto out; |
383 | } | 412 | } |
384 | 413 | ||
414 | time_start = ktime_get(); | ||
415 | |||
385 | /* | 416 | /* |
386 | * If sd_count > 0 at this point, one of the subdomains hasn't | 417 | * If sd_count > 0 at this point, one of the subdomains hasn't |
387 | * managed to call pm_genpd_poweron() for the master yet after | 418 | * managed to call pm_genpd_poweron() for the master yet after |
@@ -395,6 +426,10 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) | |||
395 | genpd_set_active(genpd); | 426 | genpd_set_active(genpd); |
396 | goto out; | 427 | goto out; |
397 | } | 428 | } |
429 | |||
430 | elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); | ||
431 | if (elapsed_ns > genpd->power_off_latency_ns) | ||
432 | genpd->power_off_latency_ns = elapsed_ns; | ||
398 | } | 433 | } |
399 | 434 | ||
400 | genpd->status = GPD_STATE_POWER_OFF; | 435 | genpd->status = GPD_STATE_POWER_OFF; |