diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2012-04-29 16:53:32 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2012-05-01 15:25:49 -0400 |
commit | 55850945e872531644f31fefd217d61dd15dcab8 (patch) | |
tree | 412ee1338d0b69e4d6e2a5e518e30e6660704921 | |
parent | 7483b4a4d9abf9dcf1ffe6e805ead2847ec3264e (diff) |
PM / Sleep: Add "prevent autosleep time" statistics to wakeup sources
Android uses one wakelock statistics that is only necessary for
opportunistic sleep. Namely, the prevent_suspend_time field
accumulates the total time the given wakelock has been locked
while "automatic suspend" was enabled. Add an analogous field,
prevent_sleep_time, to wakeup sources and make it behave in a similar
way.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | Documentation/ABI/testing/sysfs-devices-power | 11 | ||||
-rw-r--r-- | drivers/base/power/sysfs.c | 24 | ||||
-rw-r--r-- | drivers/base/power/wakeup.c | 61 | ||||
-rw-r--r-- | include/linux/pm_wakeup.h | 4 | ||||
-rw-r--r-- | include/linux/suspend.h | 1 | ||||
-rw-r--r-- | kernel/power/autosleep.c | 6 |
6 files changed, 102 insertions, 5 deletions
diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power index b0a5d9a6135e..45000f0db4d4 100644 --- a/Documentation/ABI/testing/sysfs-devices-power +++ b/Documentation/ABI/testing/sysfs-devices-power | |||
@@ -158,6 +158,17 @@ Description: | |||
158 | not enabled to wake up the system from sleep states, this | 158 | not enabled to wake up the system from sleep states, this |
159 | attribute is not present. | 159 | attribute is not present. |
160 | 160 | ||
161 | What: /sys/devices/.../power/wakeup_prevent_sleep_time_ms | ||
162 | Date: February 2012 | ||
163 | Contact: Rafael J. Wysocki <rjw@sisk.pl> | ||
164 | Description: | ||
165 | The /sys/devices/.../wakeup_prevent_sleep_time_ms attribute | ||
166 | contains the total time the device has been preventing | ||
167 | opportunistic transitions to sleep states from occuring. | ||
168 | This attribute is read-only. If the device is not enabled to | ||
169 | wake up the system from sleep states, this attribute is not | ||
170 | present. | ||
171 | |||
161 | What: /sys/devices/.../power/autosuspend_delay_ms | 172 | What: /sys/devices/.../power/autosuspend_delay_ms |
162 | Date: September 2010 | 173 | Date: September 2010 |
163 | Contact: Alan Stern <stern@rowland.harvard.edu> | 174 | Contact: Alan Stern <stern@rowland.harvard.edu> |
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 13e40b9021b9..48be2ad4dd2c 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c | |||
@@ -417,6 +417,27 @@ static ssize_t wakeup_last_time_show(struct device *dev, | |||
417 | } | 417 | } |
418 | 418 | ||
419 | static DEVICE_ATTR(wakeup_last_time_ms, 0444, wakeup_last_time_show, NULL); | 419 | static DEVICE_ATTR(wakeup_last_time_ms, 0444, wakeup_last_time_show, NULL); |
420 | |||
421 | #ifdef CONFIG_PM_AUTOSLEEP | ||
422 | static ssize_t wakeup_prevent_sleep_time_show(struct device *dev, | ||
423 | struct device_attribute *attr, | ||
424 | char *buf) | ||
425 | { | ||
426 | s64 msec = 0; | ||
427 | bool enabled = false; | ||
428 | |||
429 | spin_lock_irq(&dev->power.lock); | ||
430 | if (dev->power.wakeup) { | ||
431 | msec = ktime_to_ms(dev->power.wakeup->prevent_sleep_time); | ||
432 | enabled = true; | ||
433 | } | ||
434 | spin_unlock_irq(&dev->power.lock); | ||
435 | return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n"); | ||
436 | } | ||
437 | |||
438 | static DEVICE_ATTR(wakeup_prevent_sleep_time_ms, 0444, | ||
439 | wakeup_prevent_sleep_time_show, NULL); | ||
440 | #endif /* CONFIG_PM_AUTOSLEEP */ | ||
420 | #endif /* CONFIG_PM_SLEEP */ | 441 | #endif /* CONFIG_PM_SLEEP */ |
421 | 442 | ||
422 | #ifdef CONFIG_PM_ADVANCED_DEBUG | 443 | #ifdef CONFIG_PM_ADVANCED_DEBUG |
@@ -511,6 +532,9 @@ static struct attribute *wakeup_attrs[] = { | |||
511 | &dev_attr_wakeup_total_time_ms.attr, | 532 | &dev_attr_wakeup_total_time_ms.attr, |
512 | &dev_attr_wakeup_max_time_ms.attr, | 533 | &dev_attr_wakeup_max_time_ms.attr, |
513 | &dev_attr_wakeup_last_time_ms.attr, | 534 | &dev_attr_wakeup_last_time_ms.attr, |
535 | #ifdef CONFIG_PM_AUTOSLEEP | ||
536 | &dev_attr_wakeup_prevent_sleep_time_ms.attr, | ||
537 | #endif | ||
514 | #endif | 538 | #endif |
515 | NULL, | 539 | NULL, |
516 | }; | 540 | }; |
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index cf1706df7610..2595b8d8fe1f 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c | |||
@@ -380,6 +380,8 @@ static void wakeup_source_activate(struct wakeup_source *ws) | |||
380 | ws->active = true; | 380 | ws->active = true; |
381 | ws->active_count++; | 381 | ws->active_count++; |
382 | ws->last_time = ktime_get(); | 382 | ws->last_time = ktime_get(); |
383 | if (ws->autosleep_enabled) | ||
384 | ws->start_prevent_time = ws->last_time; | ||
383 | 385 | ||
384 | /* Increment the counter of events in progress. */ | 386 | /* Increment the counter of events in progress. */ |
385 | cec = atomic_inc_return(&combined_event_count); | 387 | cec = atomic_inc_return(&combined_event_count); |
@@ -449,6 +451,17 @@ void pm_stay_awake(struct device *dev) | |||
449 | } | 451 | } |
450 | EXPORT_SYMBOL_GPL(pm_stay_awake); | 452 | EXPORT_SYMBOL_GPL(pm_stay_awake); |
451 | 453 | ||
454 | #ifdef CONFIG_PM_AUTOSLEEP | ||
455 | static void update_prevent_sleep_time(struct wakeup_source *ws, ktime_t now) | ||
456 | { | ||
457 | ktime_t delta = ktime_sub(now, ws->start_prevent_time); | ||
458 | ws->prevent_sleep_time = ktime_add(ws->prevent_sleep_time, delta); | ||
459 | } | ||
460 | #else | ||
461 | static inline void update_prevent_sleep_time(struct wakeup_source *ws, | ||
462 | ktime_t now) {} | ||
463 | #endif | ||
464 | |||
452 | /** | 465 | /** |
453 | * wakup_source_deactivate - Mark given wakeup source as inactive. | 466 | * wakup_source_deactivate - Mark given wakeup source as inactive. |
454 | * @ws: Wakeup source to handle. | 467 | * @ws: Wakeup source to handle. |
@@ -490,6 +503,9 @@ static void wakeup_source_deactivate(struct wakeup_source *ws) | |||
490 | del_timer(&ws->timer); | 503 | del_timer(&ws->timer); |
491 | ws->timer_expires = 0; | 504 | ws->timer_expires = 0; |
492 | 505 | ||
506 | if (ws->autosleep_enabled) | ||
507 | update_prevent_sleep_time(ws, now); | ||
508 | |||
493 | /* | 509 | /* |
494 | * Increment the counter of registered wakeup events and decrement the | 510 | * Increment the counter of registered wakeup events and decrement the |
495 | * couter of wakeup events in progress simultaneously. | 511 | * couter of wakeup events in progress simultaneously. |
@@ -718,6 +734,34 @@ bool pm_save_wakeup_count(unsigned int count) | |||
718 | return events_check_enabled; | 734 | return events_check_enabled; |
719 | } | 735 | } |
720 | 736 | ||
737 | #ifdef CONFIG_PM_AUTOSLEEP | ||
738 | /** | ||
739 | * pm_wakep_autosleep_enabled - Modify autosleep_enabled for all wakeup sources. | ||
740 | * @enabled: Whether to set or to clear the autosleep_enabled flags. | ||
741 | */ | ||
742 | void pm_wakep_autosleep_enabled(bool set) | ||
743 | { | ||
744 | struct wakeup_source *ws; | ||
745 | ktime_t now = ktime_get(); | ||
746 | |||
747 | rcu_read_lock(); | ||
748 | list_for_each_entry_rcu(ws, &wakeup_sources, entry) { | ||
749 | spin_lock_irq(&ws->lock); | ||
750 | if (ws->autosleep_enabled != set) { | ||
751 | ws->autosleep_enabled = set; | ||
752 | if (ws->active) { | ||
753 | if (set) | ||
754 | ws->start_prevent_time = now; | ||
755 | else | ||
756 | update_prevent_sleep_time(ws, now); | ||
757 | } | ||
758 | } | ||
759 | spin_unlock_irq(&ws->lock); | ||
760 | } | ||
761 | rcu_read_unlock(); | ||
762 | } | ||
763 | #endif /* CONFIG_PM_AUTOSLEEP */ | ||
764 | |||
721 | static struct dentry *wakeup_sources_stats_dentry; | 765 | static struct dentry *wakeup_sources_stats_dentry; |
722 | 766 | ||
723 | /** | 767 | /** |
@@ -733,28 +777,37 @@ static int print_wakeup_source_stats(struct seq_file *m, | |||
733 | ktime_t max_time; | 777 | ktime_t max_time; |
734 | unsigned long active_count; | 778 | unsigned long active_count; |
735 | ktime_t active_time; | 779 | ktime_t active_time; |
780 | ktime_t prevent_sleep_time; | ||
736 | int ret; | 781 | int ret; |
737 | 782 | ||
738 | spin_lock_irqsave(&ws->lock, flags); | 783 | spin_lock_irqsave(&ws->lock, flags); |
739 | 784 | ||
740 | total_time = ws->total_time; | 785 | total_time = ws->total_time; |
741 | max_time = ws->max_time; | 786 | max_time = ws->max_time; |
787 | prevent_sleep_time = ws->prevent_sleep_time; | ||
742 | active_count = ws->active_count; | 788 | active_count = ws->active_count; |
743 | if (ws->active) { | 789 | if (ws->active) { |
744 | active_time = ktime_sub(ktime_get(), ws->last_time); | 790 | ktime_t now = ktime_get(); |
791 | |||
792 | active_time = ktime_sub(now, ws->last_time); | ||
745 | total_time = ktime_add(total_time, active_time); | 793 | total_time = ktime_add(total_time, active_time); |
746 | if (active_time.tv64 > max_time.tv64) | 794 | if (active_time.tv64 > max_time.tv64) |
747 | max_time = active_time; | 795 | max_time = active_time; |
796 | |||
797 | if (ws->autosleep_enabled) | ||
798 | prevent_sleep_time = ktime_add(prevent_sleep_time, | ||
799 | ktime_sub(now, ws->start_prevent_time)); | ||
748 | } else { | 800 | } else { |
749 | active_time = ktime_set(0, 0); | 801 | active_time = ktime_set(0, 0); |
750 | } | 802 | } |
751 | 803 | ||
752 | ret = seq_printf(m, "%-12s\t%lu\t\t%lu\t\t%lu\t\t%lu\t\t" | 804 | ret = seq_printf(m, "%-12s\t%lu\t\t%lu\t\t%lu\t\t%lu\t\t" |
753 | "%lld\t\t%lld\t\t%lld\t\t%lld\n", | 805 | "%lld\t\t%lld\t\t%lld\t\t%lld\t\t%lld\n", |
754 | ws->name, active_count, ws->event_count, | 806 | ws->name, active_count, ws->event_count, |
755 | ws->wakeup_count, ws->expire_count, | 807 | ws->wakeup_count, ws->expire_count, |
756 | ktime_to_ms(active_time), ktime_to_ms(total_time), | 808 | ktime_to_ms(active_time), ktime_to_ms(total_time), |
757 | ktime_to_ms(max_time), ktime_to_ms(ws->last_time)); | 809 | ktime_to_ms(max_time), ktime_to_ms(ws->last_time), |
810 | ktime_to_ms(prevent_sleep_time)); | ||
758 | 811 | ||
759 | spin_unlock_irqrestore(&ws->lock, flags); | 812 | spin_unlock_irqrestore(&ws->lock, flags); |
760 | 813 | ||
@@ -771,7 +824,7 @@ static int wakeup_sources_stats_show(struct seq_file *m, void *unused) | |||
771 | 824 | ||
772 | seq_puts(m, "name\t\tactive_count\tevent_count\twakeup_count\t" | 825 | seq_puts(m, "name\t\tactive_count\tevent_count\twakeup_count\t" |
773 | "expire_count\tactive_since\ttotal_time\tmax_time\t" | 826 | "expire_count\tactive_since\ttotal_time\tmax_time\t" |
774 | "last_change\n"); | 827 | "last_change\tprevent_suspend_time\n"); |
775 | 828 | ||
776 | rcu_read_lock(); | 829 | rcu_read_lock(); |
777 | list_for_each_entry_rcu(ws, &wakeup_sources, entry) | 830 | list_for_each_entry_rcu(ws, &wakeup_sources, entry) |
diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h index 5285317a612a..569781faa504 100644 --- a/include/linux/pm_wakeup.h +++ b/include/linux/pm_wakeup.h | |||
@@ -34,6 +34,7 @@ | |||
34 | * @total_time: Total time this wakeup source has been active. | 34 | * @total_time: Total time this wakeup source has been active. |
35 | * @max_time: Maximum time this wakeup source has been continuously active. | 35 | * @max_time: Maximum time this wakeup source has been continuously active. |
36 | * @last_time: Monotonic clock when the wakeup source's was touched last time. | 36 | * @last_time: Monotonic clock when the wakeup source's was touched last time. |
37 | * @prevent_sleep_time: Total time this source has been preventing autosleep. | ||
37 | * @event_count: Number of signaled wakeup events. | 38 | * @event_count: Number of signaled wakeup events. |
38 | * @active_count: Number of times the wakeup sorce was activated. | 39 | * @active_count: Number of times the wakeup sorce was activated. |
39 | * @relax_count: Number of times the wakeup sorce was deactivated. | 40 | * @relax_count: Number of times the wakeup sorce was deactivated. |
@@ -51,12 +52,15 @@ struct wakeup_source { | |||
51 | ktime_t total_time; | 52 | ktime_t total_time; |
52 | ktime_t max_time; | 53 | ktime_t max_time; |
53 | ktime_t last_time; | 54 | ktime_t last_time; |
55 | ktime_t start_prevent_time; | ||
56 | ktime_t prevent_sleep_time; | ||
54 | unsigned long event_count; | 57 | unsigned long event_count; |
55 | unsigned long active_count; | 58 | unsigned long active_count; |
56 | unsigned long relax_count; | 59 | unsigned long relax_count; |
57 | unsigned long expire_count; | 60 | unsigned long expire_count; |
58 | unsigned long wakeup_count; | 61 | unsigned long wakeup_count; |
59 | bool active:1; | 62 | bool active:1; |
63 | bool autosleep_enabled:1; | ||
60 | }; | 64 | }; |
61 | 65 | ||
62 | #ifdef CONFIG_PM_SLEEP | 66 | #ifdef CONFIG_PM_SLEEP |
diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 76b7ec7d3a81..cd83059fb592 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h | |||
@@ -358,6 +358,7 @@ extern bool events_check_enabled; | |||
358 | extern bool pm_wakeup_pending(void); | 358 | extern bool pm_wakeup_pending(void); |
359 | extern bool pm_get_wakeup_count(unsigned int *count, bool block); | 359 | extern bool pm_get_wakeup_count(unsigned int *count, bool block); |
360 | extern bool pm_save_wakeup_count(unsigned int count); | 360 | extern bool pm_save_wakeup_count(unsigned int count); |
361 | extern void pm_wakep_autosleep_enabled(bool set); | ||
361 | 362 | ||
362 | static inline void lock_system_sleep(void) | 363 | static inline void lock_system_sleep(void) |
363 | { | 364 | { |
diff --git a/kernel/power/autosleep.c b/kernel/power/autosleep.c index 42348e3589d3..ca304046d9e2 100644 --- a/kernel/power/autosleep.c +++ b/kernel/power/autosleep.c | |||
@@ -101,8 +101,12 @@ int pm_autosleep_set_state(suspend_state_t state) | |||
101 | 101 | ||
102 | __pm_relax(autosleep_ws); | 102 | __pm_relax(autosleep_ws); |
103 | 103 | ||
104 | if (state > PM_SUSPEND_ON) | 104 | if (state > PM_SUSPEND_ON) { |
105 | pm_wakep_autosleep_enabled(true); | ||
105 | queue_up_suspend_work(); | 106 | queue_up_suspend_work(); |
107 | } else { | ||
108 | pm_wakep_autosleep_enabled(false); | ||
109 | } | ||
106 | 110 | ||
107 | mutex_unlock(&autosleep_lock); | 111 | mutex_unlock(&autosleep_lock); |
108 | return 0; | 112 | return 0; |